Come evitare il "sé" esplicito in Python?


131

Ho imparato Python seguendo alcuni tutorial di Pygame .

Qui ho trovato ampio uso della parola chiave self , e proveniente da un background principalmente Java, trovo che continuo a dimenticare di digitare self . Ad esempio, invece di self.rect.centerxdigitare rect.centerx, perché, per me, rect è già una variabile membro della classe.

Il parallelo Java che mi viene in mente per questa situazione è di dover aggiungere tutti i riferimenti alle variabili membro con questo .

Sono bloccato con il prefisso di tutte le variabili membro con self o c'è un modo per dichiararle che mi consentirebbe di evitare di doverlo fare?

Anche se ciò che sto suggerendo non è pitonico , mi piacerebbe comunque sapere se è possibile.

Ho dato un'occhiata a queste relative domande SO, ma non rispondono esattamente a ciò che sto cercando:


5
Provengo da uno sfondo Java e lo trovo naturale, ma aggiungo esplicitamente "questo" a ogni chiamata per chiarire che mi riferisco a una variabile di istanza.
Uri,

4
Conoscete la convenzione di un m_prefisso per tutti i nomi dei membri osservati da alcuni programmatori C ++ / Java? L'uso di self.aiuta la leggibilità in modo simile. Inoltre, dovresti leggere dirtsimple.org/2004/12/python-is-not-java.html .
Beni Cherniavsky-Paskin,

2
Anche se di solito m_viene utilizzato solo per i dati non pubblici non statici (almeno in C ++).

@Beni ottimo articolo collegato, e sì, in effetti, seguo effettivamente la convenzione di utilizzare mVariableName, per le variabili membro, durante la codifica in Java. Penso che il commento di Anurag lo riassuma abbastanza bene, per quello che un dev java dovrebbe fare quando impara Python.
Bguiz,

11
Quindi, come mai tutti dicono a OP perché usare se stessi sia buono / necessario / ecc. ma nessuno dice se può essere evitato in qualche modo? Anche se per qualche tipo di trucco sporco?
einpoklum,

Risposte:


99

Python richiede di specificare se stesso. Il risultato è che non c'è mai confusione su cosa sia un membro e cosa no, anche senza la definizione di classe completa visibile. Questo porta a proprietà utili, come: non è possibile aggiungere membri che ombreggiano accidentalmente i non membri e quindi rompono il codice.

Un esempio estremo: puoi scrivere una classe senza alcuna conoscenza di quali classi base potrebbe avere e sapere sempre se stai accedendo a un membro o meno:

class A(some_function()):
  def f(self):
    self.member = 42
    self.method()

Questo è il codice completo ! (some_function restituisce il tipo usato come base.)

Un altro, in cui i metodi di una classe sono composti dinamicamente:

class B(object):
  pass

print B()
# <__main__.B object at 0xb7e4082c>

def B_init(self):
  self.answer = 42
def B_str(self):
  return "<The answer is %s.>" % self.answer
# notice these functions require no knowledge of the actual class
# how hard are they to read and realize that "members" are used?

B.__init__ = B_init
B.__str__ = B_str

print B()
# <The answer is 42.>

Ricorda, entrambi questi esempi sono estremi e non li vedrai tutti i giorni, né sto suggerendo che dovresti spesso scrivere codice come questo, ma mostrano chiaramente aspetti di sé esplicitamente richiesti.


4
Grazie. Questa risposta colpisce il punto perché spiega anche i vantaggi dell'utilizzo self.
Bguiz,

2
@Roger Pate: interrompi la modifica della mia domanda per rimuovere Python da essa. Penso che appartenga lì. (e grazie per la risposta!)
bguiz

3
@bguiz: è una convenzione SO non duplicare i tag nel titolo. Tuttavia, quando l'ho modificato 2 giorni fa, non ho visto che hai ripristinato il titolo 7 mesi fa.

1
Sarebbe bello se il sé potesse essere ridotto a un singolo personaggio.
dwjohnston,

5
Questa è in realtà una ragione piuttosto sciocca. Python potrebbe non accettare l'ombreggiatura e richiedere che tu lo dichiari in modo specifico, cioè "benedici" il tuo campo d'ombra con una sorta di __shadow__ myFieldName. Ciò impedirebbe anche l'ombreggiamento accidentale, no?
einpoklum,

39

Le risposte precedenti sono sostanzialmente varianti di "non puoi" o "non dovresti". Mentre sono d'accordo con quest'ultimo sentimento, la domanda è tecnicamente ancora senza risposta.

Inoltre, ci sono motivi legittimi per cui qualcuno potrebbe voler fare qualcosa sulla falsariga di ciò che la domanda reale sta ponendo. Una cosa che incontro a volte sono lunghe equazioni matematiche in cui l'uso di nomi lunghi rende irriconoscibile l'equazione. Ecco un paio di modi per farlo in un esempio predefinito:

import numpy as np
class MyFunkyGaussian() :
    def __init__(self, A, x0, w, s, y0) :
        self.A = float(A)
        self.x0 = x0
        self.w = w
        self.y0 = y0
        self.s = s

    # The correct way, but subjectively less readable to some (like me) 
    def calc1(self, x) :
        return (self.A/(self.w*np.sqrt(np.pi))/(1+self.s*self.w**2/2)
                * np.exp( -(x-self.x0)**2/self.w**2)
                * (1+self.s*(x-self.x0)**2) + self.y0 )

    # The correct way if you really don't want to use 'self' in the calculations
    def calc2(self, x) :
        # Explicity copy variables
        A, x0, w, y0, s = self.A, self.x0, self.w, self.y0, self.s
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

    # Probably a bad idea...
    def calc3(self, x) :
        # Automatically copy every class vairable
        for k in self.__dict__ : exec(k+'= self.'+k)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

g = MyFunkyGaussian(2.0, 1.5, 3.0, 5.0, 0.0)
print(g.calc1(0.5))
print(g.calc2(0.5))
print(g.calc3(0.5))

Il terzo esempio - vale a dire l'utilizzo for k in self.__dict__ : exec(k+'= self.'+k)è fondamentalmente ciò che la domanda sta effettivamente ponendo, ma vorrei essere chiaro che non penso che sia generalmente una buona idea.

Per ulteriori informazioni e modi per scorrere le variabili di classe o anche le funzioni, vedere le risposte e la discussione a questa domanda . Per una discussione di altri modi per nominare dinamicamente le variabili e perché di solito questa non è una buona idea, vedi questo post sul blog .

AGGIORNAMENTO: sembra che non ci sia modo di aggiornare o cambiare dinamicamente i locali in una funzione in Python3, quindi calc3 e varianti simili non sono più possibili. L'unica soluzione compatibile con python3 che mi viene in mente ora è usare globals:

def calc4(self, x) :
        # Automatically copy every class variable in globals
        globals().update(self.__dict__)
        sqrt, exp, pi = np.sqrt, np.exp, np.pi
        return ( A/( w*sqrt(pi) )/(1+s*w**2/2)
                * exp( -(x-x0)**2/w**2 )
                * (1+s*(x-x0)**2) + y0 )

Che, di nuovo, sarebbe una pratica terribile in generale.


4
Brillante! Questa è la risposta più (solo?) Corretta. +1 Fornisci anche in modo univoco un motivo pratico per farlo. + 1i
David Lotts,

Dopo aver creato una classe e spostato il codice al suo interno: ora tutti i metodi e le variabili non vengono più riconosciuti (nessun ...) Mi viene in mente l'ennesimo motivo per cui Python e non vado d'accordo. Grazie per questo come idea. Non risolve il problema / mal di testa / illeggibilità in generale, ma fornisce una soluzione alternativa modesta.
javadba,

Perché non semplicemente aggiornare localsinvece di utilizzare exec?
Nathan

Ho provato locals().update(self.__dict__)in Python 2 e 3, ma non ha funzionato. In python3 anche il trucco 'exec' non è più un'opzione. D'altra parte, globals().update(self.__dict__)funziona, ma sarebbe una pratica terribile in generale.
argentum2f

26

In realtà selfnon è una parola chiave, è solo il nome convenzionalmente assegnato al primo parametro dei metodi di istanza in Python. E quel primo parametro non può essere ignorato, poiché è l'unico meccanismo che un metodo ha di sapere su quale istanza della tua classe viene chiamata.


1
Questa risposta, in particolare la seconda frase, è molto più utile della risposta accettata per me perché posso sapere che il "sé esplicito" è solo uno dei limiti di Python e non evitabile
QuestionDriven

21

Puoi usare qualsiasi nome tu voglia, per esempio

class test(object):
    def function(this, variable):
        this.variable = variable

o anche

class test(object):
    def function(s, variable):
        s.variable = variable

ma sei bloccato con l'utilizzo di un nome per l'ambito.

Non raccomando di usare qualcosa di diverso da sé a meno che tu non abbia una ragione convincente, poiché lo renderebbe alieno per i pitonisti esperti.


26
Puoi farlo, ma non farlo ! Non vi è alcun motivo per rendere il codice più strano di quanto debba essere. Sì, puoi chiamarlo qualsiasi cosa, ma la convenzione è chiamarla selfe dovresti seguire la convenzione. Semplifica la comprensione del codice per qualsiasi programmatore esperto in Python che lo osserva. (Questo include te, tra sei mesi, cercando di capire cosa fa il tuo vecchio programma!)
steveha,

3
Ancora più alieno:def function(_, variable): _.variable = variable
Bob Stein,

1
@ BobStein-VisiBone ancora più alieno:def funcion(*args): args[0].variable = args[1]
Aemyl,

4
@steveha non lo sta raccomandando, questa informazione è molto utile per le persone come me che non sapevano che puoi usare parole chiave diverse da sé e chiedersi perché viene passato un oggetto della propria classe.
Sven van den Boogaart,

> "weirder". Python è strano - specialmente le sue strutture di classe e questo uso di è un canarino per supportare l'anti leggibilità. So che arriveranno molti difensori, ma ciò non cambia la veridicità.
javadba,

9

sì, devi sempre specificare self, perché esplicito è meglio che implicito, secondo la filosofia di Python.

Scoprirai anche che il modo in cui programmi in Python è molto diverso dal modo in cui programmi in Java, quindi l'uso di selftende a diminuire perché non proietti tutto all'interno dell'oggetto. Piuttosto, fai un uso maggiore della funzione a livello di modulo, che può essere testata meglio.

a proposito. All'inizio l'ho odiato, ora odio il contrario. lo stesso per il controllo del flusso indentato.


2
"Sfruttate maggiormente la funzione a livello di modulo, che può essere testata meglio" è dubbia e dovrei essere fortemente in disaccordo. È vero che non sei obbligato a rendere tutto un metodo (statico o no) di una classe, indipendentemente dal fatto che sia "logicamente a livello di modulo", ma che non ha nulla a che fare con se stesso e non ha alcun impatto significativo su test in un modo o nell'altro (per me).

Nasce dalla mia esperienza. Non lo seguo come un mantra ogni volta, ma rende le cose più facili da testare se metti qualcosa che non richiede l'accesso alla variabile membro come metodo separato e indipendente. sì, separi la logica dai dati, che sì, è contro OOP, ma sono insieme, proprio a livello di modulo. Non do l'uno o l'altro il marchio "migliore", è solo una questione di gusti. A volte mi ritrovo a specificare metodi di classe che non hanno nulla a che fare con la classe stessa, poiché non si toccano selfin alcun modo. Allora, qual è il punto di averli in classe?
Stefano Borini,

Non sono d'accordo con entrambe le parti (sembra che io usi "non metodi" non più spesso che in altre lingue), ma "che può essere testato meglio" implica che un modo è superiore all'altro (è proprio così leggerlo? non sembra probabile), mentre non trovo supporto per quello nella mia esperienza. Nota che non sto dicendo che dovresti sempre usare l'uno o l'altro, sto solo dicendo che metodi e non metodi sono ugualmente in grado di essere testati.

5
sì, certo che puoi, ma l'approccio è diverso. Un oggetto ha uno stato, un metodo a livello di modulo no. Se scopri che un test ha esito negativo durante il test di un metodo a livello di classe, due cose avrebbero potuto essere sbagliate: 1) lo stato dell'oggetto al momento della chiamata 2) il metodo stesso. Se si dispone di un metodo a livello di modulo stateless, può verificarsi solo il caso 2. Hai spostato il setup dall'oggetto (dove è la scatola nera per quanto riguarda il test, dato che è governato dalla logica eventualmente complessa all'interno dell'oggetto) al test suite. Stai riducendo la complessità e mantenendo un controllo più stretto sull'installazione.
Stefano Borini,

1
"Se si dispone di un metodo a livello di modulo stateless", che dire di un metodo a livello di modulo stateful? Tutto quello che mi stai dicendo è che le funzioni senza stato sono più facili da testare rispetto a quelle con stato, e sono d'accordo con quello, ma non ha nulla a che fare con metodi vs non metodi. Visualizza il parametro del parametro esattamente come questo: solo un altro parametro per la funzione.

4

Il "sé" è il segnaposto convenzionale dell'istanza dell'oggetto corrente di una classe. Viene utilizzato quando si desidera fare riferimento alla proprietà, al campo o al metodo dell'oggetto all'interno di una classe come se si riferisse a "se stesso". Ma per rendere più breve qualcuno nel regno della programmazione Python ha iniziato a usare "self", altri regni usano "this" ma lo rendono come una parola chiave che non può essere sostituita. Ho piuttosto usato "suo" per aumentare la leggibilità del codice. È una delle cose positive di Python: hai la libertà di scegliere il tuo segnaposto per l'istanza dell'oggetto diverso da "sé". Esempio per sé:

class UserAccount():    
    def __init__(self, user_type, username, password):
        self.user_type = user_type
        self.username = username            
        self.password = encrypt(password)        

    def get_password(self):
        return decrypt(self.password)

    def set_password(self, password):
        self.password = encrypt(password)

Ora sostituiamo 'self' con 'its':

class UserAccount():    
    def __init__(its, user_type, username, password):
        its.user_type = user_type
        its.username = username            
        its.password = encrypt(password)        

    def get_password(its):
        return decrypt(its.password)

    def set_password(its, password):
        its.password = encrypt(password)

quale è più leggibile ora?


perché non solo s(o qualche altra lettera singola) invece diits
javadba,

'its' ha un significato e 's' no.
LEMUEL ADANE,

sha lo stesso significato: un alias all'istanza di classe. dovrei cercare cosa itssignifichi nel contesto lo stesso
javadba

Entrambi non sono leggibili per me. È ancora illogico ottenere "te stesso" come parametro esterno, non ha alcun senso.
Cesar,

3

self fa parte della sintassi di Python per accedere ai membri degli oggetti, quindi temo che tu sia bloccato con esso


2
self è un modo per dire al modificatore di accesso senza realmente usarne uno. +1
Perpetualcoder

1

In realtà puoi usare la ricetta "Sé implicito" della presentazione di Armin Ronacher "5 anni di cattive idee" (google it).

È una ricetta molto intelligente, come quasi tutto di Armin Ronacher, ma non credo che questa idea sia molto interessante. Penso che preferirei esplicitare questo in C # / Java.

Aggiornare. Link a "ricetta per idee sbagliate": https://speakerdeck.com/mitsuhiko/5-years-of-bad-ideas?slide=58


È questo il link a cui ti riferisci a cui ti riferivi? In tal caso, includi nella tua risposta
reubenjohn,

No, la "cattiva idea" di Armin sembra più divertente per i miei gusti. Ho incluso il link.
Alex Yu,

Prima di fare clic sul collegamento della ricetta, si noti che ciò lo rende implicito nell'elenco dei parametri def method(<del> self </del> ), ma self.variableè ancora necessario con questo trucco intelligente.
David Lotts,

0

Sì, il sé è noioso. Ma è meglio?

class Test:

    def __init__(_):
        _.test = 'test'

    def run(_):
        print _.test

10
_ha un significato speciale nella shell Python, dove contiene l'ultimo valore restituito. È sicuro usarlo in questo modo, ma potenzialmente confuso; Lo eviterei.
Cairnarvon,

No, non è meglio, ma perché non una singola lettera, ad esempio so m(per imitare il C ++)
javadba

0

Da: Self Hell - Funzioni più stateful.

... un approccio ibrido funziona meglio. Tutti i metodi di classe che eseguono effettivamente il calcolo dovrebbero essere spostati in chiusure e le estensioni per ripulire la sintassi dovrebbero essere mantenute nelle classi. Riponi le chiusure in classi, trattando la classe in modo simile a uno spazio dei nomi. Le chiusure sono essenzialmente funzioni statiche e quindi non richiedono sé *, anche nella classe ...


Le chiusure vanno bene per i piccoli casi d'uso, ma l'uso prolungato di essi aumenterà il sovraccarico di memoria di un programma piuttosto pesantemente (poiché stai usando OO basato su prototipo anziché OO basato su classe - quindi ogni oggetto richiede il proprio set di funzioni piuttosto mantenendo un insieme comune di funzioni in una classe). Inoltre, ti impedirà di essere in grado di usare metodi magici / di sicurezza (ad es. __str__E simili) poiché questi sono invocati in modo diverso dai metodi normali.
Dune,

0

Penso che sarebbe più facile e più leggibile se ci fosse una dichiarazione "membro" così come c'è "globale" in modo da poter dire all'interprete quali sono gli oggetti membri della classe.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.