元类的奇怪继承

2024-04-28 10:48:52 发布

您现在位置:Python中文网/ 问答频道 /正文

当我试图从一个带有元类的类继承时,我在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

然后,在通知程序.py公司名称:

^{pr2}$

但是,当我尝试导入CommentNotifier时,它返回Notifier。在外壳中:

^{3}$

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


Tags: instancenamenewforreturnisvaluedef
1条回答
网友
1楼 · 发布于 2024-04-28 10:48:52

好吧,我想我知道了。正在导入正确的类。只是名字不对。如果在类上设置属性,应该可以看到这一点。如果您将someJunk = "Notifier"放在通知程序定义中,someJunk = "CommentNotifier"放在CommentNotifier定义中,那么当您导入CommentNotifier时,它将具有正确的值。在

问题是在创建attrs时,排除了所有双下划线属性,包括__module__。当您调用超类__new__时,您将传入您的attrs,它没有__module__项,因此Python会为您创建一个。因为这个文件并没有正确地在元类中执行,而不是在元类中执行。在

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

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

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

相关问题 更多 >