Як створити спеціальне представлення рядків для об’єкта класу?


202

Розглянемо цей клас:

class foo(object):
    pass

Представлення рядків за замовчуванням виглядає приблизно так:

>>> str(foo)
"<class '__main__.foo'>"

Як я можу зробити цей дисплей користувацьким рядком?

Відповіді:


272

Реалізація __str__()або __repr__()в метакласі класу.

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object):
  __metaclass__ = MC

print C

Використовуйте, __str__якщо ви маєте на увазі читабельну строфікацію, використовуйте __repr__для однозначних уявлень.


2
Я хотів би створити якийсь декоратор класу, тому я можу легко встановити власні рядкові представлення для моїх класів без необхідності писати метаклас для кожного з них. Я не дуже знайомий з метакласами Python, тож чи можете ви дати мені там будь-які вказівки?
Бьорн Поллекс

На жаль, цього не можна зробити з декораторами класу; він повинен бути встановлений, коли клас визначений.
Ігнасіо Васкес-Абрамс

10
@ Space_C0wb0y: Ви можете додати рядок як _representationу тіло класу, так і return self._representationв __repr__()методі метакласу.
Свен Марнах

@ BjörnPollex Ви, можливо, зможете вирішити це за допомогою декоратора, але я би сподівався, що вам доведеться боротися з великою кількістю тонкощів мови. І навіть якщо ви все ще зобов'язані використовувати метаклас в тій чи іншій мірі, оскільки ви не хочете використовувати за замовчуванням __repr__представлення C. Альтернативою _representationчлену є створення фабрики метакласу, яка виробляє метаклас з належним __repr__(це може бути добре, якщо ви багато використовуєте).
злетіння


22
class foo(object):
    def __str__(self):
        return "representation"
    def __unicode__(self):
        return u"representation"

10
Це змінює представлення рядків для instancesкласу, а не для самого класу.
тауран

вибачте, не бачу другу частину вашого допису. Використовуйте вищевказаний метод.
Андрій Губарев

6
@RobertSiemer Чому? Хоча його відповідь не стосується конкретного питання ОП, вона все-таки корисна. Це мені допомогло. І на перший погляд, я не бачу жодного питання, яке б запитувало реалізацію, наприклад. Тому, ймовірно, люди приземляються на цій сторінці першими.
акінурі

14

Якщо вам потрібно вибрати __repr__або __str__перейти на перший, як за замовчуванням __str__виклики впровадження, __repr__коли це не було визначено.

Приклад користувальницького Vector3:

class Vector3(object):
    def __init__(self, args):
        self.x = args[0]
        self.y = args[1]
        self.z = args[2]

    def __repr__(self):
        return "Vector3([{0},{1},{2}])".format(self.x, self.y, self.z)

    def __str__(self):
        return "x: {0}, y: {1}, z: {2}".format(self.x, self.y, self.z)

У цьому прикладі reprзнову повертається рядок, який можна безпосередньо споживати / виконувати, тоді strяк корисніший як вихід налагодження.

v = Vector3([1,2,3])
print repr(v)    #Vector3([1,2,3])
print str(v)     #x:1, y:2, z:3

1
Хоча ваша думка щодо __repr__vs __str__є правильною, це не відповідає дійсному питанню, що стосується об'єктів class, а не екземплярів.
Бьорн Поллекс

Дякую за відгуки, цілком наглядайте за цим. Дозвольте переглянути свою відповідь.
користувач1767754

Я думаю, що ви реалізацію для repr та str замінені
jspencer

4

Відповідна відповідь Ігнасіо Васкеса-Абрамса цілком правильна. Однак це покоління Python 2. Оновленням для поточного Python 3 буде:

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(object, metaclass=MC):
    pass

print(C)

Якщо ви хочете код, який працює через Python 2 та Python 3, ви охопили шість модулів:

from __future__ import print_function
from six import with_metaclass

class MC(type):
  def __repr__(self):
    return 'Wahaha!'

class C(with_metaclass(MC)):
    pass

print(C)

Нарешті, якщо у вас є один клас, у якому ви хочете мати спеціальний статичний репр, підхід на основі класу вище працює. Але якщо їх декілька, вам доведеться генерувати метаклас, подібний до MCкожного, і це може набриднути. У такому випадку, якщо зробити ваш метапрограмування ще на крок і створити фабрику метакласу, це стає дещо більш чистим:

from __future__ import print_function
from six import with_metaclass

def custom_class_repr(name):
    """
    Factory that returns custom metaclass with a class ``__repr__`` that
    returns ``name``.
    """
    return type('whatever', (type,), {'__repr__': lambda self: name})

class C(with_metaclass(custom_class_repr('Wahaha!'))): pass

class D(with_metaclass(custom_class_repr('Booyah!'))): pass

class E(with_metaclass(custom_class_repr('Gotcha!'))): pass

print(C, D, E)

відбитки:

Wahaha! Booyah! Gotcha!

Метапрограмування - це не те, що вам зазвичай потрібно щодня, але коли вам це потрібно, воно дійсно потрапляє на місце!


0

Просто додаю до всіх тонких відповідей мою версію з прикрасою:

from __future__ import print_function
import six

def classrep(rep):
    def decorate(cls):
        class RepMetaclass(type):
            def __repr__(self):
                return rep

        class Decorated(six.with_metaclass(RepMetaclass, cls)):
            pass

        return Decorated
    return decorate


@classrep("Wahaha!")
class C(object):
    pass

print(C)

stdout:

Wahaha!

Нижні сторони:

  1. Ви не можете заявити Cбез суперкласу (ні class C:)
  2. Cекземпляри будуть екземплярами якоїсь дивної деривації, тому, ймовірно, буде хорошою ідеєю додати також і __repr__для екземплярів.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.