如何在构造中用参数覆盖Python类的__str__方法?

时间:2015-02-20 07:20:37

标签: python

我可以这样做:

class Person:
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName

    def __str__(self):
        print self.firstName

bob = Person('Robert', 'Smith')
print bob
>>> Robert

但我想更普遍地做,而不是手动覆盖每个班级__str__。我尝试过使用装饰器和元类,但我很困惑。我认为应该可以做类似的事情,但我不知道如何使装饰器或元类接受一个参数,仍然创建一个正确的可实例化对象:

class strAs:

    def __init__(self, prop):
        self.prop = prop

    def __call__(self, cls):
        decoratorSelf = self
        def wrapper(*args, **kwargs):
            def __str__(s):
                return getattr(s, decoratorSelf.prop)

            c = cls(*args, **kwargs)
            c.__str__ = __str__

            return c
        return wrapper


@strAs(prop='firstName')
class Person:
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName

@strAs(prop='name')
class Dog:
    def __init__(self, name):
        self.name = name

bob = Person('Robert', 'Smith')
fido = Dog('Fido')

print bob
print fido

>>> Robert
Fido

但这失败了:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/py6495xVN", line 33, in <module>
    print bob
TypeError: __str__() takes exactly 1 argument (0 given)

我无法弄清楚__str__()未将self传递给它的原因。我注意到bob类的Person实例似乎缺少一些典型的对象方法,所以我试着像这样继承object(并添加了两个print语句):

class strAs(object):

    def __init__(self, prop):
        self.prop = prop

    def __call__(self, cls):
        decoratorSelf = self
        def wrapper(*args, **kwargs):
            print 'in wrapper'
            def __newstr__(s):
                print 'in newstr'
                return getattr(s, decoratorSelf.prop)

            c = cls(*args, **kwargs)
            c.__str__ = __newstr__

            return c
        return wrapper


@strAs(prop='firstName')
class Person(object):
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName

@strAs(prop='name')
class Dog(object):
    def __init__(self, name):
        self.name = name

bob = Person('Robert', 'Smith')
fido = Dog('Fido')

print bob
print fido

现在我得到了一些显然正确调用__str__的输出:

>>> in wrapper
in wrapper
<__main__.Person object at 0x7f179389e450>
<__main__.Dog object at 0x7f179389e510>

但它没有按原样打印属性,并且它甚至不会使用__newstr__函数,因为它不打印{{1} }。但是,如果我检查in newstr,我发现它确实是bob.__str__

我一定错过了一些明显的东西,但我觉得现在这已经过去了。 :/

1 个答案:

答案 0 :(得分:3)

在你的情况下,我只是让装饰者修改这个类。如

class strAs:

    def __init__(self, prop):
        self.prop = prop

    def __call__(self, cls):
        prop = self.prop
        def __str__(s):
            return getattr(s, prop)
        cls.__str__ = __str__
        return cls