Возьмем как пример парочку выдуманных классов:
import requests
class A:
def __init__(self, a="string", b=10, c=["a", "b", "c", 1, 2, 3]):
# параметр c - произвольной длины
self.a = a
self.b = b
self.c = c
class B:
def __init__(self, A_object):
# A_object - объект типа A
self.neighbor = A_object
self.session = requests.Session()
1) Как следовало бы написать __repr__ и __str__ для этих классов?
2) Документация говорит о том, что __repr__ - это однозначное представление объект
в виде строки, которое можно использовать, чтобы воссоздать точно такой же объект,
если это невозможно, то вывести какое-нибудь полезное сообщение. Очень похоже, что требуется сериализовать объект таким образом. В чем же тогда отличие от связки __getstate__, __setstate__? Эти два метода тоже могут запросто строку вернуть и требования у этой строки такие же - однозначное представление объекта. Зачем же дублирование?
И наоборот, как замену getstate, setstate можно придумать следующее:
def __repr__(self):
return "A({a}, {b}, {c})".format(a=self.a, b=self.b, c=self.c)
а потом вызывать это:
A1 = A()
A2 = eval(A1.__repr__())
3) Та же самая документация говорит, что __str__ - должен вывести красивое, читабельно
информационное сообщение, отражающее объект. Как тогда различить результат этого метода и какое-нибудь полезное сообщение из __repr__?
4) Далеко не всегда можно представить какой-то объект в виде строки. Например, функции
длинные коллекции, другие объекты без str и без repr. Выходит, что в этом случае либ
не выполняется требование __repr__, либо необходимо для всех подобных объектов вывести содержание __dict__, что хранит значение всех-всех методов и переменных. Выглядит это решение плоховато, что же делать?
5) Возвращаясь к документации __repr__ - можно пример ситуации, когда невозможн
однозначно представить объект как строку? Объекты ведь не имеют к квантовому миру никакого отношения - они ВСЕГДА детерминированы, всегда есть набор переменных и набор методов. но не зря же в документации эта строка?
6) А еще есть __format__, который тоже должен возвращать строку...
Ответы
Ответ 1
datetime.date хорошо иллюстрирует разницу:
>>> from datetime import date
>>> date.today() # sys.displayhook() uses repr() by default
datetime.date(2016, 6, 13)
>>> print(date.today()) # print uses str() here
2016-06-13
repr(obj) возвращает однозначное текстовое представление (representation) объект
полезное для отладки, сообщений об ошибках, REPL, которое (иногда) позволяет его (теоретически) восстановить: eval(repr(obj)) == obj.
str(obj) возвращает читаемый текст. Для многих объектов имеет смысл определить __repr__(
(так как реализация по умолчанию в object.__repr__ не слишком информативна). __str__() имеет смысл определять для объектов, для которых существует «естественное» человеко-читаемое (неспецифичное для Питона) представление (как в примере с датой), например, для логов.
Если __str__ не определён, то str() использует repr().
Разные форматы рассчитаны на разного потребителя (человек/программа, Питон/обще
назначение). Вот график, иллюстрирующий когда разные форматы удобно использовать:
^ ^
| | Более дружелюбный для человека
| +---+
| |str|
| +---+
|
| +----+ +----+
| |repr| |json|
| +----+ +----+
|
| +------+
| |pickle|
| +------+
|
| | Машино-читаемый
+--------------------------------->
Специфичный для Питона -> Более общий
1) Как следовало бы написать __repr__ и __str__ для этих классов?
class A:
def __repr__(self):
return "A(%r, %r, %r)" % (self.a, self.b, self.c)
class B:
def __repr__(self):
return "" % (self.neighbor, self.session)
Объекты B не могут быть восстановлены из repr().
2) Документация говорит о том, что __repr__ - это однозначное представление объект
в виде строки, которое можно использовать, чтобы воссоздать точно такой же объект,
если это невозможно, то вывести какое-нибудь полезное сообщение. Очень похоже, что требуется сериализовать объект таким образом. В чем же тогда отличие от связки __getstate__, __setstate__? Эти два метода тоже могут запросто строку вернуть и требования у этой строки такие же - однозначное представление объекта. Зачем же дублирование?
pickle (__getstate__/__setstate__) не является человеко-читаемым форматом. Цели
ограничения на дизайн другие. То что eval(repr(obj)) иногда работает, ещё не значит что это хорошая идея использовать это вместо pickle.loads(pickle.dumps(obj)) или json.loads(json.dumps(obj)).
Основной потребитель repr() это человек. Потребитель pickle.dumps() это как правил
программа, например, multiprocessing использует pickle для обмена данными между процессами.
3) Та же самая документация говорит, что __str__ - должен вывести красивое, читабельно
информационное сообщение, отражающее объект. Как тогда различить результат этого метода и какое-нибудь полезное сообщение из __repr__?
Если вы не видите имя типа, то это не __repr__() (за очевидным исключением констант (Python literals) таких как 'abc', 123).
4) Далеко не всегда можно представить какой-то объект в виде строки. Например
функции, длинные коллекции, другие объекты без str и без repr. Выходит, что в этом случа
либо не выполняется требование __repr__, либо необходимо для всех подобных объектов вывести содержание __dict__, что хранит значение всех-всех методов и переменных. Выглядит это решение плоховато, что же делать?
eval(repr(obj)) это подсказка, а не требование: если объект большой, то очевидн
нужно сократить его представление. Во многих случаях, можно использовать многоточие:
>>> numpy.arange(10000000)
array([ 0, 1, 2, ..., 9999997, 9999998, 9999999])
>>> print(numpy.arange(10000000))
[ 0 1 2 ..., 9999997 9999998 9999999]
Ещё раз: потребитель repr()—программист Питона, который видит это представление в REPL или debugger.
5) Возвращаясь к документации __repr__ - можно пример ситуации, когда невозможн
однозначно представить объект как строку? Объекты ведь не имеют к квантовому миру никакого отношения - они ВСЕГДА детерминированы, всегда есть набор переменных и набор методов. но не зря же в документации эта строка?
Документация говорит про случай, когда eval(repr(obj)) == obj не имеет смысла:
>>> object()
Комментариев нет:
Отправить комментарий