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 をインポートしようとすると、Notifier が返されます。シェル内:

$ 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'>

実際、これは (少なくとも私はそう思います) 実際、1 週間前にいくつかのDjango モデルで発生したのと同じ問題です。最初は Django の動作に関係していると思っていましたが、今ではメタクラスと継承に関する Python の「問題」に似ているのではないかと考えています。
これは既知の問題ですか、それとも私が間違ったことをしているだけですか? あなたが私を助けてくれることを願っています。
編集: Notifier にメタクラスを指定しないと期待どおりに動作するため、この「エラー」をメタクラスに帰することを忘れていました。

4

1 に答える 1

2

わかりました、私はそれを理解したと思います。正しいクラスがインポートされています。名前が間違っているだけです。クラスに属性を設定すると、これを確認できるはずです。someJunk = "Notifier"Notifier 定義とCommentNotifier 定義を入れた場合someJunk = "CommentNotifier"、CommentNotifier をインポートすると正しい値になります。

問題は、 を作成する際にattrs、 を含むすべての二重下線属性を除外することです__module__。スーパークラス を呼び出すときは、エントリを持たない を__new__渡します。そのため、Python がエントリを作成します。しかし、このコードはメタクラスを含むファイル内で実行されるため、モジュールは実際のクラスのファイルではなくメタクラスのファイルに誤って設定されます。attrs__module__

クラスの実際の名前について観察した動作は見られません。モジュールについてのみです。つまり、私にとって、インポートされたクラスの名前metafile.CommentNotifierは で、metafileはメタクラスを含むファイルです。ここsubmeta.CommentNotifierで、submetaは CommentNotifierClass を含むファイルです。なぜあなたがそれを見ているのかはわかり__name__ませんが、モジュール/名前の割り当ての微妙な処理がPythonのバージョンごとに異なっていても驚かないでしょう.

__notify__Python のマジック メソッドではあり__notification__ません。独自の目的で何かを示すために二重下線を使用しているため、二重下線メソッドを除外しているようです。これを行うべきではありません。_Notifier必要に応じて、独自のメソッド (など) に他のプレフィックスを使用し、それらのメソッドを除外して、二重アンダースコアのメソッドをそのままにしておきます。二重下線メソッドを除外すると、他の問題が発生する可能性があります。__str__特に、このメタクラスを使用するクラスで実際のマジック メソッド (例: ) を定義しようとすると、失敗の原因になります。

(明確にするために:プライベート属性として、必要に応じて二重アンダースコアで始まるメソッドを使用できますが、これはおそらく良い考えではありません。ただし、これを行う場合は、特別な処理のみを行うようにする必要がありますそれらの属性であり、Python 内部の魔法のメソッドである 2 つのアンダースコアで始まり__notify__ 2 つのアンダースコアで終わる属性ではありません. すべきでないことは、2 つのアンダースコアで始まり、2 つのアンダースコアで終わる独自の名前を作成することです.)

于 2012-08-25T20:56:08.753 に答える