我写了一个装饰器函数来修改给定类的__repr__
,这样当调用类实例时,它的所有属性都会打印给用户。在下面示例中的Container
类中使用时,装饰器__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
但是在尝试子类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
使用Conatainer
函数时,为什么子类__repr__dec
会失败?有可能解决这个问题吗?
答案 0 :(得分:4)
问题在于你的装饰者使Container
成为一个函数而不再是一个类。你可以非常简单地控制它:
>>> type(Container)
<class 'function'>
这是因为您对装饰器的使用以下结尾:
宣布未修饰的课程
class Container:
...
使用装饰器:
Container = __repr__dec(Container)
当__repr__dec
返回一个函数时,你确实将Container
更改为一个能够返回具有预期__repr__
成员的对象的函数,但它不再是一个类。
如果您希望以后能够将它子类化,那么您的装饰器必须返回一个类:
def repr_dec(cls):
cls.__repr__ = __repr__wrapper
return cls
然后一切都很好:
>>> Container
<class '__main__.Container'>
>>> occ=Container(42, how="now")
>>> occ
Attributes: metadata, how
您可以成功地将其子类化:
>>> class Handle(Container):
def __init__(self, *args, **kwargs):
Container.__init__(self, *args, **kwargs)
>>> han = Handle(42, foo="now")
>>> han
Attributes: metadata, foo
Handle
类已从其父级继承了__repr__
方法。
答案 1 :(得分:1)
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
使用代理类可以轻松实现此目的。
另外,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