Python Language Metodi legati, non associati e statici


Esempio

L'idea dei metodi bound e nonbound è stata rimossa in Python 3 . In Python 3 quando dichiari un metodo all'interno di una classe, stai usando una parola chiave def , creando così un oggetto funzione. Questa è una funzione regolare e la classe circostante funziona come il suo spazio dei nomi. Nell'esempio seguente dichiariamo il metodo f all'interno della classe A , e diventa una funzione Af :

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <function A.f at ...>  (in Python 3.x)

In Python 2 il comportamento era diverso: gli oggetti funzione all'interno della classe venivano implicitamente sostituiti con oggetti di tipo instancemethod , che venivano chiamati metodi non associati perché non erano associati a nessuna particolare istanza di classe. Era possibile accedere alla funzione sottostante usando la proprietà .__func__ .

Python 2.x 2.3
A.f
# <unbound method A.f>   (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>

Questi ultimi comportamenti sono confermati dall'ispezione: i metodi sono riconosciuti come funzioni in Python 3, mentre la distinzione è confermata in Python 2.

Python 3.x 3.0
import inspect

inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True

In entrambe le versioni della funzione / metodo Python, Af può essere chiamato direttamente, a condizione di passare un'istanza di classe A come primo argomento.

A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
#                      A instance as first argument (got int instance instead) 
# Python 3: 14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

Ora supponiamo a è un'istanza della classe A , che cosa è af allora? Beh, intuitivamente questo dovrebbe essere lo stesso metodo f di classe A , solo che dovrebbe in qualche modo "sapere" che è stato applicato all'oggetto a - in Python questo si chiama il metodo legato a a .

I dettagli essenziali sono i seguenti: la scrittura af invoca la magia __getattribute__ metodo a , che controlla innanzitutto se a ha un attributo denominato f (che non), quindi controlla la classe A se contiene un metodo con un tale nome (lo fa), e crea un nuovo oggetto m di tipo method che ha il riferimento al Af originale in m.__func__ , e un riferimento all'oggetto a in m.__self__ . Quando questo oggetto viene chiamato come funzione, fa semplicemente quanto segue: m(...) => m.__func__(m.__self__, ...) . Quindi questo oggetto è chiamato metodo vincolato perché quando viene invocato esso sa di fornire l'oggetto a cui era associato come primo argomento. (Queste cose funzionano allo stesso modo in Python 2 e 3).

a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4

# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f  # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f  # True

Infine, Python ha metodi di classe e metodi statici - tipi speciali di metodi. I metodi di classe funzionano allo stesso modo dei metodi regolari, tranne che quando invocati su un oggetto si collegano alla classe dell'oggetto anziché all'oggetto. Quindi m.__self__ = type(a) . Quando chiamate tale metodo associato, passa la classe di a come primo argomento. I metodi statici sono ancora più semplici: non legano nulla e restituiscono semplicemente la funzione sottostante senza alcuna trasformazione.

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        return cls.multiplier * x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world

Si noti che i metodi di classe sono associati alla classe anche quando si accede all'istanza:

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20

Vale la pena notare che al livello più basso, le funzioni, i metodi, i metodi statici, ecc. Sono in realtà dei descrittori che invocano __get__ , __set __ e facoltativamente __del__ metodi speciali. Per maggiori dettagli su classmethods e staticmethods: