self o non self in Python

Il 23 Settembre scorso Bruce Eckel scrive su Artima un post che lì per lì passa un pò in sordina. Oggetto del post è la presenza di self nella lista di argomenti che va specificata in fase di definizione di un metodo di una classe. Per intenderci:

class Foo(object):
    def method(self): pass

Ricordo ai lettori meno python-addicted che self nel linguaggio non è una parola riservata ma semplicemente il nome che viene dato per convenzione alla variabile che riferisce l’oggetto corrente. Eckel lamenta l’incoerenza che emerge fra definizione e invocazione del metodo di una classe: in fase di definizione (vedi sopra) self va specificato, mentre in fase di invocazione self è implicito, come nel caso:

obj = Foo()
obj.method()

Quindi il secondo comandamento dello Zen of Python, “explicit is better than implicit“, applicato alla definizione del metodo, viene meno in fase di invocazione. Questo fatto, che di per sè potrebbe limitarsi ad urtare il senso estetico di alcuni, diventa proprio fuorviante nel momento in cui il programmatore di turno sbabglia ad invocare il metodo, esempio:

obj = Foo()
obj.method( 123,45 )

In questo caso Python se ne esce con l’errore:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 1 argument (2 given)

Ma come? Io di argomenti ne ho passati uno solo, per l’appunto..! E anche una volta accortosi che il metodo method() non prendeva alcun argomento, leggendo il messaggio il programmatore potrebbe pensare che il metodo vada comunque chiamato così:

obj = Foo()
Foo.method(obj)

Soluzione secondo Eckel: self diventa una parola riservata del linguaggio, ed è implicitamente passata nella lista di argomenti in fase di definizione del metodo; altrove il suo utilizzo rimane esplicito. In aggiunta a questo, il messaggio di errore visto sopra dovrebbe riportare il numero effettivo di parametri necessari, self escluso.

Tutti convinti? Ma nemmeno per sogno…

Domenica mattina, 26 Ottobre, GVR risponde dal suo blog nuovo di fiamma con un post dal discreto piglio. Oggetto del post: perchè self dovrebbe rimanere così com’è.

A supporto della cosa Guido cita nell’ordine:

  • La necessità di mantenere l’equivalenza:
foo.method(arg) == C.method(foo, arg) # dove foo è istanza di C
  • La possibilità di fare il poking di metodi all’interno di classi a runtime:# Define an empty class:
# definiamo una classe
class C:
   pass
# definiamo una funzione globale, notare l'uso di myself al posto di self
def meth(myself, arg):
   myself.val = arg
   return myself.val
# Facciamo il poking del metodo all'interno della classe
C.meth = meth
  • L’esistenza dei decoratori

E con quest’ultimo punto è scacco matto, effettivamente la soluzione proposta da Eckel si sposa difficilmente con l’esistenza dei decoratori, a meno di grossi stravolgimenti. Con il metodo Eckel, infatti, se decoriamo il metodo di una classe, non sappiamo a priori se aggiungere self oppure no. Se prendiamo due decoratori “famosi”, @classmethod e @staticmethod si capisce meglio: il primo decora metodi che al posto di self (riferimento all’istanza) ricevono un riferimento alla classe, e il secondo decora metodi che non prendono nè l’uno nè l’altro.

Morale della favola, Guido ammette che lo status quo è certamente migliorabile, ma dubita fortemente che l’idea di Eckel diventerà un PEP di successo.