Вопрос по python – Переопределение специальных методов в экземпляре

21

Я надеюсь, что кто-то может ответить на это, у которого есть хорошее глубокое понимание Python :)

Рассмотрим следующий код:

<code>>>> class A(object):
...     pass
...
>>> def __repr__(self):
...     return "A"
...
>>> from types import MethodType
>>> a = A()
>>> a
<__main__.A object at 0x00AC6990>
>>> repr(a)
'<__main__.A object at 0x00AC6990>'
>>> setattr(a, "__repr__", MethodType(__repr__, a, a.__class__))
>>> a
<__main__.A object at 0x00AC6990>
>>> repr(a)
'<__main__.A object at 0x00AC6990>'
>>>
</code>

Обратите внимание, что repr (a) не приводит к ожидаемому результату "A" ? Я хочу знать, почему это так и есть ли способ достичь этого ...

Я контрастирую, однако, работает следующий пример (Maybe because we're not trying to override a special method?):

<code>>>> class A(object):
...     def foo(self):
...             return "foo"
...
>>> def bar(self):
...     return "bar"
...
>>> from types import MethodType
>>> a = A()
>>> a.foo()
'foo'
>>> setattr(a, "foo", MethodType(bar, a, a.__class__))
>>> a.foo()
'bar'
>>>
</code>
Вы, вероятно, получите гораздо более поучительный ответ, если скажете, чего в конечном итоге пытаетесь достичь. mattmanser
Для классов нового стиля специальные методы ищутся в классе, а не в экземпляре. Смотрите мой ответ ниже для более подробной информации и некоторых обходных путей. Raymond Hettinger
Я не могу описать, что я хочу сделать, в деталях. Сожалею. Короче говоря, я пытаюсь смоделировать прототип ОО модели, где я могу выполнять такие операторы, как: World = Object (). Clone (). Mixin (World); Где World - это класс с коллекцией методов, которые переопределяют / заменяют один в экземпляре Object.clone (). James Mills

Ваш Ответ

4   ответа
3

__x__()) определены для класса, а не для экземпляра.

Это имеет смысл, когда вы думаете о__new__() - было бы невозможно вызвать это для экземпляра, поскольку экземпляр не существует, когда он вызывается.

Таким образом, вы можете сделать это для класса в целом, если вы хотите:

>>> A.__repr__ = __repr__
>>> a
A

Или в индивидуальном случае, какв ответе kindall, (Обратите внимание, что здесь есть много общего, но я подумал, что мои примеры добавлены достаточно, чтобы опубликовать это тоже).

20

__ по экземпляру, но только по классу, по-видимому, для улучшения производительности. Так что нет возможности переопределить__repr__() непосредственно на экземпляре и заставить его работать. Вместо этого вам нужно сделать что-то вроде этого:

class A(object):
    def __repr__(self):
        return self._repr()
    def _repr(self):
        return object.__repr__(self)

Теперь вы можете переопределить__repr__() на экземпляр путем замены_repr().

Error: User Rate Limit ExceededthisError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded__hash__()Error: User Rate Limit Exceeded__repr__()Error: User Rate Limit Exceeded__getattribute__ alsoError: User Rate Limit ExceededisError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded James Mills
Error: User Rate Limit ExceededguaranteeError: User Rate Limit Exceeded
Error: User Rate Limit Exceedednot about performanceError: User Rate Limit Exceededclass objectsError: User Rate Limit Exceeded__hash__Error: User Rate Limit Exceeded__hash__Error: User Rate Limit Exceededhash(ClassObject)Error: User Rate Limit Exceeded
4

который обходит экземпляры. Вот выдержка изисточник:

  1164 /* Internal routines to do a method lookup in the type
  1165    without looking in the instance dictionary
  1166    (so we can't use PyObject_GetAttr) but still binding
  1167    it to the instance.  The arguments are the object,
  1168    the method name as a C string, and the address of a
  1169    static variable used to cache the interned Python string.
  1170 
  1171    Two variants:
  1172 
  1173    - lookup_maybe() returns NULL without raising an exception
  1174      when the _PyType_Lookup() call fails;
  1175 
  1176    - lookup_method() always raises an exception upon errors.
  1177 
  1178    - _PyObject_LookupSpecial() exported for the benefit of other places.
  1179 */

Вы можете перейти на класс старого стиля (не наследуйте отobject) или вы можете добавить методы диспетчера к классу (методы, которые направляют поиск обратно в экземпляр). Для примера методов диспетчера экземпляра, см. Рецепт вhttp://code.activestate.com/recipes/578091

Error: User Rate Limit Exceeded James Mills
10

Специальный метод поиска:

For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary … In addition to bypassing any instance attributes in the interest of correctness, implicit special method lookup generally also bypasses the __getattribute__() method even of the object’s metaclass

(Часть, которую я вычеркнул, объясняет причину этого, если вы заинтересованы в этом.)

Python не документирует точно, когда реализация должна или не должна искать метод для типа; фактически все, что он документирует, состоит в том, что реализации могут или не могут искать экземпляр для поиска специальных методов, так что вы не должны рассчитывать ни на один из них.

Как вы можете догадаться из результатов вашего теста, в реализации CPython,__repr__ является одной из функций, ищущих по типу.

В 2.x все немного по-другому, в основном из-за присутствия классических классов, но пока вы только создаете классы нового стиля, вы можете думать о них как об одном и том же.

Самая распространенная причина, по которой люди хотят это сделать, - это обезьяны-патчи разных экземпляров объекта, чтобы делать разные вещи. Вы не можете сделать это специальными методами, поэтому & # x2026; что ты можешь сделать? Это чистое решение и хакерское решение.

Чистое решение заключается в реализации специального метода в классе, который просто вызывает обычный метод в экземпляре. Затем вы можете использовать этот обычный метод для каждого экземпляра. Например:

class C(object):
    def __repr__(self):
        return getattr(self, '_repr')()
    def _repr(self):
        return 'Boring: {}'.format(object.__repr__(self))

c = C()
def c_repr(self):
    return "It's-a me, c_repr: {}".format(object.__repr__(self))
c._repr = c_repr.__get__(c)

Хакерское решение заключается в создании нового подкласса на лету и переклассификации объекта. Я подозреваю, что любой, у кого действительно есть ситуация, когда это хорошая идея, будет знать, как реализовать ее из этого предложения, и любой, кто не знает, как это сделать, не должен пытаться, поэтому я оставлю это на этом.

Error: User Rate Limit Exceededgetattr(self, '_repr')()Error: User Rate Limit Exceededself._repr()?
Error: User Rate Limit Exceeded__get__(c, type(c))Error: User Rate Limit Exceededtypes.MethodTypeError: User Rate Limit Exceeded

Похожие вопросы