Подчеркивание в именах#

В Python подчеркивание в начале или в конце имени указывает на специальные имена. Чаще всего это всего лишь договоренность, но иногда это действительно влияет на поведение объекта.

Подчеркивание как имя#

В Python одно подчеркивание используется для обозначения того, что данные просто выбрасываются.

Например, если из строки line надо получить MAC-адрес, IP-адрес, VLAN и интерфейс и отбросить остальные поля, можно использовать такой вариант:

In [1]: line = '00:09:BB:3D:D6:58  10.1.10.2 86250   dhcp-snooping   10  FastEthernet0/1'

In [2]: mac, ip, _, _, vlan, intf = line.split()

In [3]: print(mac, ip, vlan, intf)
00:09:BB:3D:D6:58 10.1.10.2 10 FastEthernet0/1

Такая запись говорит о том, что нам не нужны третий и четвертый элементы.

Можно сделать так:

In [4]: mac, ip, lease, entry_type, vlan, intf = line.split()

Но тогда может быть непонятно, почему переменные lease и entry_type не используются дальше. Если понятней использовать имена, то лучше назвать переменные именами вроде ignored.

Аналогичный прием может использоваться, когда переменная цикла не нужна:

In [5]: [0 for _ in range(10)]
Out[5]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Подчеркивание в интерпретаторе#

В интерпретаторе python и ipython подчеркивание используется для получения результата последнего выражения

In [6]: [0 for _ in range(10)]
Out[6]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

In [7]: _
Out[7]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

In [8]: a = _

In [9]: a
Out[9]: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Одно подчеркивание#

Одно подчеркивание перед именем#

Одно подчеркивание перед именем указывает, что имя используется как внутреннее.

Например, если одно подчеркивание указано в имени функции или метода, это означает, что этот объект является внутренней особенностью реализации и не стоит его использовать напрямую.

Но, кроме того, при импорте вида from module import * не будут импортироваться объекты, которые начинаются с подчеркивания.

Например, в файле example.py такие переменные и функции:

db_name = 'dhcp_snooping.db'
_path = '/home/nata/pyneng/'


def func1(arg):
    print arg


def _func2(arg):
    print arg

Если импортировать все объекты из модуля, то те, которые начинаются с подчеркивания, не будут импортированы:

In [7]: from example import *

In [8]: db_name
Out[8]: 'dhcp_snooping.db'

In [9]: _path
...
NameError: name '_path' is not defined

In [10]: func1(1)
1

In [11]: _func2(1)
...
NameError: name '_func2' is not defined

Одно подчеркивание после имени#

Одно подчеркивание после имени используется в том случае, когда имя объекта или параметра пересекается со встроенными именами.

Пример:

In [12]: line = '00:09:BB:3D:D6:58  10.1.10.2 86250   dhcp-snooping   10  FastEthernet0/1'

In [13]: mac, ip, lease, type_, vlan, intf = line.split()

Два подчеркивания#

Два подчеркивания перед именем#

Два подчеркивания перед именем метода используются не просто как договоренность. Такие имена трансформируются в формат «имя класса + имя метода». Это позволяет создавать уникальные методы и атрибуты классов.

Такое преобразование выполняется только в том случае, если в конце менее двух подчеркиваний или нет подчеркиваний.

In [14]: class Switch(object):
    ...:     __quantity = 0
    ...:     def __configure(self):
    ...:         pass
    ...:

In [15]: dir(Switch)
Out[15]:
['_Switch__configure', '_Switch__quantity', ...]

Хотя методы создавались без приставки _Switch, она была добавлена.

Если создать подкласс, то метод __configure не перепишет метод родительского класса Switch:

In [16]: class CiscoSwitch(Switch):
    ...:     __quantity = 0
    ...:     def __configure(self):
    ...:         pass
    ...:

In [17]: dir(CiscoSwitch)
Out[17]:
['_CiscoSwitch__configure', '_CiscoSwitch__quantity', '_Switch__configure', '_Switch__quantity', ...]

Два подчеркивания перед и после имени#

Таким образом обозначаются специальные переменные и методы.

Например, в модуле Python есть такие специальные переменные:

  • __name__ - эта переменная равна строке __main__, когда скрипт запускается напрямую, и равна имени модуля, когда импортируется

  • __file__ - эта переменная равна имени скрипта, который был запущен напрямую, и равна полному пути к модулю, когда он импортируется

Переменная __name__ чаще всего используется, чтобы указать, что определенная часть кода должна выполняться, только когда модуль выполняется напрямую:

def multiply(a, b):

    return a * b

if __name__ == '__main__':
    print(multiply(3, 5))

А переменная __file__ может быть полезна в определении текущего пути к файлу скрипта:

import os

print('__file__', __file__)
print(os.path.abspath(__file__))

Вывод будет таким:

__file__ example2.py
/home/vagrant/repos/tests/example2.py

Кроме того, таким образом в Python обозначаются специальные методы. Эти методы вызываются при использовании функций и операторов Python и позволяют реализовать определенный функционал.

Как правило, такие методы не нужно вызывать напрямую. Но, например, при создании своего класса может понадобиться описать такой метод, чтобы объект поддерживал какие-то операции в Python.

Например, для того, чтобы можно было получить длину объекта, он должен поддерживать метод __len__.

Ещё один специальный метод __str__ вызывается, когда используется оператор print или вызывается функция str(). Если необходимо, чтобы при этом отображение было в определенном виде, надо создать этот метод в классе:

In [10]: class Switch(object):
    ...:
    ...:     def set_name(self, name):
    ...:         self.name = name
    ...:
    ...:     def __configure(self):
    ...:         pass
    ...:
    ...:     def __str__(self):
    ...:         return 'Switch {}'.format(self.name)
    ...:

In [11]: sw1 = Switch()

In [12]: sw1.set_name('sw1')

In [13]: print sw1
Switch sw1

In [14]: str(sw1)
Out[14]: 'Switch sw1'

Таких специальных методов в Python очень много. Несколько полезных ссылок, где можно почитать про конкретный метод: