__repr__ 装饰器在子类上失败
__repr__ decorator fails on subclasses
背景
我编写了一个修饰函数来修改给定 class 的 __repr__
,这样当调用 class 实例时,它的所有属性都会打印给用户。当在下面示例中的 Container
class 上使用时,装饰器 __repr__dec
的行为符合预期。
输入
def __repr__wrapper(self):
"""Show all attributes."""
return "Attributes: "+", ".join(list(self.__dict__.keys()))
def __repr__dec(func):
"""Replaces the __repr__ function of a class with __repr__wrapper"""
def call(*args, **kwargs):
func.__repr__ = __repr__wrapper
result = func(*args, **kwargs)
return result
return call
@__repr__dec
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
occ = Container(42, how="now")
occ
输出
Attributes: metadata, how
然而,当我尝试 subclass Container
时,我收到一条 TypeError 消息:
输入
class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
han = Handle(42)
输出
TypeError Traceback (most recent call last)
<ipython-input-17-b4c252411c1f> in <module>()
----> 1 class Handle(Container):
2 def __init__(self, *args, **kwargs):
3 Container.__init__(self, *args, **kwargs)
4
5 han = Handle(42)
TypeError: function() argument 1 must be code, not str
问题
为什么使用 __repr__dec
函数时 sub-classing Conatainer
失败?有可能解决这个问题吗?
问题是您的装饰器使 Container
成为一个函数而不再是 class。你可以很简单的控制它:
>>> type(Container)
<class 'function'>
这是因为你对装饰器的使用结束于:
声明未修饰的 class
class Container:
...
使用装饰器:
Container = __repr__dec(Container)
作为 __repr__dec
returns 函数,您确实已将 Container
更改为能够 return 具有预期 __repr__
成员的对象的函数,但它不再是 class.
你的装饰器必须 return 一个 class 如果你想以后能够子class 它:
def repr_dec(cls):
cls.__repr__ = __repr__wrapper
return cls
那么一切正常:
>>> Container
<class '__main__.Container'>
>>> occ=Container(42, how="now")
>>> occ
Attributes: metadata, how
并且你可以成功子class它:
>>> class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
>>> han = Handle(42, foo="now")
>>> han
Attributes: metadata, foo
Handle
class 从其父级继承了 __repr__
方法。
def replace_str(cls):
class Wrapper:
def __init__(self, *args, **kargs):
self.wrapped = cls(*args, **kargs)
def __getattr__(self, attrname):
return getattr(self.wrapped, attrname)
def __str__(self):
return "Attributes: " + ", ".join(list(self.wrapped.__dict__.keys()))
return Wrapper
@replace_str
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
使用代理 class 可以轻松实现这一点。
另外,metaclass
可以这样做:
class PrintKey(type):
def __new__(meta, classname, bases, namespace):
namespace['__str__'] = lambda self: "Attributes: " + ", ".join(list(self.__dict__.keys()))
return type.__new__(meta, classname, bases, namespace)
class Container(object, metaclass=PrintKey):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
背景
我编写了一个修饰函数来修改给定 class 的 __repr__
,这样当调用 class 实例时,它的所有属性都会打印给用户。当在下面示例中的 Container
class 上使用时,装饰器 __repr__dec
的行为符合预期。
输入
def __repr__wrapper(self):
"""Show all attributes."""
return "Attributes: "+", ".join(list(self.__dict__.keys()))
def __repr__dec(func):
"""Replaces the __repr__ function of a class with __repr__wrapper"""
def call(*args, **kwargs):
func.__repr__ = __repr__wrapper
result = func(*args, **kwargs)
return result
return call
@__repr__dec
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
occ = Container(42, how="now")
occ
输出
Attributes: metadata, how
然而,当我尝试 subclass Container
时,我收到一条 TypeError 消息:
输入
class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
han = Handle(42)
输出
TypeError Traceback (most recent call last)
<ipython-input-17-b4c252411c1f> in <module>()
----> 1 class Handle(Container):
2 def __init__(self, *args, **kwargs):
3 Container.__init__(self, *args, **kwargs)
4
5 han = Handle(42)
TypeError: function() argument 1 must be code, not str
问题
为什么使用 __repr__dec
函数时 sub-classing Conatainer
失败?有可能解决这个问题吗?
问题是您的装饰器使 Container
成为一个函数而不再是 class。你可以很简单的控制它:
>>> type(Container)
<class 'function'>
这是因为你对装饰器的使用结束于:
声明未修饰的 class
class Container: ...
使用装饰器:
Container = __repr__dec(Container)
作为 __repr__dec
returns 函数,您确实已将 Container
更改为能够 return 具有预期 __repr__
成员的对象的函数,但它不再是 class.
你的装饰器必须 return 一个 class 如果你想以后能够子class 它:
def repr_dec(cls):
cls.__repr__ = __repr__wrapper
return cls
那么一切正常:
>>> Container
<class '__main__.Container'>
>>> occ=Container(42, how="now")
>>> occ
Attributes: metadata, how
并且你可以成功子class它:
>>> class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
>>> han = Handle(42, foo="now")
>>> han
Attributes: metadata, foo
Handle
class 从其父级继承了 __repr__
方法。
def replace_str(cls):
class Wrapper:
def __init__(self, *args, **kargs):
self.wrapped = cls(*args, **kargs)
def __getattr__(self, attrname):
return getattr(self.wrapped, attrname)
def __str__(self):
return "Attributes: " + ", ".join(list(self.wrapped.__dict__.keys()))
return Wrapper
@replace_str
class Container(object):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v
使用代理 class 可以轻松实现这一点。
另外,metaclass
可以这样做:
class PrintKey(type):
def __new__(meta, classname, bases, namespace):
namespace['__str__'] = lambda self: "Attributes: " + ", ".join(list(self.__dict__.keys()))
return type.__new__(meta, classname, bases, namespace)
class Container(object, metaclass=PrintKey):
def __init__(self, *args, **kwargs):
self.metadata = args[0]
for k,v in kwargs.items():
self.__dict__[k] = v