Ответ 1
Вы можете использовать object.__repr__(obj)
. Это работает, потому что поведение по умолчанию repr
определено в object.__repr__
.
Если кто-то пишет класс в python и не может указать свой собственный метод __repr__()
, то для них предоставляется по умолчанию. Однако предположим, что мы хотим написать функцию, которая имеет то же или подобное поведение по умолчанию __repr__()
. Однако мы хотим, чтобы эта функция имела поведение метода __repr__()
по умолчанию, даже если фактический __repr__()
для класса был перегружен. То есть предположим, что мы хотим написать функцию, которая имеет такое же поведение, что и значение по умолчанию __repr__()
, независимо от того, перегрузил ли кто-либо метод __repr__()
или нет. Как мы можем это сделать?
class DemoClass:
def __init__(self):
self.var = 4
def __repr__(self):
return str(self.var)
def true_repr(x):
# [magic happens here]
s = "I'm not implemented yet"
return s
obj = DemoClass()
print(obj.__repr__())
print(true_repr(obj))
print(obj.__repr__())
выводит 4
, но print(true_repr(obj))
печатает что-то вроде: <__main__.DemoClass object at 0x0000000009F26588>
Вы можете использовать object.__repr__(obj)
. Это работает, потому что поведение по умолчанию repr
определено в object.__repr__
.
Обычно для этого мы можем использовать object.__repr__
, но это будет для "объекта для каждого элемента", поэтому:
>>> object.__repr__(4)
'<int object at 0xa6dd20>'
Так как a int
является object
, но с __repr__
overriden.
Если вы хотите перейти на один уровень перезаписи, мы можем использовать super(..)
:
>>> super(type(4), 4).__repr__() # going up one level
'<int object at 0xa6dd20>'
Для int
, что, таким образом, снова означает, что мы будем печатать <int object at ...>
, но если бы мы использовали подкласс int
, то он снова использовал бы __repr__
of int
, например:
class special_int(int):
def __repr__(self):
return 'Special int'
Затем он будет выглядеть так:
>>> s = special_int(4)
>>> super(type(s), s).__repr__()
'4'
Что мы здесь делаем, это создать прокси-объект с super(..)
. Super выполнит порядок разрешения метода (MRO) объекта и попытается найти первую функцию (из суперкласса s
), которая превзошла эту функцию. Если мы используем одиночное наследование, то это самый близкий родитель, который переопределяет функцию, но если это связано с несколькими множественными наследованиями, то это более сложно. Таким образом, мы выбираем __repr__
этого родителя и вызываем эту функцию.
Это также довольно странное приложение super
, так как обычно класс (здесь type(s)
) является фиксированным и не зависит от типа самого s
, так как в противном случае несколько таких вызовов super(..)
приведет к бесконечному циклу.
Но обычно это плохая идея сломать все-таки. Причина, по которой программист переопределяет функцию, - это изменение поведения. Несоблюдение этого может, конечно, иногда приводить к некоторым полезным функциям, но часто это приводит к тому, что кодовые контракты больше не выполняются. Например, если программист переопределяет __eq__
, он также переопределит __hash__
, если вы используете хэш другого класса и реальный __eq__
, тогда все начнет ломаться.
Вызов функции магии напрямую также часто рассматривается как антипаттерн, поэтому вам лучше избегать этого.
Обратите внимание: лучший ответ - это просто использовать object.__repr__
напрямую, как указывали другие. Но можно реализовать ту же функциональность примерно так:
>>> def true_repr(x):
... type_ = type(x)
... module = type_.__module__
... qualname = type_.__qualname__
... return f"<{module}.{qualname} object at {hex(id(x))}>"
...
Так....
>>> A()
hahahahaha
>>> true_repr(A())
'<__main__.A object at 0x106549208>'
>>>