it-swarm.it

Perché hai bisogno esplicitamente dell'argomento "self" in un metodo Python?

Quando si definisce un metodo su una classe in Python, sembra qualcosa del genere:

class MyClass(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

Ma in alcuni altri linguaggi, come C #, si ha un riferimento all'oggetto al quale il metodo è associato con la parola chiave "this" senza dichiararlo come argomento nel prototipo del metodo. 

Si trattava di una decisione di progettazione del linguaggio intenzionale in Python o ci sono dei dettagli di implementazione che richiedono il passaggio del "sé" come argomento?

180
Readonly

Mi piace citare lo Zen di Python di Peters. "L'esplicito è meglio che implicito."

In Java e C++ è possibile dedurre 'this.', tranne quando si hanno nomi variabili che rendono impossibile dedurre. Quindi a volte ne hai bisogno e a volte no.

Python sceglie di rendere esplicite cose del genere piuttosto che basate su una regola. 

Inoltre, poiché nulla è implicito o presupposto, parti dell'attuazione sono esposte. self.__class__, self.__dict__ e altre strutture "interne" sono disponibili in modo ovvio.

88
S.Lott

Serve a minimizzare la differenza tra metodi e funzioni. Consente di generare facilmente metodi in metaclassi o di aggiungere metodi in fase di esecuzione a classi preesistenti.

per esempio.

>>> class C(object):
...     def foo(self):
...         print "Hi!"
...
>>>
>>> def bar(self):
...     print "Bork bork bork!"
...
>>>
>>> c = C()
>>> C.bar = bar
>>> c.bar()
Bork bork bork!
>>> c.foo()
Hi!
>>>

Inoltre, per quanto ne so, semplifica l'implementazione del runtime di python.

55
Ryan

Suggerisco che si dovrebbe leggere Il blog di Guido van Rossum su questo argomento - Perché il sé esplicito deve rimanere .

Quando una definizione di un metodo è decorata, non sappiamo se assegnargli automaticamente un parametro 'self' o no: il decoratore potrebbe trasformare la funzione in un metodo statico (che non ha 'self'), o un metodo di classe (che ha un tipo divertente di sé che si riferisce ad una classe invece che ad un'istanza), oppure potrebbe fare qualcosa di completamente diverso (è banale scrivere un decoratore che implementa '@classmethod' o '@staticmethod' in puro Python). Non c'è modo, senza sapere cosa fa il decoratore, se dotare o meno il metodo che viene definito con un argomento implicito di "sé".

Rifiuto gli hack come quelli speciali "@classmethod" e "@staticmethod".

51
bhadra

Python non ti obbliga a usare "te stesso". Puoi dargli il nome che vuoi. Devi solo ricordare che il primo argomento in un'intestazione di definizione del metodo è un riferimento all'oggetto.

16
Victor Noagbodji

Permette anche di farlo: (in breve, invocare Outer(3).create_inner_class(4)().weird_sum_with_closure_scope(5) restituirà 12, ma lo farà nel modo più pazzo.

class Outer(object):
    def __init__(self, outer_num):
        self.outer_num = outer_num

    def create_inner_class(outer_self, inner_arg):
        class Inner(object):
            inner_arg = inner_arg
            def weird_sum_with_closure_scope(inner_self, num)
                return num + outer_self.outer_num + inner_arg
        return Inner

Naturalmente, questo è più difficile da immaginare in linguaggi come Java e C #. Rendendo esplicito il riferimento personale, sei libero di riferirti a qualsiasi oggetto con tale riferimento personale. Inoltre, un tale modo di giocare con le classi in fase di esecuzione è più difficile da fare nei linguaggi più statici - non è necessariamente buono o cattivo. È solo che il sé esplicito permette a tutta questa follia di esistere.

Inoltre, immagina questo: vorremmo personalizzare il comportamento dei metodi (per la creazione di profili o qualche pazza magia nera). Questo può indurci a pensare: cosa accadrebbe se avessimo una classe Method il cui comportamento potremmo sovrascrivere o controllare?

Bene, eccolo qui

from functools import partial

class MagicMethod(object):
    """Does black magic when called"""
    def __get__(self, obj, obj_type):
        # This binds the <other> class instance to the <innocent_self> parameter
        # of the method MagicMethod.invoke
        return partial(self.invoke, obj)


    def invoke(magic_self, innocent_self, *args, **kwargs):
        # do black magic here
        ...
        print magic_self, innocent_self, args, kwargs

class InnocentClass(object):
    magic_method = MagicMethod()

E ora: InnocentClass().magic_method() si comporterà come previsto. Il metodo sarà associato con il parametro innocent_self a InnocentClass e con magic_self all'istanza di MagicMethod. Strano eh? È come avere 2 parole chiave this1 e this2 in lingue come Java e C #. Magia come questa permette ai framework di fare cose che altrimenti sarebbero molto più prolisse.

Ancora una volta, non voglio commentare l'etica di questa roba. Volevo solo mostrare cose che sarebbero state più difficili da fare senza un'auto referenza esplicita.

6
vlad-ardelean

Penso che la vera ragione oltre a "The Zen of Python" sia che le funzioni sono cittadini di prima classe in Python. 

Che essenzialmente li rende un oggetto. Ora il problema fondamentale è che se le tue funzioni sono anche oggetto allora, nel paradigma orientato agli oggetti, come faresti ad inviare messaggi agli oggetti quando i messaggi stessi sono oggetti? 

Sembra un problema Egg Chicken, per ridurre questo paradosso, l'unico modo possibile è passare un contesto di esecuzione ai metodi o rilevarlo. Ma dal momento che Python può avere funzioni annidate, sarebbe impossibile farlo in quanto il contesto di esecuzione cambierebbe per le funzioni interne. 

Ciò significa che l'unica soluzione possibile è passare "sé" esplicitamente (il contesto dell'esecuzione).

Quindi credo che sia un problema di implementazione lo Zen è venuto molto dopo.

2
pankajdoharey

Penso che abbia a che fare con PEP 227:

I nomi nell'ambito della classe non sono accessibili. I nomi sono risolti nel ambito della funzione di chiusura più interno. Se una definizione di classe si verifica in a catena di ambiti nidificati, il processo di risoluzione ignora la classe definizioni. Questa regola impedisce le interazioni dispari tra la classe attributi e accesso alle variabili locali. Se un'operazione di associazione nome si verifica in una definizione di classe, crea un attributo sul risultante oggetto di classe. Per accedere a questa variabile in un metodo o in una funzione nidificati all'interno di un metodo, deve essere usato un riferimento all'attributo, via self o tramite il nome della classe.

2
daole

Come spiegato in self in Python, Demystified

qualsiasi cosa come obj.meth (args) diventa Class.meth (obj, args). Il processo chiamante è automatico mentre il processo di ricezione non lo è (è esplicito). Questo è il motivo per cui il primo parametro di una funzione in classe deve essere l'oggetto stesso. 

class Point(object):
    def __init__(self,x = 0,y = 0):
        self.x = x
        self.y = y

    def distance(self):
        """Find distance from Origin"""
        return (self.x**2 + self.y**2) ** 0.5

Invocazioni:

>>> p1 = Point(6,8)
>>> p1.distance()
10.0

init () definisce tre parametri ma ne abbiamo appena passati due (6 e 8). Analogamente distance () richiede uno ma gli argomenti zero sono stati passati. 

Perché Python non si lamenta di questo argomento numero mismatch?

In genere, quando chiamiamo un metodo con alcuni argomenti, la funzione di classe corrispondente viene chiamata posizionando l'oggetto del metodo prima del primo argomento. Quindi, qualsiasi cosa come obj.meth (args) diventa Class.meth (obj, args). Il processo chiamante è automatico mentre il processo di ricezione non lo è (è esplicito).

Questo è il motivo per cui il primo parametro di una funzione in classe deve essere l'oggetto stesso. Scrivere questo parametro come auto è semplicemente una convenzione. Non è una parola chiave e non ha alcun significato speciale in Python. Potremmo usare altri nomi (come questo) ma vi consiglio caldamente di non farlo. L'uso di nomi diversi da sé è disapprovato dalla maggior parte degli sviluppatori e degrada la leggibilità del codice ("Contabilità Readability") . 
...
In, il primo esempio self.x è un attributo di istanza mentre x è una variabile locale. Non sono uguali e si trovano in diversi spazi dei nomi.

Il Sé è qui per restare

Molti hanno proposto di creare una parola chiave in Python, come in C++ e Java. Ciò eliminerebbe l'uso ridondante del sé esplicito dall'elenco dei parametri formali nei metodi. Anche se questa idea sembra promettente, non accadrà. Almeno non nel prossimo futuro. Il motivo principale è la retrocompatibilità. Ecco un blog del creatore di Python stesso che spiega perché il sé esplicito deve rimanere.

0
mon