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.
Предыдущий ответ показывает, как переопределить 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