Python __repr__ и нет

Я новичок в Python, и в настоящее время мне нужно иметь __repr__ для класса SqlAlchemy. У меня есть целочисленный столбец, который может принимать значение Null, и SqlAlchemy преобразует его в None. Например:

class Stats(Base):
   __tablename__ = "stats"
   description = Column(String(2000))
   mystat = Column(Integer, nullable=True)

Как правильно представить поле «mystat» в функции __repr__, когда SqlAlchemy возвращает None?


person Patrizio Rullo    schedule 13.10.2011    source источник
comment
__repr__() просто возвращает строковое представление объекта python. Я не уверен, что есть правильный способ его реализации.   -  person    schedule 13.10.2011
comment
Вы понимаете, что в своем примере кода вы создаете атрибуты класса, а не атрибуты экземпляра, верно?   -  person Karl Knechtel    schedule 13.10.2011
comment
@KarlKnechtel Теперь, когда я искал в Интернете, я знаю, спасибо. Вот почему я написал, что я новичок в Python :)   -  person Patrizio Rullo    schedule 14.10.2011


Ответы (6)


__repr__ должен возвращать строку, описывающую объект. Если возможно, это должно быть допустимое выражение Python, результатом которого является равный объект. Это верно для встроенных типов, таких как int или str:

>>> x = 'foo'
>>> eval(repr(x)) == x
True

Если это невозможно, это должна быть строка '<...>', однозначно описывающая объект. Примером этого является значение по умолчанию __repr__:

>>> class Foo(object):
        pass
>>>
>>> repr(Foo())
'<__main__.Foo object at 0x02A74E50>'

Он использует адрес объекта в памяти, чтобы однозначно идентифицировать его. Конечно, адрес мало что говорит нам об объекте, поэтому полезно переопределить __repr__ и вернуть строку, описывающую состояние объекта.

Состояние объекта определяется другими объектами, которые он содержит, поэтому имеет смысл включить их repr в ваше. Это именно то, что делают list или dict:

>>> repr(['bar', Foo()])
"['bar', <__main__.Foo object at 0x02A74710>]"

В вашем случае состояние находится в ваших свойствах Column, поэтому вы хотите использовать их repr. Для этого вы можете использовать форматирование %r, оно вставляет repr() аргумента:

def __repr__(self):
    return '<Stats: description=%r, mystat=%r>' % (self.description, self.mystat)

Эквивалент с использованием нового форматирования:

def __repr__(self):
    return '<Stats: description={0.description!r}, mystat={0.mystat!r}>'.format(self)
person yak    schedule 13.10.2011
comment
Вау, спасибо. Я пропустил соглашение об угловых скобках (т.е. < ... >) для repr, если мы не можем вернуть код, который создает объект обратно. Также обратите внимание, что str возвращается к repr. - person Tomasz Gandor; 17.01.2019

Я пытался найти общий метод __repr__, который можно было бы использовать для любого объекта SQLAlchemy, и нашел только эту страницу. Итак, я решил написать свой собственный, и вот что я сделал:

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

if __debug__:
    # monkey-patch in useful repr() for all objects, but only in dev
    def tablerepr(self):
        return "<{}({})>".format(
            self.__class__.__name__,
            ', '.join(
                ["{}={}".format(k, repr(self.__dict__[k]))
                    for k in sorted(self.__dict__.keys())
                    if k[0] != '_']
            )
        )
    Base.__repr__ = tablerepr

Это расширяет класс Base для вывода содержимого конкретного экземпляра. Итак, теперь каждый создаваемый мной объект, который расширяет Base, будет иметь метод __repr__, который печатает больше, чем просто имя класса и хэш экземпляра.

РЕДАКТИРОВАТЬ: я добавил if __debug__, так как это изменение может привести к утечке информации, которую вы не хотите раскрывать в производственной среде. Я также добавил sorted, чтобы отображение было последовательным.

person Tim Tisdall    schedule 10.04.2013

Разве это не generic_repr декоратор sqlalchemy -utils предоставляет решение на основе сообщества, которое соответствует вашим потребностям?

Он оставляет его как Нет.

person Yeray Álvarez Romero    schedule 22.05.2018
comment
Это правильный ответ. Все остальные ответы, к сожалению, предшествуют этому решению, но это то, что люди должны использовать сейчас. Однако может быть полезно обернуть этот декоратор чем-то, что проверяет среду разработки, чтобы предотвратить утечку данных в рабочей среде. - person Tim Tisdall; 22.07.2020

может быть, repr(mystat) это то, что вы хотите?

person glglgl    schedule 13.10.2011

'Нуль', если mystat имеет значение None else mystat

person Austin Marshall    schedule 13.10.2011

Предыдущий ответ, показывающий, как переопределить Base.__repr__, был именно тем, что мне было нужно. Благодарю вас! Здесь он переписан с f-строками для Python 3.7+ и переопределением flask-sqlalchemy db.Model.

def override_default_repr(self):
    class_name = self.__class__.__name__
    attribs = ", ".join(
        [
            f"{k}={self.__dict__[k]!r}"
            for k in sorted(self.__dict__.keys())
            if k[0] != "_"
        ]
    )
    return f"<{class_name}({attribs})>"

db.Model.__repr__ = override_default_repr
person C.R.    schedule 04.06.2020