让我觉得一个类的默认__repr__()
是如此无法提供信息:
>>> class Opaque(object): pass
...
>>> Opaque()
<__main__.Opaque object at 0x7f3ac50eba90>
......所以我一直在考虑如何改进它。经过一番考虑后,我想出了这个抽象的基类,它利用了pickle
协议的__getnewargs__()
方法:
from abc import abstractmethod
class Repro(object):
"""Abstract base class for objects with informative ``repr()`` behaviour."""
@abstractmethod
def __getnewargs__(self):
raise NotImplementedError
def __repr__(self):
signature = ", ".join(repr(arg) for arg in self.__getnewargs__())
return "%s(%s)" % (self.__class__.__name__, signature)
以下是其用法的一个简单示例:
class Transparent(Repro):
"""An example of a ``Repro`` subclass."""
def __init__(self, *args):
self.args = args
def __getnewargs__(self):
return self.args
...以及由此产生的repr()
行为:
>>> Transparent("an absurd signature", [1, 2, 3], str)
Transparent('an absurd signature', [1, 2, 3], <type 'str'>)
>>>
现在,我可以看到Python默认情况下不会立即执行此操作的一个原因 - 要求每个类定义__getnewargs__()
方法比预期(但不要求)定义{{{}更加繁重1}}方法。
我想知道的是:它有多危险和/或脆弱?另一方面,我想不出任何可能出现严重错误的事情,除非如果一个__repr__()
实例包含自己,你将获得无限递归......但这是可以解决的,但代价是制作上面的代码丑陋。
我还错过了什么?
答案 0 :(得分:4)
如果您遇到这种情况,为什么不通过使用装饰器从__init__
自动获取参数?然后,您不需要手动处理用户负担,并且您可以透明地处理具有多个参数的常规方法签名。这是我提出的快速版本:
def deco(f):
def newFunc(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs
f(self, *args, **kwargs)
return newFunc
class AutoRepr(object):
def __repr__(self):
args = ', '.join(repr(arg) for arg in self._args)
kwargs = ', '.join('{0}={1}'.format(k, repr(v)) for k, v in self._kwargs.iteritems())
allArgs = ', '.join([args, kwargs]).strip(', ')
return '{0}({1})'.format(self.__class__.__name__, allArgs)
现在您可以正常定义AutoRepr的子类,并使用正常的__init__
签名:
class Thingy(AutoRepr):
@deco
def __init__(self, foo, bar=88):
self.foo = foo
self.bar = bar
__repr__
会自动生效:
>>> Thingy(1, 2)
Thingy(1, 2)
>>> Thingy(10)
Thingy(10)
>>> Thingy(1, bar=2)
Thingy(1, bar=2)
>>> Thingy(bar=1, foo=2)
Thingy(foo=2, bar=1)
>>> Thingy([1, 2, 3], "Some junk")
Thingy([1, 2, 3], 'Some junk')
将@deco
放在__init__
上要比编写整个__getnewargs__
容易得多。如果你甚至不想这样做,你可以编写一个元类,以这种方式自动修饰__init__
方法。
答案 1 :(得分:2)
这个整体想法的一个问题是,某些类型的对象的状态并不完全依赖于赋予其构造函数的参数。对于一个简单的案例,考虑一个具有随机状态的类:
import random
def A(object):
def __init__(self):
self.state = random.random()
这个类没有办法正确实现__getnewargs__
,所以你__repr__
的植入也是不可能的。可能是像上面那样的类没有很好地设计。但是pickle
可以毫无问题地处理它(我假设使用从__reduce__
继承的object
方法,但我的pickle-fu还不足以确定地这样说。)
这就是为什么__repr__
可以编码来做任何你想要的事情。如果您希望内部状态可见,则可以让您的班级__repr__
执行此操作。如果对象应该是不透明的,那么你也可以这样做。对于上面的课程,我可能会像这样实现__repr__
:
def __repr__(self):
return "<A object with state=%f>" % self.state