元类的奇怪继承 [英] Weird inheritance with metaclasses

查看:94
本文介绍了元类的奇怪继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试从具有元类的类继承时,我在Python中遇到了一些非常奇怪的问题.我有这个:

I'm experiencing some really weird problems in Python when trying to inherit from a class with a metaclass. I have this:

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中:

And then, in 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.在外壳中:

However, when I try to import CommentNotifier it returns Notifier. In the shell:

$ 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问题".
这是一个已知问题,还是我做错了什么?希望你能帮助我.
我忘了提到我将此错误"归因于元类,因为如果我不将元类提供给Notifier,它将按预期工作.

In fact, this is (at least that's what I think) actually the same problem I got a week ago with some Django models. At first I thought it was related to the way Django works, but now I suspect it's more like a Python "problem" with metaclasses and inheritance.
Is this a known issue or am I simply doing things wrong? Hope you can help me.
I forgot to mention that I attribute this "error" to metaclasses because if I don't give a metaclass to Notifier it works as expected.

推荐答案

好的,我想我已经知道了.正在导入正确的类.它只是名字错误.如果您在类上设置属性,则应该能够看到此信息.如果将someJunk = "Notifier"放在Notifier定义中,并将someJunk = "CommentNotifier"放在CommentNotifier定义中,则在导入CommentNotifier时它将具有正确的值.

Okay, I think I figured it out. The correct class is being imported. It just has the wrong name. You should be able to see this if you set attributes on the classes. If you put someJunk = "Notifier" in the Notifier definition and someJunk = "CommentNotifier" in the CommentNotifier definition, then when you import CommentNotifier it will have the right value.

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

The problem is that in creating your attrs, you exclude all double-underscore attributes, including __module__. When you call the superclass __new__, you pass in your attrs, which does not have a __module__ entry, so Python creates one for you. But since this code is executing inside the file containing the metaclass, the module is incorrectly set to the metaclass's file and not the actual class's file.

我没有看到您对类的实际名称所观察到的行为,仅对模块而言.也就是说,对我而言,导入的类名为metafile.CommentNotifier,其中metafile是包含元类的文件.它应命名为submeta.CommentNotifier,其中submeta是包含CommentNotifierClass的文件.我不确定为什么您还会在__name__中看到它,但是如果模块/名称分配的某些微妙处理在不同的Python版本中有所不同,这也不会令我感到惊讶.

I'm not seeing the behavior you observe for the actual name of the class, only for the module. That is, for me the imported class is named metafile.CommentNotifier, where metafile is the file containing the metaclass. It should be named submeta.CommentNotifier, where submeta is the file containing the CommentNotifierClass. I'm not sure why you're seeing it for __name__ as well, but it wouldn't surprise me if some subtle handling of module/name assignment varies across different Python versions.

__notify____notification__不是Python魔术方法.看来您正在排除双下划线方法,因为您使用双下划线来指示某些内容以达到自己的目的.你不应该这样做.如果需要的话,可以为自己的方法使用其他前缀(例如_Notifier或其他内容),然后排除这些方法并单独使用双下划线.排除双下划线方法可能会导致其他问题.特别是,如果您决定在使用此元类的类上定义一个真正的魔术方法(例如__str__),则会导致失败.

__notify__ and __notification__ are not Python magic methods. It appears that you're excluding double-underscore methods because you are using double underscores to indicate something for your own purposes. You shouldn't do this. Use some other prefix for your own methods (like _Notifier or something) if you must, then exclude those methods and leave the double-underscore ones alone. Excluding double-underscore methods could cause other problems. In particular, it will cause failure if you ever decide to define a real magic method (e.g., __str__) on a class that uses this metaclass.

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

(To clarify: you can use methods that begin with double underscores if you want, as private attributes, although this still is probably not a good idea. If you do this, though, you need to make sure you only do your special processing on those attributes, and not ones that begin and end with double underscores, which are Python-internal magic methods. What you shouldn't do is create your own names that begin and end with double underscores, like __notify__.)

这篇关于元类的奇怪继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆