1 class 继承了 2 个不同的 metaclasses(abcmeta 和用户定义的 meta)
1 class inherits 2 different metaclasses (abcmeta and user defined meta)
我有一个 class1 需要从 2 个不同的 metaclasses 继承,即 Meta1 和 abc.ABCMeta
当前实施:
Meta1 的实现:
class Meta1(type):
def __new__(cls, classname, parent, attr):
new_class = type.__new__(cls, classname, parent, attr)
return super(Meta1, cls).__new__(cls, classname, parent, attr)
实施class1摘要
class class1Abstract(object):
__metaclass__ = Meta1
__metaclass__ = abc.ABCMeta
主要实施class
class mainClass(class1Abstract):
# do abstract method stuff
我知道将 2 个不同的元实现两次是错误的。
我改变了 metclass 的加载方式(尝试了几次),我得到了这个
TypeError: 调用 metaclass bases
时出错
metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我运行没主意了...
EDITED 1
我试过这个解决方案,但 mainClass 不是 class1Abstract
的实例
print issubclass(mainClass, class1Abstract) # true
print isinstance(mainClass, class1Abstract) # false
实施class1摘要
class TestMeta(Meta1):
pass
class AbcMeta(object):
__metaclass__ = abc.ABCMeta
pass
class CombineMeta(AbcMeta, TestMeta):
pass
class class1Abstract(object):
__metaclass__ = CombineMeta
@abc.abstractmethod
def do_shared_stuff(self):
pass
@abc.abstractmethod
def test_method(self):
''' test method '''
mainClass 的实现
class mainClass(class1Abstract):
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(mainClass, class1Abstract) # False
因为 mainClass 继承自抽象 class python 应该抱怨 test_method 没有在 mainClass 中实现。但它不会抱怨任何事情,因为 print isinstance(mainClass, class1Abstract) # False
dir(mainClass)
没有
['__abstractmethods__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry']
求助!
EDITED 2
实施class1摘要
CombineMeta = type("CombineMeta", (abc.ABCMeta, Meta1), {})
class class1Abstract(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def do_shared_stuff(self):
pass
@abc.abstractmethod
def test_method(self):
''' test method '''
mainClass 的实现
class mainClass(class1Abstract):
__metaclass__ = CombineMeta
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(mainClass, class1Abstract) # False
dir(mainClass)
现在有 abstractmethod 的魔术方法
['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'do_shared_stuff', 'test_method']
但是 python 不会警告 test_method 没有被实例化
求助!
无需设置两个元类:Meta1
应继承自 abc.ABCMeta
。
默认情况下 python 仅在您尝试实例化 class 时抱怨 class 具有抽象方法,而不是在您创建 class 时抱怨。这是因为 class 的 metaclass 仍然是 ABCMeta
(或其子类型),所以它允许有抽象方法。
要获得您想要的内容,您需要编写自己的元class,当它发现 __abstractmethods__
不为空时会引发错误。这样,当不再允许使用 class 抽象方法时,您必须明确说明。
from abc import ABCMeta, abstractmethod
class YourMeta(type):
def __init__(self, *args, **kwargs):
super(YourMeta, self).__init__(*args, **kwargs)
print "YourMeta.__init__"
def __new__(cls, *args, **kwargs):
newcls = super(YourMeta, cls).__new__(cls, *args, **kwargs)
print "YourMeta.__new__"
return newcls
class ConcreteClassMeta(ABCMeta):
def __init__(self, *args, **kwargs):
super(ConcreteClassMeta, self).__init__(*args, **kwargs)
if self.__abstractmethods__:
raise TypeError("{} has not implemented abstract methods {}".format(
self.__name__, ", ".join(self.__abstractmethods__)))
class CombinedMeta(ConcreteClassMeta, YourMeta):
pass
class AbstractBase(object):
__metaclass__ = ABCMeta
@abstractmethod
def f(self):
raise NotImplemented
try:
class ConcreteClass(AbstractBase):
__metaclass__ = CombinedMeta
except TypeError as e:
print "Couldn't create class --", e
class ConcreteClass(AbstractBase):
__metaclass__ = CombinedMeta
def f(self):
print "ConcreteClass.f"
assert hasattr(ConcreteClass, "__abstractmethods__")
c = ConcreteClass()
c.f()
输出:
YourMeta.__new__
YourMeta.__init__
Couldn't create class -- ConcreteClass has not implemented abstract methods f
YourMeta.__new__
YourMeta.__init__
ConcreteClass.f
在Python中,每个class只能有一个metaclass,不多。但是,可以通过混合这些元classes do 来实现类似的行为(比如它有多个元classes)。
让我们从简单的开始。我们自己的 metaclass,只是将新属性添加到 class:
class SampleMetaClass(type):
"""Sample metaclass: adds `sample` attribute to the class"""
def __new__(cls, clsname, bases, dct):
dct['sample'] = 'this a sample class attribute'
return super(SampleMetaClass, cls).__new__(cls, clsname, bases, dct)
class MyClass(object):
__metaclass__ = SampleMetaClass
print("SampleMetaClass was mixed in!" if 'sample' in MyClass.__dict__ else "We've had a problem here")
这会打印 "SampleMetaClass was mixed in!",所以我们知道我们的基本元 class 工作正常。
现在,另一方面,我们想要一个抽象 class,最简单的是:
from abc import ABCMeta, abstractmethod
class AbstractClass(object):
__metaclass__ = ABCMeta
@abstractmethod
def implement_me(self):
pass
class IncompleteImplementor(AbstractClass):
pass
class MainClass(AbstractClass):
def implement_me(self):
return "correct implementation in `MainClass`"
try:
IncompleteImplementor()
except TypeError as terr:
print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()
这将打印 "missing implementation in IncompleteImplementor
",然后是 "correct implementation in MainClass
"。因此,摘要 class 也可以正常工作。
现在,我们有 2 个简单的实现,我们需要将两个元classes 的行为混合在一起。这里有多个选项。
选项 1 - subclassing
可以将 SampleMetaClass
实现为 ABCMeta
的子 class - metaclasses 也是 classes 并且可以继承它们!
class SampleMetaABC(ABCMeta):
"""Same as SampleMetaClass, but also inherits ABCMeta behaviour"""
def __new__(cls, clsname, bases, dct):
dct['sample'] = 'this a sample class attribute'
return super(SampleMetaABC, cls).__new__(cls, clsname, bases, dct)
现在,我们更改 AbstractClass
定义中的元class:
class AbstractClass(object):
__metaclass__ = SampleMetaABC
@abstractmethod
def implement_me(self):
pass
# IncompleteImplementor and MainClass implementation is the same, but make sure to redeclare them if you use same interpreter from the previous test
和运行我们再次测试:
try:
IncompleteImplementor()
except TypeError as terr:
print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()
print("sample was added!" if 'sample' in IncompleteImplementor.__dict__ else "We've had a problem here")
print("sample was added!" if 'sample' in MainClass.__dict__ else "We've had a problem here")
这仍然会打印出 IncompleteImplementor
未正确实现,MainClass
是,并且两者现在都添加了 sample
class 级属性。这里要注意的是,元 class 的 Sample
部分也成功应用于 IncompleteImplementor
(好吧,没有理由不这样做)。
正如预期的那样,isinstance
和 issubclass
仍然按预期工作:
print(issubclass(MainClass, AbstractClass)) # True, inheriting from AbtractClass
print(isinstance(MainClass, AbstractClass)) # False as expected - AbstractClass is a base class, not a metaclass
print(isinstance(MainClass(), AbstractClass)) # True, now created an instance here
选项 2 - 编写元classes
其实问题本身就有这个选项,只需要稍微修改一下就可以了。将新元class 声明为几个更简单的元class 的组合以混合它们的行为:
SampleMetaWithAbcMixin = type('SampleMetaWithAbcMixin', (ABCMeta, SampleMetaClass), {})
如前所述,为 AbstractClass
更改 metaclass(同样,IncompleteImplementor
和 MainClass
不会更改,但如果在同一个解释器中则重新声明它们):
class AbstractClass(object):
__metaclass__ = SampleMetaWithAbcMixin
@abstractmethod
def implement_me(self):
pass
从这里开始,运行相同的测试应该会产生相同的结果:ABCMeta
仍然有效并确保实现 @abstractmethod
-s,SampleMetaClass
添加一个 sample
属性。
我个人更喜欢后一种选择,原因与我通常更喜欢组合而不是继承的原因相同:多个(元)classes 之间最终需要的组合越多 - 组合越简单.
有关元的更多信息classes
最后,我读过的对 metaclasses 最好的解释是这个 SO 答案:
What is a metaclass in Python?
在您编辑的代码(1 和 2)中,您几乎完成了。唯一错误的是你如何使用 isinstance
。您想要检查 class 实例(在本例中为 self
)是否是给定 class(class1Abstract
)的实例。例如:
class mainClass(class1Abstract):
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(self, class1Abstract) # True
我有一个 class1 需要从 2 个不同的 metaclasses 继承,即 Meta1 和 abc.ABCMeta
当前实施:
Meta1 的实现:
class Meta1(type):
def __new__(cls, classname, parent, attr):
new_class = type.__new__(cls, classname, parent, attr)
return super(Meta1, cls).__new__(cls, classname, parent, attr)
实施class1摘要
class class1Abstract(object):
__metaclass__ = Meta1
__metaclass__ = abc.ABCMeta
主要实施class
class mainClass(class1Abstract):
# do abstract method stuff
我知道将 2 个不同的元实现两次是错误的。
我改变了 metclass 的加载方式(尝试了几次),我得到了这个 TypeError: 调用 metaclass bases
时出错metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我运行没主意了...
EDITED 1
我试过这个解决方案,但 mainClass 不是 class1Abstract
的实例print issubclass(mainClass, class1Abstract) # true
print isinstance(mainClass, class1Abstract) # false
实施class1摘要
class TestMeta(Meta1):
pass
class AbcMeta(object):
__metaclass__ = abc.ABCMeta
pass
class CombineMeta(AbcMeta, TestMeta):
pass
class class1Abstract(object):
__metaclass__ = CombineMeta
@abc.abstractmethod
def do_shared_stuff(self):
pass
@abc.abstractmethod
def test_method(self):
''' test method '''
mainClass 的实现
class mainClass(class1Abstract):
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(mainClass, class1Abstract) # False
因为 mainClass 继承自抽象 class python 应该抱怨 test_method 没有在 mainClass 中实现。但它不会抱怨任何事情,因为 print isinstance(mainClass, class1Abstract) # False
dir(mainClass)
没有
['__abstractmethods__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry']
求助!
EDITED 2
实施class1摘要
CombineMeta = type("CombineMeta", (abc.ABCMeta, Meta1), {})
class class1Abstract(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def do_shared_stuff(self):
pass
@abc.abstractmethod
def test_method(self):
''' test method '''
mainClass 的实现
class mainClass(class1Abstract):
__metaclass__ = CombineMeta
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(mainClass, class1Abstract) # False
dir(mainClass)
现在有 abstractmethod 的魔术方法
['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__metaclass__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version', '_abc_registry', 'do_shared_stuff', 'test_method']
但是 python 不会警告 test_method 没有被实例化
求助!
无需设置两个元类:Meta1
应继承自 abc.ABCMeta
。
默认情况下 python 仅在您尝试实例化 class 时抱怨 class 具有抽象方法,而不是在您创建 class 时抱怨。这是因为 class 的 metaclass 仍然是 ABCMeta
(或其子类型),所以它允许有抽象方法。
要获得您想要的内容,您需要编写自己的元class,当它发现 __abstractmethods__
不为空时会引发错误。这样,当不再允许使用 class 抽象方法时,您必须明确说明。
from abc import ABCMeta, abstractmethod
class YourMeta(type):
def __init__(self, *args, **kwargs):
super(YourMeta, self).__init__(*args, **kwargs)
print "YourMeta.__init__"
def __new__(cls, *args, **kwargs):
newcls = super(YourMeta, cls).__new__(cls, *args, **kwargs)
print "YourMeta.__new__"
return newcls
class ConcreteClassMeta(ABCMeta):
def __init__(self, *args, **kwargs):
super(ConcreteClassMeta, self).__init__(*args, **kwargs)
if self.__abstractmethods__:
raise TypeError("{} has not implemented abstract methods {}".format(
self.__name__, ", ".join(self.__abstractmethods__)))
class CombinedMeta(ConcreteClassMeta, YourMeta):
pass
class AbstractBase(object):
__metaclass__ = ABCMeta
@abstractmethod
def f(self):
raise NotImplemented
try:
class ConcreteClass(AbstractBase):
__metaclass__ = CombinedMeta
except TypeError as e:
print "Couldn't create class --", e
class ConcreteClass(AbstractBase):
__metaclass__ = CombinedMeta
def f(self):
print "ConcreteClass.f"
assert hasattr(ConcreteClass, "__abstractmethods__")
c = ConcreteClass()
c.f()
输出:
YourMeta.__new__
YourMeta.__init__
Couldn't create class -- ConcreteClass has not implemented abstract methods f
YourMeta.__new__
YourMeta.__init__
ConcreteClass.f
在Python中,每个class只能有一个metaclass,不多。但是,可以通过混合这些元classes do 来实现类似的行为(比如它有多个元classes)。
让我们从简单的开始。我们自己的 metaclass,只是将新属性添加到 class:
class SampleMetaClass(type):
"""Sample metaclass: adds `sample` attribute to the class"""
def __new__(cls, clsname, bases, dct):
dct['sample'] = 'this a sample class attribute'
return super(SampleMetaClass, cls).__new__(cls, clsname, bases, dct)
class MyClass(object):
__metaclass__ = SampleMetaClass
print("SampleMetaClass was mixed in!" if 'sample' in MyClass.__dict__ else "We've had a problem here")
这会打印 "SampleMetaClass was mixed in!",所以我们知道我们的基本元 class 工作正常。
现在,另一方面,我们想要一个抽象 class,最简单的是:
from abc import ABCMeta, abstractmethod
class AbstractClass(object):
__metaclass__ = ABCMeta
@abstractmethod
def implement_me(self):
pass
class IncompleteImplementor(AbstractClass):
pass
class MainClass(AbstractClass):
def implement_me(self):
return "correct implementation in `MainClass`"
try:
IncompleteImplementor()
except TypeError as terr:
print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()
这将打印 "missing implementation in IncompleteImplementor
",然后是 "correct implementation in MainClass
"。因此,摘要 class 也可以正常工作。
现在,我们有 2 个简单的实现,我们需要将两个元classes 的行为混合在一起。这里有多个选项。
选项 1 - subclassing
可以将 SampleMetaClass
实现为 ABCMeta
的子 class - metaclasses 也是 classes 并且可以继承它们!
class SampleMetaABC(ABCMeta):
"""Same as SampleMetaClass, but also inherits ABCMeta behaviour"""
def __new__(cls, clsname, bases, dct):
dct['sample'] = 'this a sample class attribute'
return super(SampleMetaABC, cls).__new__(cls, clsname, bases, dct)
现在,我们更改 AbstractClass
定义中的元class:
class AbstractClass(object):
__metaclass__ = SampleMetaABC
@abstractmethod
def implement_me(self):
pass
# IncompleteImplementor and MainClass implementation is the same, but make sure to redeclare them if you use same interpreter from the previous test
和运行我们再次测试:
try:
IncompleteImplementor()
except TypeError as terr:
print("missing implementation in `IncompleteImplementor`")
MainClass().implement_me()
print("sample was added!" if 'sample' in IncompleteImplementor.__dict__ else "We've had a problem here")
print("sample was added!" if 'sample' in MainClass.__dict__ else "We've had a problem here")
这仍然会打印出 IncompleteImplementor
未正确实现,MainClass
是,并且两者现在都添加了 sample
class 级属性。这里要注意的是,元 class 的 Sample
部分也成功应用于 IncompleteImplementor
(好吧,没有理由不这样做)。
正如预期的那样,isinstance
和 issubclass
仍然按预期工作:
print(issubclass(MainClass, AbstractClass)) # True, inheriting from AbtractClass
print(isinstance(MainClass, AbstractClass)) # False as expected - AbstractClass is a base class, not a metaclass
print(isinstance(MainClass(), AbstractClass)) # True, now created an instance here
选项 2 - 编写元classes
其实问题本身就有这个选项,只需要稍微修改一下就可以了。将新元class 声明为几个更简单的元class 的组合以混合它们的行为:
SampleMetaWithAbcMixin = type('SampleMetaWithAbcMixin', (ABCMeta, SampleMetaClass), {})
如前所述,为 AbstractClass
更改 metaclass(同样,IncompleteImplementor
和 MainClass
不会更改,但如果在同一个解释器中则重新声明它们):
class AbstractClass(object):
__metaclass__ = SampleMetaWithAbcMixin
@abstractmethod
def implement_me(self):
pass
从这里开始,运行相同的测试应该会产生相同的结果:ABCMeta
仍然有效并确保实现 @abstractmethod
-s,SampleMetaClass
添加一个 sample
属性。
我个人更喜欢后一种选择,原因与我通常更喜欢组合而不是继承的原因相同:多个(元)classes 之间最终需要的组合越多 - 组合越简单.
有关元的更多信息classes
最后,我读过的对 metaclasses 最好的解释是这个 SO 答案: What is a metaclass in Python?
在您编辑的代码(1 和 2)中,您几乎完成了。唯一错误的是你如何使用 isinstance
。您想要检查 class 实例(在本例中为 self
)是否是给定 class(class1Abstract
)的实例。例如:
class mainClass(class1Abstract):
def do_shared_stuff(self):
print issubclass(mainClass, class1Abstract) # True
print isinstance(self, class1Abstract) # True