让我们定义简单的类装饰器函数,它创建子类并仅将'Dec'添加到原始类名:
def decorate_class(klass):
new_class = type(klass.__name__ + 'Dec', (klass,), {})
return new_class
现在将它应用于一个简单的子类定义:
class Base(object):
def __init__(self):
print 'Base init'
@decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super(MyClass, self).__init__()
现在,如果您尝试实例化装饰MyClass
,它将以无限循环结束:
c = MyClass()
# ...
# File "test.py", line 40, in __init__
# super(MyClass, self).__init__()
# RuntimeError: maximum recursion depth exceeded while calling a Python object
看来,super
无法处理这种情况,也不会从继承链中跳过当前类。
问题是,如何使用super
在类上正确使用类装饰器?
奖金问题,如何从super
创建的代理对象中获取最终类? IE浏览器。从object
表达式获取super(Base, self).__init__
类,作为定义为__init__
的已确定父类。
答案 0 :(得分:2)
由于装饰器返回一个具有不同名称的全新类,因此该类MyClass
对象甚至不存在。这不是类装饰器的用途。它们旨在为现有类添加其他功能,而不是直接将其替换为其他类。
如果您使用的是Python3,解决方案很简单 -
@decorate_class
class MyClass(Base):
def __init__(self):
print 'MyClass init'
super().__init__()
否则,我怀疑有任何直接的解决方案,您只需要更改您的实施。当您重命名该类时,您需要使用更新的名称重写覆盖__init__
。
答案 1 :(得分:2)
如果您只是想更改班级的.__name__
属性,请制作一个装饰者来做这件事。
from __future__ import print_function
def decorate_class(klass):
klass.__name__ += 'Dec'
return klass
class Base(object):
def __init__(self):
print('Base init')
@decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2输出
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
Python 3输出
MyClass init
Base init
<class '__main__.MyClass'> MyClassDec
请注意cls
的repr的差异。 (我不确定为什么你想改变一个班级的名字,但这听起来像是一个混乱的秘诀,但我想它还可以对于这个简单的例子)。
正如其他人所说,@decorator
并不打算创建一个子类。您可以使用无{arg-less} super
形式(即super().__init__()
)在Python 3中执行此操作。通过显式提供父类而不是使用super
,您可以在Python 3和Python 2中使用它。
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
@decorate_class
class MyClass(Base):
def __init__(self):
print('MyClass init')
Base.__init__(self)
c = MyClass()
cls = c.__class__
print(cls, cls.__name__)
Python 2&amp; 3输出
MyClass init
Base init
<class '__main__.MyClassDec'> MyClassDec
最后,如果我们只使用普通函数语法而不是decorate_class
来调用@decorator
,我们可以使用super
。
from __future__ import print_function
def decorate_class(klass):
name = klass.__name__
return type(name + 'Dec', (klass,), {})
class Base(object):
def __init__(self):
print('Base init')
class MyClass(Base):
def __init__(self):
print('MyClass init')
super(MyClass, self).__init__()
MyClassDec = decorate_class(MyClass)
c = MyClassDec()
cls = c.__class__
print(cls, cls.__name__)
输出与上一版本相同。
答案 2 :(得分:0)
问题是你的装饰者创建了原始的子类。这意味着super(Myclass)
现在指向......原始类本身!
我甚至无法解释super
的0 arg形式是如何在Python 3中完成工作的,我在参考手册中找不到任何明确的内容。我假设它必须使用声明时使用它的类。但我无法想象在Python2中获得该结果的方法。
如果您希望能够在Python 2中的装饰类中使用super
,则不应创建派生类,而应直接修改原始类。
例如,这是一个装饰器,它在调用任何方法之前和之后打印一行:
def decorate_class(klass):
for name, method in klass.__dict__.iteritems(): # iterate the class attributes
if isinstance(method, types.FunctionType): # identify the methods
def meth(*args, **kwargs): # define a wrapper
print "Before", name
method(*args, **kwargs)
print "After", name
setattr(klass, name, meth) # tell the class to use the wrapper
return klass
根据您的示例,它会按预期显示:
>>> c = MyClass()
Before __init__
MyClass init
Base init
After __init__