2011-01-09 10 views
12

Python documentation about the is operator mówi:Dlaczego metoda nie jest identyczna z samą sobą?

operatorów is i is not badanie tożsamości obiektu: x is y jest prawdziwe wtedy i tylko wtedy, gdy x i y są tego samego obiektu. x is not y daje wartość odwrotną .

Spróbujmy że:

>>> def m(): 
... pass 
... 
>>> m is m 
True 

Python documentation also says:

związku z automatycznym śmieci zbierania, bezpłatnych wykazów oraz dynamiczny charakter deskryptorów, można zauważyć, pozornie nietypowe zachowanie w niektórych zastosowaniach operatora operatora is, na przykład z udziałem wyładowania między metodami instancji, lub stałe. Sprawdź ich dokumentację , aby uzyskać więcej informacji.

>>> class C: 
... def m(): 
...  pass 
... 
>>> C.m is C.m 
False 

Szukałam więcej wyjaśnień, ale nie był w stanie znaleźć żadnego.

Dlaczego jest C.m is C.m fałsz?

Używam Python 2.x. Jak zauważono w poniższych odpowiedziach, w Pythonie 3.x C.m is C.m jest prawdziwe.

Odpowiedz

17

Gdy poprosisz o atrybut instancji, która jest funkcją, otrzymasz związany metodę: obiekt wywoływalny, który owija funkcję zdefiniowaną w klasie i przekazuje instancję jako pierwszy argument. W Pythonie 2.x, jeśli poprosisz o atrybut klasy, która jest funkcją masz podobny obiekt proxy zwany niezwiązanego metodę:

>>> class A: m = lambda: None 
... 
>>> A.m 
<unbound method A.<lambda>> 

Ten szczególny obiekt jest tworzony, gdy o to poprosi, i nie pozornie zbuforowana w dowolnym miejscu. Oznacza to, że gdy zrobisz

>>> A.m is A.m 
False 

tworzysz dwie odrębne niezwiązanych obiektów metoda i testowanie ich tożsamości.

Zauważ, że

>>> x = A.m 
>>> x is x 
True 

i

>>> A.m.im_func is A.m.im_func 
True 

działać prawidłowo. (im_func jest oryginalną funkcją, która jest owijanie niezwiązany metoda obiektu).

w Pythonie 3.x, nawiasem mówiąc, C.m is C.m jest prawda, ponieważ (nieco bezsensowne) Metoda niezwiązana obiekty proxy zostały usunięte całkowicie i po prostu dostać oryginał funkcja, którą zdefiniowałeś.


Jest to tylko jeden z przykładów bardzo dynamiczny charakter odnośnika atrybut w Pythonie: jeśli poprosisz o atrybut obiektu, możliwe jest uruchomienie dowolnego Python, aby obliczyć wartość tego atrybutu. Oto kolejny przykład, gdzie test nie powiedzie się, w jakiej jest znacznie jaśniejsze dlaczego:

>>> class ChangingAttribute(object): 
...  @property 
...  def n(self): 
...    self._n += 1 
...    return self._n 
... 
...  def __init__(self): 
...    self._n = 0 
... 
>>> foo = ChangingAttribute() 
>>> foo.n 
1 
>>> foo.n 
2 
>>> foo.n 
3 
>>> foo.n is foo.n 
False 
>>> foo.n 
6 
+0

Aby uzyskać kompletność, [Deskryptory] (http://docs.python.org/reference/datamodel.html#invoking- descriptors) wykonują to dynamiczne wyszukiwanie atrybutów. To właśnie utrudnia dostęp do atrybutów również nadklasom. –

+0

Tak, chociaż możesz również napisać przykład używając '__getattr__'. – katrielalex

4

Ponieważ Cm() nie jest statyczna metoda klasy C:

Spróbuj tak:

class C: 
    @staticmethod 
    def m(): 
     pass 

print C.m is C.m 
# True 

c = C() 
print c.m is C.m 
# True 

Ponieważ metody statyczne są jak zmienne klasy, chcemy dla nich tylko jednego odniesienia, więc jeśli zmienimy ich powiązaną wartość, ta zmiana powinna być automatyczna we wszystkich klasach i egzemplarzach tej klasy.

Z drugiej strony, w przykładzie, C.m nie jest to metoda statyczna więc Python czyni założenie, że powinien on być traktowany jak metody non-statycznego, więc kiedy tylko zadzwonić C.m, zwróci nową instancję:

class C: 
    def m(): 
     pass 

a = C.m 
b = C.m 

print id(a), id(b) 
# 43811616, 43355984 
print a is b 
# False 

Uwaga: metody statyczne nie są metodami klasowymi!

6

Zakładam, że używasz Pythona 2? W języku Python 3, C.m is C.m (ale C().m is C().m wciąż jest fałszywe). Jeśli wpiszesz tylko C.m w REPL, założę się, że widzisz coś takiego jak <UnboundMethod... >. Opakowanie UnboundMethod robi bardzo mało, z wyjątkiem sprawdzania isinstance(self, cls). (Wydaje się, że nie ma sensu tworzyć takiego wrappera? Jest tak, że został upuszczony w Pythonie 3 - C.m to tylko funkcja). Nowa instancja opakowania jest tworzona na żądanie zawsze, gdy metoda jest dostępna - C.m tworzy jeden, inny C.m tworzy inny. Ponieważ są to różne instancje, C.m is not C.m.

Ściśle związane są metody związane, które umożliwiają wykonywanie f = obj.method; f(*args), ale także powodują instance.method is not instance.method. Po wywołaniu wszystkie funkcje zdefiniowane w klasie (czytaj: wszystkie metody, z wyjątkiem oczywiście monkeypatched) stają się właściwościami instancji. Po uzyskaniu do nich dostępu otrzymuje się nową instancję opakowania (metodę związaną) wokół funkcji zwykłej. To opakowanie zapamiętuje instancję (self) i po wywołaniu z (arg1, arg2, ..., argN) po prostu przekazuje je do funkcji - z self dodanym jako pierwszy argument. Zwykle tego nie zauważasz, ponieważ od razu wywołujesz tę metodę - ale to pozwala na bezcelowe przekazywanie self bez uciekania się do oszustw na poziomie języka.

Aby uzyskać więcej informacji i, proszę, historię, zobacz artykuł the history of Python.

Powiązane problemy