2016-06-14 12 views
8

pisałem kod, który znajduje „metody niezwiązanych” danej klasy przy użyciu introspekcji i był zaskoczony, aby zobaczyć dwa różne rodzaje deskryptorów dla wbudowanych typów:Różnica między deskryptorami "opakowania" i "metody"?

>>> type(list.append), list.append 
(<class 'method_descriptor'>, <method 'append' of 'list' objects>) 
>>> type(list.__add__), list.__add__ 
(<class 'wrapper_descriptor'>, <slot wrapper '__add__' of 'list' objects>) 

Searching the docs okazało się bardzo ograniczony, ale ciekawe wyniki:

  1. A note in the inspect module że inspect.getattr_static nie rozwiązuje deskryptorów i zawiera kod, który może być użyty do ich rozwiązania.
  2. an optimization made in python 2.4 twierdząc, że method_descriptor jest bardziej wydajny niż wrapper_descriptor ale nie wyjaśniając, czym są:

    Metody list.__getitem__(), dict.__getitem__() i dict.__contains__() są teraz realizowane jako method_descriptor obiekty zamiast wrapper_descriptor obiektów. Ta forma dostępu podwaja ich wydajność i czyni je bardziej odpowiednimi do użycia jako argumenty dla funkcjonałów: map(mydict.__getitem__, keylist).

Różnica w wydajności dość intryguje mnie, wyraźnie istnieje różnica więc poszedłem szukasz dodatkowych informacji.

Żadna z tych typów są w module types:

>>> import types 
>>> type(list.append) in vars(types).values() 
False 
>>> type(list.__add__) in vars(types).values() 
False 

wykorzystaniem help nie dostarcza żadnej użytecznej informacji:

>>> help(type(list.append)) 
Help on class method_descriptor in module builtins: 

class method_descriptor(object) 
| Methods defined here: 
| 
    <generic descriptions for> 
     __call__, __get__, __getattribute__, __reduce__, and __repr__ 
| 
| ---------------------------------------------------------------------- 
| Data descriptors defined here: 
| 
| __objclass__ 
| 
| __text_signature__ 

>>> help(type(list.__add__)) 
Help on class wrapper_descriptor in module builtins: 

class wrapper_descriptor(object) 
| Methods defined here: 
| 
    <generic descriptions for> 
     __call__, __get__, __getattribute__, __reduce__, and __repr__ 
| 
| ---------------------------------------------------------------------- 
| Data descriptors defined here: 
| 
| __objclass__ 
| 
| __text_signature__ 

Searching the internet tylko wymyślił wyników „co jest deskryptor "lub niejasne odniesienia do konkretnych zaangażowanych typów.

Więc moje pytanie brzmi:

Jaka jest faktyczna różnica pomiędzy <class 'method_descriptor'> i <class 'wrapper_descriptor'>?

+1

Nie ufaj "inspekcji" dotyczącej deskryptorów. Zobacz https://bugs.python.org/issue26103 (również istotna: http://stackoverflow.com/q/3798835/541136) –

+0

@AaronHall Pomyślałem, że w tym jeden, ale nie drugi był prawdopodobnie przeoczeniem, ale nie był pewien , Powrócę do wersji, która nie wskazywała na niespójność w sprawdzanych dokumentach. –

Odpowiedz

1

To szczegół implementacji. Na poziomie C wbudowany typ, taki jak list, definiuje metody takie jak append przez nazwę poprzez tablicę struktur PyMethodDef, podczas gdy specjalne metody, takie jak __add__, są zdefiniowane bardziej pośrednio.

__add__ odpowiada wskaźnik funkcji w jednej z dwóch szczelin sq_concat w rodzaju na tp_as_sequence lub nb_add w typowania tp_as_number. Jeśli typ definiuje jedno z tych gniazd, Python generuje wrapper_descriptor zawijając to gniazdo dla metody __add__ interfejsu API poziomu Python.

Owijanie niezbędne dla typu szczeliny i konstrukcji PyMethodDef jest nieco inne; na przykład dwie szczeliny mogą odpowiadać jednej metodzie lub jedna szczelina może odpowiadać sześciu metodom.Sloty również nie noszą przy sobie nazw swoich metod, podczas gdy nazwa metody jest jednym z pól w PyMethodDef. Ponieważ w obu przypadkach potrzebny jest inny kod, Python używa różnych typów opakowania, aby je zawinąć.

Jeśli chcesz zobaczyć kod, zarówno method_descriptor i wrapper_descriptor są realizowane w Objects/descrobject.c, z struct typedefs w Include/descrobject.h. Możesz zobaczyć kod inicjujący owijki w Objects/typeobject.c, gdzie PyType_Ready deleguje do add_operators dla wrapper_descriptor s oraz add_methods dla method_descriptor s.

+0

Większość z nich brzmi poprawnie, ale co masz na myśli mówiąc, że "automaty nie noszą przy sobie nazw swoich metod"? Nadal mogę zrobić 'list .__ dodaj __.__ name__' i wróć' '__add __' ' –

+0

@ TadhgMcDonald-Jensen: C-level' sq_concat' ma tylko wskaźnik funkcji. Owijarka szczeliny musi zawierać nazwę "__add__". – user2357112

+0

Czy możesz dodać linki do kodu źródłowego, który obsługuje te informacje? Różnica, która jest szczegółem implementacji, jest zdecydowanie poprawną odpowiedzią, ponieważ Jython nie ma 'wrapper_descriptor', ale wciąż nie mam żadnych referencji, które opisująby działanie każdej z tych klas. –

0

Wygląda na to, że method_descriptor i wrapper_descriptor to rodzaj kalendarzy dostępnych w CPython. Różnica między nimi wydaje się być prosta method_descriptor najwyraźniej wykorzystywane do metod wbudowanych w (realizowanego w C) obiektów:

set.__dict__['union'].__class__ 
<class 'wrapper_descriptor'> 

wrapper_descriptor jest wykorzystywany na przykład operatorów wbudowanych typów:

int.__dict__['__add__'].__class__. 
<class 'method-wrapper'> 

This is the place gdzie znalazłem tę informację.

+0

Rozumiem, co to jest 'method_wrapper', jest zbudowany w równaniu z' ', który jest używany dla metod związanych, Pytam o różnicę między' method_descriptor' i 'wrapper_descriptor'. –

Powiązane problemy