Python 抽象装饰不起作用
Python abstract decoration not working
我正在编写装饰(出于好奇)以在 python 中制作 class 摘要。到目前为止它看起来可以正常工作,但我遇到了意外行为。
装饰的想法是这样的:
from abc import ABCMeta, abstractmethod
def abstract(cls):
cls.__metaclass__ = ABCMeta
return cls
那么在使用这个装饰的时候,只需要定义一个抽象方法就可以了
@abstract
class Dog(object):
@abstractmethod
def bark(self):
pass
但是当我测试时,我能够实例化一个 Dog 对象:
d = Dog()
d.bark() //no errors
Dog.__metaclass__ //returned "<class 'abc.ABCMeta'>"
测试直接分配 __metaclass__
时,它的行为符合预期:
class Dog(object):
__metaclass__ = ABCMeta
@abstractmethod
def bark(self):
pass
测试:
d = Dog()
"Traceback (most recent call last):
File "<pyshell#98>", line 1, in <module>
d = Dog()
TypeError: Can't instantiate abstract class Dog with abstract methods bark"
为什么会这样?
我已经能够重现您的问题。
ABCMeta.__new__ 永远不会被调用 使用你的装饰器。当你真正将它定义为 Dog 的 metaclass 时,它就会被调用。这是因为 Dog class 已经被定义,所以在之后添加 ABCMeta 实际上不会有任何区别,因为它的 new 在定义时没有被调用,不会将 Dog class 注册为抽象对象,并且不会发现其抽象方法。我邀请您阅读 ABCMeta。new 代码,您将了解它的作用。
但是,我找到了这个解决方案,仍然可以让你的装饰器工作:
def abstract(cls):
return ABCMeta(cls.__name__, cls.__bases__, dict(cls.__dict__))
现在可以正常使用了。这意味着 你必须 subclass Dog
才能调用 bark
.
python参考states:
When the class definition is read, if __metaclass__
is defined then the callable assigned to it will be called instead of type(). This allows classes or functions to be written which monitor or alter the class creation process
重要的部分是 当 class 定义为 read 时,这意味着您无法更改 metaclass 之后,这正是装饰器试图做的,因为它只是语法糖。
@some_decorator
class SomeClass(object):
pass
等同于:
class SomeClass(object):
pass
SomeClass = some_decorator(SomeClass)
所以调用装饰器的时候,class的定义已经被读取了
我正在编写装饰(出于好奇)以在 python 中制作 class 摘要。到目前为止它看起来可以正常工作,但我遇到了意外行为。
装饰的想法是这样的:
from abc import ABCMeta, abstractmethod
def abstract(cls):
cls.__metaclass__ = ABCMeta
return cls
那么在使用这个装饰的时候,只需要定义一个抽象方法就可以了
@abstract
class Dog(object):
@abstractmethod
def bark(self):
pass
但是当我测试时,我能够实例化一个 Dog 对象:
d = Dog()
d.bark() //no errors
Dog.__metaclass__ //returned "<class 'abc.ABCMeta'>"
测试直接分配 __metaclass__
时,它的行为符合预期:
class Dog(object):
__metaclass__ = ABCMeta
@abstractmethod
def bark(self):
pass
测试:
d = Dog()
"Traceback (most recent call last):
File "<pyshell#98>", line 1, in <module>
d = Dog()
TypeError: Can't instantiate abstract class Dog with abstract methods bark"
为什么会这样?
我已经能够重现您的问题。
ABCMeta.__new__ 永远不会被调用 使用你的装饰器。当你真正将它定义为 Dog 的 metaclass 时,它就会被调用。这是因为 Dog class 已经被定义,所以在之后添加 ABCMeta 实际上不会有任何区别,因为它的 new 在定义时没有被调用,不会将 Dog class 注册为抽象对象,并且不会发现其抽象方法。我邀请您阅读 ABCMeta。new 代码,您将了解它的作用。
但是,我找到了这个解决方案,仍然可以让你的装饰器工作:
def abstract(cls):
return ABCMeta(cls.__name__, cls.__bases__, dict(cls.__dict__))
现在可以正常使用了。这意味着 你必须 subclass Dog
才能调用 bark
.
python参考states:
When the class definition is read, if
__metaclass__
is defined then the callable assigned to it will be called instead of type(). This allows classes or functions to be written which monitor or alter the class creation process
重要的部分是 当 class 定义为 read 时,这意味着您无法更改 metaclass 之后,这正是装饰器试图做的,因为它只是语法糖。
@some_decorator
class SomeClass(object):
pass
等同于:
class SomeClass(object):
pass
SomeClass = some_decorator(SomeClass)
所以调用装饰器的时候,class的定义已经被读取了