Python __repr__ и None

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

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

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

5 ответов

Решение

__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)

Я пытался найти общий __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 поэтому дисплей будет согласованным.

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

Это оставляет его как None.

"Ноль", если mystat - это "Нет", mystat

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

Предыдущий ответ показывает, как переопределить Base.__repr__было именно то, что мне нужно. Спасибо! Здесь он переписан с f-строками для Python 3.7+ и переопределением flask-sqlalchemydb.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
Другие вопросы по тегам