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?


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

'Null', ако 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