2015-02-20 2 views
3

я могу это сделать:Как переопределить метод __str__ класса 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, как это (и добавлены два оператора печати):

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__, потому что это не печать in newstr. Однако, если я проверю bob.__str__, я вижу, что это действительно <function __newstr__ at 0x7f17951d78c0>.

Мне, должно быть, недостает чего-то очевидного, но я чувствую, что это сейчас над моей головой. :/

+0

Если ваш вопрос о том, как писать декораторов, которые принимают параметры, вы можете захотеть взглянуть на этот вопрос [] (http://stackoverflow.com/questions/5929107/python-decorators-with- параметры). – BrenBarn

+0

Спасибо, я много искал и нашел такую ​​информацию, но то, что я пытаюсь сделать, это украсить класс и заменить один из его методов, передав аргумент конструктору, который изменит оформленный класс. Я могу найти множество примеров декораторов функций, декораторов функций с аргументами и декораторов классов, но я не могу найти декораторов классов (то есть, применяя декораторы к классам, а не к декораторам на основе классов) с аргументами. Я уверен, что мне не хватает чего-то очевидного, но я думаю, это не очевидно для меня. – blujay

+1

Идея такая же. Если вы прочитаете вопрос, который я связал, вы увидите ответ: «Декоратор с аргументами должен вернуть функцию, которая будет принимать функцию и вернуть другую функцию». То же самое относится к декораторам классов; это просто означает, что декоратор с аргументами должен возвращать функцию, которая возьмет класс и вернет другой класс. Другими словами, 'strAs' должен вернуть фактический декоратор. Попробуйте приспособить там ответ и задайте более конкретный вопрос, если вы не можете понять это. – BrenBarn

ответ

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 
+0

Ну, это просто, и это работает. Спасибо. Теперь, чтобы понять, почему моя последняя попытка не сработала. Это похоже на то, что я был так близко, но я чувствовал себя в темноте ... – blujay

+0

@blujay Ваша последняя версия не работает, потому что вы меняете экземпляр '__str__', а не его класс. Это не работает. Даже если бы это было так, он не принял бы аргумент 'self', поскольку он работал бы на уровне экземпляра. – glglgl

+0

О, я думал, что изменение метода экземпляра было таким, каким оно должно было работать. У меня есть чему поучиться. Спасибо за вашу помощь. – blujay

Смежные вопросы