Pytanie w sprawie python, metaprogramming – Nadpisywanie specjalnych metod na instancji

21

Mam nadzieję, że ktoś może odpowiedzieć na to pytanie, które dobrze rozumie Python :)

Rozważ następujący kod:

<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>

Zauważ, jak repr (a) nie daje oczekiwanego wyniku „A”? Chcę wiedzieć, dlaczego tak jest i czy istnieje sposób na osiągnięcie tego ...

W przeciwieństwie do tego poniższy przykład działa jednak (Może dlatego, że nie próbujemy zastąpić specjalnej metody?):

<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>
Prawdopodobnie otrzymasz o wiele bardziej pouczającą odpowiedź, jeśli powiesz, co ostatecznie próbujesz osiągnąć. mattmanser
W klasach nowego stylu sprawdzane są metody specjalne w klasie, a nie w instancji. Zobacz moją odpowiedź poniżej, aby uzyskać więcej informacji i kilka obejść. Raymond Hettinger
Nie mogłem opisać, co chcę robić szczegółowo. Przepraszam. Ale w skrócie próbuję modelować prototypowy model OO, w którym mogę wykonywać operatory takie jak: World = Object (). Clone (). Mixin (World); Gdzie Świat jest klasą z kolekcją metod, które zastępują / zastępują jedną w instancji Object.clone (). James Mills

Twoja odpowiedź

4   odpowiedź
3

__x__()) są zdefiniowane dla klasy, a nie dla instancji.

Ma to sens, gdy się nad tym zastanawia__new__() - niemożliwe byłoby wywołanie tego w instancji, ponieważ instancja nie istnieje, gdy jest wywoływana.

Możesz to zrobić w klasie jako całości, jeśli chcesz:

<code>>>> A.__repr__ = __repr__
>>> a
A
</code>

Lub na indywidualnej instancji, jakw odpowiedzi rodzaju. (Zauważ, że jest tu wiele podobieństw, ale myślałem, że moje przykłady zostały dodane na tyle, by je opublikować).

4

etod, które omija instancje. Oto fragment zźródło:

<code>  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 */
</code>

Możesz zmienić klasę na starą (nie dziedzicz zobiekt) lub możesz dodać metody dyspozytora do klasy (metody, które przekazują odnośniki z powrotem do instancji). Przykład metody dyspozytora instancji znajduje się w przepisie nahttp://code.activestate.com/recipes/578091

AFAIK, gdybym miał zmienić klasę na Old-Style, nie zadziałałoby to w Pythonie 3? James Mills
10

Wyszukiwanie metod specjalnych:

W przypadku klas niestandardowych wywołania niejawne metod specjalnych gwarantują poprawne działanie tylko wtedy, gdy są zdefiniowane na typie obiektu, a nie w słowniku instancji obiektu… Oprócz pomijania atrybutów instancji w celu poprawności, niejawne wyszukiwanie metod specjalnych zwykle omija również__getattribute__() metoda nawet metaklasy obiektu

(Część, którą omówiłem, wyjaśnia uzasadnienie tego, jeśli jesteś zainteresowany tym.)

Python nie dokumentuje dokładnie, kiedy implementacja powinna lub nie powinna szukać metody na typie; wszystko to dokumentuje, że implementacje mogą, ale nie muszą, patrzeć na instancję w poszukiwaniu specjalnych metod, więc nie powinieneś na nich liczyć.

Jak można się domyślić z wyników testu, w implementacji CPython,__repr__ jest jedną z funkcji sprawdzanych na typie.

W 2.x sprawy wyglądają nieco inaczej, głównie z powodu obecności klas klas, ale dopóki tworzysz tylko klasy nowego stylu, możesz myśleć o nich tak samo.

Najczęstszym powodem, dla którego ludzie chcą to robić, jest łączenie różnych instancji obiektu w celu wykonania różnych czynności. Nie możesz tego zrobić za pomocą specjalnych metod, więc… co możesz zrobić? Jest czyste rozwiązanie i hacky rozwiązanie.

Czystym rozwiązaniem jest zaimplementowanie specjalnej metody na klasie, która wywołuje zwykłą metodę na instancji. Następnie możesz małpować łatę, która jest normalną metodą w każdym przypadku. Na przykład:

<code>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)
</code>

Hacky rozwiązanie polega na zbudowaniu nowej podklasy w locie i zmianie klasy obiektu. Podejrzewam, że każdy, kto naprawdę ma sytuację, w której jest to dobry pomysł, będzie wiedział, jak go wdrożyć z tego zdania, a każdy, kto nie wie, jak to zrobić, nie powinien próbować, więc zostawię to.

Myślę, że aby pracować na 2.x, być może będziesz musiał zadzwonić__get__(c, type(c)), ale nie pamiętam na pewno i nie mam działającego interpretera 2.x na tej maszynie… Może lepiej byłoby użyćtypes.MethodType zamiast? abarnert
Czy jest powód, dla którego używaszgetattr(self, '_repr')() zamiast po prostuself._repr()? Nathan
20

tych o nazwie otoczonej__ na instancji, ale tylko w klasie, najwyraźniej w celu poprawy wydajności. Więc nie ma sposobu na zastąpienie__repr__() bezpośrednio na instancji i spraw, by działała. Zamiast tego musisz zrobić coś takiego:

<code>class A(object):
    def __repr__(self):
        return self._repr()
    def _repr(self):
        return object.__repr__(self)
</code>

Teraz możesz nadpisać__repr__() na instancji przez zastąpienie_repr().

Nie chodzi przede wszystkim o poprawę wydajności. Jak mówią doktorzy, „uzasadnieniem tego zachowania jest szereg specjalnych metod, takich jak__hash__() i__repr__() które są implementowane przez wszystkie obiekty, w tym obiekty typu. Jeśli niejawne wyszukiwanie tych metod wykorzysta konwencjonalny proces wyszukiwania, nie powiedzie się, gdy zostanie wywołany na samym obiekcie typu… ”(Jednak uzasadnienie dla__getattribute__ również być pomijalnymjest wydajność.) abarnert
To jestnie o wydajności. Sprawdzane są specjalne metody typu, aby upewnić się, że zarówno instancja, jak i typ mogą obsługiwać operację specjalną. Na przykład,obiekty klasy są dostępne. Oni mają__hash__ metoda. Jeśli klasa implementuje a__hash__ metoda tworzenia instancji hashable, wtedy ta metoda kolidowałaby jeślihash(ClassObject) wyszuka metodę bezpośrednio na klasie (która sama jest instancją metatype). Martijn Pieters
Ponadto „Python nie nazywa specjalnych metod…” nie jest prawdą. Python niegwarancja zadzwonić do nich. CPython ma specyficzną listę przypadków, w których pomija normalne wyszukiwanie, ale ta lista jest specyficzna dla implementacji i nie jest udokumentowana, więc nie powinieneś na niej polegać. Specjalne metody mogą lub nie mogą być sprawdzane na instancji, więc upewnij się, że twój kod działa w dowolny sposób. abarnert
Widziećto. Zasady są nieco złagodzone w klasach starego stylu, ale powinieneś używać klas nowego stylu, aw Pythonie 3 i tak są one jedyne. kindall
„Python nie nazywa specjalnych metod, tych o nazwie otoczonej __ na instancji, ale tylko na klasie, najwyraźniej w celu poprawy wydajności” <- Czy masz na to odniesienie? James Mills

Powiązane pytania