Perché alcune funzioni hanno caratteri di sottolineatura “__” prima e dopo il nome della funzione?


425

Questa "sottolineatura" sembra verificarsi molto, e mi chiedevo se fosse un requisito nel linguaggio Python, o semplicemente una questione di convenzioni?

Inoltre, qualcuno potrebbe nominare e spiegare quali funzioni tendono ad avere i caratteri di sottolineatura e perché ( __init__, ad esempio)?


8
@AustinHenley: non per i trattini bassi prima e dopo il nome. Stai pensando di sottolineare solo prima del nome.



@MackM Nota che questa domanda fa domande sui caratteri di sottolineatura prima e dopo il nome, e il target duplicato che hai proposto fa domande sui caratteri di sottolineatura solo prima del nome. Tuttavia, ammetto che alcune delle risposte qui riguardano anche questo caso.
Georgy,

Risposte:


527

Da Python PEP 8 - Guida allo stile per Python Code :

Descrittivo: stili di denominazione

Sono riconosciuti i seguenti moduli speciali che utilizzano trattini bassi iniziali o finali (generalmente possono essere combinati con qualsiasi convenzione di caso):

  • _single_leading_underscore: indicatore di "uso interno" debole. Ad esempio from M import *, non importa oggetti il ​​cui nome inizia con un trattino basso.

  • single_trailing_underscore_: utilizzato per convenzione per evitare conflitti con la parola chiave Python, ad es

    Tkinter.Toplevel(master, class_='ClassName')

  • __double_leading_underscore: durante la denominazione di un attributo di classe, invoca la modifica del nome (all'interno della classe FooBar, __boodiventa _FooBar__boo; vedi sotto).

  • __double_leading_and_trailing_underscore__: oggetti "magici" o attributi che vivono in spazi dei nomi controllati dall'utente. Ad esempio __init__, __import__o __file__. Non inventare mai tali nomi; usali solo come documentato.

Si noti che i nomi con trattini bassi iniziali e finali doppi sono essenzialmente riservati a Python stesso: "Non inventare mai tali nomi; usali solo come documentati".


6
Raymond spiega anche perché vorresti che il comportamento mangling dei nomi iniziasse in circa 34 minuti in questo video: youtube.com/watch?v=HTLu2DFOdTg
johncip

5
Quindi la scelta tra il carattere di sottolineatura principale singolo e il carattere di sottolineatura principale doppio in un nome è un po 'come scegliere tra protetto e privato in C ++ e Java? _single_leading_underscore può essere modificato dai bambini, ma __double_leading_underscore non può?
Alex W,

2
__double_leading_underscoreè ancora pubblico , la variabile viene semplicemente rinominata per evitare uno scontro.
CZ,

59

Gli altri intervistati hanno ragione nel descrivere i doppi trattini di sottolineatura iniziali e finali come una convenzione di denominazione per metodi "speciali" o "magici".

Sebbene sia possibile chiamare questi metodi direttamente ( [10, 20].__len__()ad esempio), la presenza dei caratteri di sottolineatura indica che questi metodi devono essere richiamati indirettamente ( len([10, 20])ad esempio). La maggior parte degli operatori Python ha un metodo "magico" associato (ad esempio, a[x]è il solito modo di invocare a.__getitem__(x)).



5

In realtà uso i nomi dei metodi _ quando devo differenziare tra i nomi delle classi genitore e figlio. Ho letto alcuni codici che utilizzavano questo modo di creare classi genitore-figlio. Come esempio posso fornire questo codice:

class ThreadableMixin:
   def start_worker(self):
       threading.Thread(target=self.worker).start()

   def worker(self):
      try:
        self._worker()
    except tornado.web.HTTPError, e:
        self.set_status(e.status_code)
    except:
        logging.error("_worker problem", exc_info=True)
        self.set_status(500)
    tornado.ioloop.IOLoop.instance().add_callback(self.async_callback(self.results))

...

e il bambino che ha un metodo _worker

class Handler(tornado.web.RequestHandler, ThreadableMixin):
   def _worker(self):
      self.res = self.render_string("template.html",
        title = _("Title"),
        data = self.application.db.query("select ... where object_id=%s", self.object_id)
    )

...


Non è a questo che serve il doppio prefisso di sottolineatura?
AMC

1

Questa convenzione viene utilizzata per variabili o metodi speciali (il cosiddetto "metodo magico") come __init__e __len__. Questi metodi forniscono speciali caratteristiche sintattiche o fanno cose speciali.

Ad esempio, __file__indica la posizione del file Python, __eq__viene eseguito quando a == bviene eseguita l'espressione.

Un utente ovviamente può creare un metodo speciale personalizzato, che è un caso molto raro, ma spesso potrebbe modificare alcuni dei metodi speciali incorporati (ad esempio, è necessario inizializzare la classe con __init__quella che verrà eseguita all'inizio quando un'istanza di una classe è creato).

class A:
    def __init__(self, a):  # use special method '__init__' for initializing
        self.a = a
    def __custom__(self):  # custom special method. you might almost do not use it
        pass

0

Aggiunto un esempio per comprendere l'uso di __ in Python. Ecco l'elenco di Tutti __

https://docs.python.org/3/genindex-all.html#_

Alcune classi di identificatori (oltre alle parole chiave) hanno significati speciali. Qualsiasi uso di * nomi, in qualsiasi altro contesto, che non segue un uso esplicitamente documentato, è soggetto a rotture senza preavviso

Limitazione dell'accesso usando __

"""
Identifiers:
-  Contain only (A-z, 0-9, and _ )
-  Start with a lowercase letter or _.
-  Single leading _ :  private
-  Double leading __ :  strong private
-  Start & End  __ : Language defined Special Name of Object/ Method
-  Class names start with an uppercase letter.
-

"""


class BankAccount(object):
    def __init__(self, name, money, password):
        self.name = name            # Public
        self._money = money         # Private : Package Level
        self.__password = password  # Super Private

    def earn_money(self, amount):
        self._money += amount
        print("Salary Received: ", amount, " Updated Balance is: ", self._money)

    def withdraw_money(self, amount):
        self._money -= amount
        print("Money Withdraw: ", amount, " Updated Balance is: ", self._money)

    def show_balance(self):
        print(" Current Balance is: ", self._money)


account = BankAccount("Hitesh", 1000, "PWD")  # Object Initalization

# Method Call
account.earn_money(100)

# Show Balance
print(account.show_balance())

print("PUBLIC ACCESS:", account.name)  # Public Access

# account._money is accessible because it is only hidden by convention
print("PROTECTED ACCESS:", account._money)  # Protected Access

# account.__password will throw error but account._BankAccount__password will not
# because __password is super private
print("PRIVATE ACCESS:", account._BankAccount__password)

# Method Call
account.withdraw_money(200)

# Show Balance
print(account.show_balance())

# account._money is accessible because it is only hidden by convention
print(account._money)  # Protected Access
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.