it-swarm-eu.dev

Rozdíly metod třídy v Pythonu: vázané, nevázané a statické

Jaký je rozdíl mezi následujícími metodami třídy?

Je to ten, který je statický a druhý není?

class Test(object):
  def method_one(self):
    print "Called method_one"

  def method_two():
    print "Called method_two"

a_test = Test()
a_test.method_one()
a_test.method_two()
225
Franck Mesirard

V Pythonu existuje rozdíl mezi metodami bound a unbound

V podstatě volání funkce člena (jako method_one), vázaná funkce

a_test.method_one()

je přeložen do

Test.method_one(a_test)

tj. volání na nevázanou metodu. Z tohoto důvodu se volání s verzí method_two nezdaří s TypeError

>>> a_test = Test() 
>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given) 

Chování metody můžete změnit pomocí dekoratéra

class Test(object):
    def method_one(self):
        print "Called method_one"

    @staticmethod
    def method_two():
        print "Called method two"

Dekodér řekne vestavěné výchozí metaclass type (třída třídy, cf. tato otázka ), aby nevytvářel vázané metody pro method_two.

Nyní můžete vyvolat statickou metodu přímo na instanci nebo ve třídě:

>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two
399
Torsten Marek

Metody v Pythonu jsou velmi jednoduchou věcí, jakmile pochopíte základy systému deskriptorů. Představte si následující třídu:

class C(object):
    def foo(self):
        pass

Podívejme se nyní na tuto třídu ve Shell:

>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>

Jak můžete vidět, pokud přistupujete k atributu foo ve třídě, dostanete zpět nevázanou metodu, ale uvnitř úložiště třídy (dict) je funkce. Proč je to? Důvodem je to, že třída vaší třídy implementuje __getattribute__, která řeší deskriptory. Zní to složitě, ale není. C.foo je zhruba ekvivalentní tomuto kódu v tomto zvláštním případě:

>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>

To proto, že funkce mají metodu __get__, která z nich dělá deskriptory. Pokud máte instanci třídy, je téměř stejná, právě ta None je instancí třídy:

>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>

Proč to dělá Python? Protože objekt metody váže první parametr funkce na instanci třídy. Odtud pochází já. Někdy nechcete, aby vaše třída vytvořila funkci metodu, do které vstoupí staticmethod:

 class C(object):
  @staticmethod
  def foo():
   pass

Dekorátor staticmethod zalamuje vaši třídu a implementuje dummy __get__, která vrací zabalenou funkci jako funkci a ne jako metodu:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

Doufám, že to to vysvětluje.

185
Armin Ronacher

Když zavoláte člen třídy, Python automaticky použije odkaz na objekt jako první parametr. Proměnná self ve skutečnosti neznamená nic, je to jen konvence kódování. Pokud byste chtěli, můžete to nazvat gargaloo. To znamená, že volání method_two by zvýšilo TypeError, protože Python se automaticky pokouší předat parametr (odkaz na jeho nadřazený objekt) metodě, která nebyla definována jako parametr.

Aby to fungovalo, můžete jej přidat do definice třídy:

method_two = staticmethod(method_two)

nebo můžete použít @staticmethodfunction decorator .

11
Justin Poliey
>>> class Class(object):
...     def __init__(self):
...         self.i = 0
...     def instance_method(self):
...         self.i += 1
...         print self.i
...     c = 0
...     @classmethod
...     def class_method(cls):
...         cls.c += 1
...         print cls.c
...     @staticmethod
...     def static_method(s):
...         s += 1
...         print s
... 
>>> a = Class()
>>> a.class_method()
1
>>> Class.class_method()    # The class shares this value across instances
2
>>> a.instance_method()
1
>>> Class.instance_method() # The class cannot use an instance method
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead)
>>> Class.instance_method(a)
2
>>> b = 0
>>> a.static_method(b)
1
>>> a.static_method(a.c) # Static method does not have direct access to 
>>>                      # class or instance properties.
3
>>> Class.c        # a.c above was passed by value and not by reference.
2
>>> a.c
2
>>> a.c = 5        # The connection between the instance
>>> Class.c        # and its class is weak as seen here.
2
>>> Class.class_method()
3
>>> a.c
5
11
kzh

method_two nebude fungovat, protože definujete funkci člena, ale neřeknete, co je funkce členem. Pokud provedete poslední řádek, dostanete:

>>> a_test.method_two()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)

Pokud definujete členské funkce pro třídu, první argument musí být vždy „já“.

4
Jon Cage

Přesné vysvětlení od Armina Ronachera výše, rozšiřování jeho odpovědí tak, aby začátečníci jako já dobře rozuměli:

Rozdíl v metodách definovaných ve třídě, zda statická nebo instanční metoda (existuje ještě další metoda typu třídy - zde se o ní nehovořilo, takže se přeskakuje), spočívá v tom, zda jsou nějakým způsobem vázány na instanci třídy nebo ne. Řekněme například, zda metoda obdrží během běhu odkaz na instanci třídy

class C:
    a = [] 
    def foo(self):
        pass

C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C() 
c # this is the class instance

Vlastnost slovníku __dict__ objektu třídy obsahuje odkaz na všechny vlastnosti a metody objektu třídy, a tím i 

>>> C.__dict__['foo']
<function foo at 0x17d05b0>

metoda foo je přístupná jak je uvedeno výše. Důležité je poznamenat, že vše v pythonu je objekt a odkazy ve výše uvedeném slovníku směřují na jiné objekty. Dovolte mi říkat jim objekty třídy Property - nebo jako CPO v rámci mé odpovědi na stručnost.

Pokud je CPO deskriptor, pak pythonový interpret zavolá metodu __get__() CPO pro přístup k hodnotě, kterou obsahuje.

Aby bylo možné určit, zda je CPO deskriptor, python interpretor kontroluje, zda implementuje protokol deskriptoru. Pro implementaci protokolu deskriptorů je implementace 3 metod

def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)

pro např. 

>>> C.__dict__['foo'].__get__(c, C)

kde 

  • self je CPO (může to být instance seznamu, str, funkce atd.) a je dodávána runtime
  • instance je instancí třídy, ve které je tato CPO definována (objekt „c“ výše) a musí být dodána námi dodávaným způsobem
  • owner je třída, kde je tato CPO definována (objekt třídy 'C' výše) a musí být dodána námi. Je to však proto, že ji voláme na CPO. když tomu říkáme na instanci, nemusíme to dodávat, protože runtime může poskytnout instanci nebo její třídu (polymorfismus)
  • value je zamýšlená hodnota CPO a musí být dodána námi

Ne všechny CPO jsou deskriptory. Například 

>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510> 
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'

Je to proto, že třída seznamů neimplementuje protokol deskriptoru.

Tudíž je argument self v c.foo(self) vyžadován, protože jeho signatura metody je vlastně C.__dict__['foo'].__get__(c, C) (jak bylo vysvětleno výše, C není potřeba, protože to může být zjištěno nebo polymorfováno) A to je také důvod, proč dostanete TypeError pokud jste dont předat požadovaný argument instance.

Pokud si všimnete, že metoda je stále odkazována přes objekt třídy C a vazba s instancí třídy je dosažena předáním kontextu ve formě objektu instance do této funkce. 

To je dost děsivé, protože pokud se rozhodnete neudržovat žádný kontext ani žádnou vazbu k instanci, bylo potřeba napsat třídu, která zabalí popisovač CPO a přepíše jeho metodu __get__(), která nevyžaduje žádný kontext. Tato nová třída je to, co nazýváme dekoratérem a je aplikováno prostřednictvím klíčového slova @staticmethod

class C(object):
  @staticmethod
  def foo():
   pass

Nepřítomnost kontextu v novém zabaleném CPO foo nečiní chybu a lze ji ověřit následujícím způsobem:

>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>

Případ statické metody je spíše jmenný prostor a udržovatelnost kódu (vyjmutí z třídy a zpřístupnění v celém modulu atd.). 

Je možné, že je lepší psát spíše statické metody než metody instancí, kdykoli je to možné, pokud není nutné, aby metody používaly (např. Proměnné přístupových instancí, proměnné třídy atd.). Jedním z důvodů je zmírnění odpadků tím, že nechcete nechtěný odkaz na objekty.

3
supi

Volání metody method_two vyvolá výjimku pro nepřijetí vlastního parametru, který modul Python runtime automaticky předá.

Pokud chcete vytvořit statickou metodu ve třídě Python, ozdobte ji staticmethod decorator.

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()
1
MvdD

to je chyba.

první řádek by měl být takový (pozor na velká písmena)

class Test(object):

Kdykoliv zavoláte metodu třídy, dostane se jako první argument (tedy jméno self) a metoda method_two tuto chybu 

>>> a.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)
1
hayalci

Druhý z nich nebude fungovat, protože když ho zavoláte jako python, pokouší se jej zavolat jako instanci a_test jako první argument, ale metoda method_two nepřijímá žádné argumenty, takže to nebude fungovat. Pokud chcete ekvivalent statické metody, můžete použít metodu třídy. Je mnohem méně potřeba metod třídy v Pythonu než statické metody v jazycích jako Java nebo C #. Nejčastěji je nejlepším řešením použití metody v modulu, mimo definici třídy, které fungují efektivněji než metody třídy.

1
Vasil

Přečtěte si, prosím, tento dokument z Guido First Class vše Jasně vysvětlil, jak se rodí nevázané metody.

1
James Sapam

Definice method_two je neplatná. Když zavoláte method_two, dostanete TypeError: method_two() takes 0 positional arguments but 1 was given z tlumočníka. 

Metoda instance je omezená funkce, když ji voláte jako a_test.method_two(). Automaticky přijímá self, které odkazuje na instanci Test, jako svůj první parametr. Prostřednictvím parametru self může metoda instance volně přistupovat k atributům a upravovat je na stejném objektu.

0
Yossarian42