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

事实上,这(至少我是这么认为的)实际上是我一周前遇到的一些Django 模型的同样问题。起初我认为它与 Django 的工作方式有关,但现在我怀疑它更像是一个 Python 的“问题”,涉及元类和继承。
这是一个已知问题还是我只是做错了什么?希望您能够帮助我。
编辑:我忘了提到我将此“错误”归因于元类,因为如果我不给通知程序一个元类,它会按预期工作。

4

1 回答 1

2

好吧,我想我明白了。正在导入正确的类。它只是有错误的名字。如果您在类上设置属性,您应该能够看到这一点。如果您someJunk = "Notifier"输入了 Notifier 定义和someJunk = "CommentNotifier"CommentNotifier 定义,那么当您导入 CommentNotifier 时,它将具有正确的值。

问题是,在创建您的attrs时,您排除了所有双下划线属性,包括__module__. 当你调用超类__new__时,你传入你的attrs,它没有__module__条目,所以 Python 为你创建了一个。但是由于这段代码是在包含元类的文件中执行的,所以模块被错误地设置为元类的文件而不是实际类的文件。

我没有看到您对类的实际名称观察到的行为,仅针对模块。也就是说,对我来说,导入的类名为metafile.CommentNotifiermetafile包含元类的文件在哪里。它应该命名为submeta.CommentNotifiersubmeta包含 CommentNotifierClass 的文件在哪里。我不确定你为什么也会看到它__name__,但如果模块/名称分配的一些微妙处理在不同的 Python 版本中有所不同,我也不会感到惊讶。

__notify__并且__notification__不是 Python 魔术方法。看来您排除了双下划线方法,因为您使用双下划线来表示您自己的目的。你不应该这样做。如果必须,请为您自己的方法(如或其他)使用其他前缀_Notifier,然后排除这些方法并保留双下划线的方法。排除双下划线方法可能会导致其他问题。特别是,如果您决定在__str__使用此元类的类上定义一个真正的魔术方法(例如,),它将导致失败。

(澄清一下:如果需要,您可以使用以双下划线开头的方法作为私有属性,尽管这可能仍然不是一个好主意。但是,如果您这样做,您需要确保只对这些属性,而不是那些以双下划线开头结尾的属性,它们是 Python 内部的魔术方法。你不应该做的是创建自己的以双下划线开头和结尾的名称,例如__notify__.)

于 2012-08-25T20:56:08.753 回答