元类的奇怪继承 [英] Weird inheritance with metaclasses
问题描述
在尝试从具有元类的类继承时,我在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屋!