2012-08-25 59 views
0

當我嘗試從一個帶有元類的類繼承時,我遇到了Python中一些非常奇怪的問題。我有這樣的:帶元類的奇怪繼承

class NotifierMetaclass(type): 

    def __new__(cls, name, bases, dct): 

     attrs = ((name, value) for name, value in dct.items() 
        if not name.startswith('__')) 

     def wrap_method(meth): 
      return instance_wrapper()(meth) # instance_wrapper is a decorator of my own 

     def is_callable(value): 
      return hasattr(value, '__call__') 

     decorated_meth = dict(
      (name, value) if not is_callable(value) 
      else (name, wrap_method(value)) 
      for name, value in attrs 
     ) 

     return super(NotifierMetaclass, cls).__new__(
      cls, name, bases, decorated_meth 
     ) 


class Notifier(object): 

    def __init__(self, instance): 
     self._i = instance 

    __metaclass__ = NotifierMetaclass 

,然後在notifiers.py:

from helpers import Notifier 

class CommentNotifier(Notifier): 

    def __notification__(self, notification): 
     return '%s has commented on your board' % self.sender 

    def __notify__(self): 
     receivers = self.retrieve_users() 
     notif_type = self.__notificationtype__() 
     for user in receivers: 
      Notification.objects.create(
       object_id=self.id, 
       receiver=user, 
       sender_id=self.sender_id, 
       type=notif_type 
      ) 

然而,當我嘗試導入CommentNotifier返回通知。在外殼:

$ python 
Python 2.7.3 (default, Apr 20 2012, 22:44:07) 
[GCC 4.6.3] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
(InteractiveConsole) 
>>> from logic.notifiers import CommentNotifier 
>>> CommentNotifier 
<class 'helpers.CommentNotifier'> 

其實,這是(至少這是我的想法)實際上是相同的問題我一個星期前的一些Django models。起初我認爲它與Django的工作方式有關,但現在我懷疑它更像是一個帶元類和繼承的Python「問題」。
這是一個已知的問題,或者我只是做錯了什麼?希望您能夠幫助我。
編輯:我忘了提及,我將這個「錯誤」歸因於元類,因爲如果我沒有給通告的元類,它按預期工作。

+1

您的示例適用於我。你是不是在「東西」部分忽略了一些重要的東西?請提供一個實際展現你的意思的例子。 – BrenBarn

+0

剛剛編輯了真實案例的代碼 – cronos2

+0

我得到了''(只使用了兩個文件,'helpers.py'和'notifiers.py')和''如果我把它放在一個包裏。儘量減少它,並在各個地方粘貼一些打印語句,以排除您沒有導入您認爲自己的版本的可能性。 – DSM

回答

2

好吧,我想我想通了。正在導入正確的課程。它只是名稱錯了。你應該能夠看到這個,如果你設置了類的屬性。如果您在通知程序定義中放置了someJunk = "Notifier",並且在CommentNotifier定義中放置了someJunk = "CommentNotifier",則當您導入CommentNotifier時,它將具有正確的值。

問題是,在創建attrs時,排除了所有雙下劃線屬性,包括__module__。當你打電話給超類__new__時,你傳入你的attrs,它沒有__module__條目,所以Python爲你創建一個。但由於此代碼在包含元類的文件內執行,因此該模塊被錯誤地設置爲元類的文件而不是實際的類文件。

我沒有看到你觀察到的類的實際名稱的行爲,只爲模塊。也就是說,對於我來說,導入的類名爲metafile.CommentNotifier,其中metafile是包含元類的文件。它應該被命名爲submeta.CommentNotifier,其中submeta是包含CommentNotifierClass的文件。我不確定爲什麼你看到它也是__name__,但如果在不同的Python版本中對模塊/名稱分配的某些細微處理有所不同,這並不會讓我感到驚訝。

__notify____notification__不是Python魔術方法。您似乎排除了雙下劃線方法,因爲您使用雙下劃線來表示出於您自己的目的。你不應該這樣做。如果必須的話,爲自己的方法使用一些其他前綴(如_Notifier或其他),然後排除這些方法並保留雙下劃線。排除雙下劃線方法可能會導致其他問題。特別是,如果你決定在使用這個元類的類上定義一個真正的魔術方法(例如,__str__),它將導致失敗。 (說明:如果你想使用私人屬性,你可以使用開始的雙下劃線,儘管這仍然不是一個好主意,但是如果你這樣做,你需要確保你只對這些屬性進行特殊處理,而不是以結尾的雙下劃線,這是Python內部的魔法方法。你不應該做的是創建自己的名字,它以雙下劃線開頭和結尾,就像__notify__。)

+0

+1,但我發現類名稱的差異真令人費解。我也使用2.7.3。 – DSM

+0

不是+1而是+1000,你真的給了我一個Python課程。首先,我想爲所有的困惑「助手。通話人」產生了道歉。它實際上是「helpers.CommentNotifier」口譯員說的,但我不知不覺地修改了文本。對於那個很抱歉。 其次,你的猜測是完全正確的,如果我省略了「__」的檢查,它實際上正確設置了__module__屬性並保留了__foo__方法。 最後但並非最不重要的是,這是方法命名的事實。我從來沒有完全滿意'__foo__'命名,但它的工作。我應該重新考慮它。 – cronos2