python

Подчеркивание в Python

  • пятница, 12 июля 2019 г. в 00:19:25
https://habr.com/ru/post/459672/
  • Python


Недавно мне довелось послушать доклад о хороших и плохих практиках программирования на языке Си. В нем, в частности, была затронута тема расшифровки забавно выглядящего программного кода (смайликов в Си). После чего последовал спор о целесообразности использования такого запутанного кода для проверки навыков кандидата на должность программиста при собеседованиях. Спор не привел к единому мнению.

Рассмотрим возможный вопрос по смайликам при собеседовании на должность, подразумевающую знание языка программирования Python.

Задача


Имеются два выражения:

  1. _+_
  2. _|_

Какое из этих двух выражений чаще будет приводить к возникновению ошибки и почему?

Замечания к выражениям
Следует отметить, что данные выражения плохо читаются, такие выражения не целесообразно использовать в рабочем коде, а можно использовать только в качестве головоломки.
Также нужно помнить, что смайлики в задаче написаны не питонично (не соответствуют стандарту стиля PEP 8).

Решение


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

Задача сводится к рассмотрению случаев появления ошибок при сложении и при «побитовом или» (для краткости - просто «или») одной переменной. Т.е. к поиску встроенных типов (классов) Python для которых реализована одна операция и к поиску встроенных типов где реализована другая операция.

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

  1. В вычислении каждого выражения нет ошибки
    >>> _ = 10
    >>> _+_
    20
    >>> _ = 10
    >>> _|_
    10
  2. В вычислении первого выражения нет ошибки
    >>> _ = '10'
    >>> _+_
    '1010'
    >>> _ = '10'
    >>> _|_
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for |: 'str' and 'str'
  3. В вычислении второго выражения нет ошибки
    >>> _ = {1, 0}
    >>> _+_
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for +: 'set' and 'set'
    >>> _|_
    {0, 1}
  4. В вычислении каждого выражения - ошибка
    >>> _ = {1: 0}
    >>> _+_
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
    >>> _|_
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: unsupported operand type(s) for |: 'dict' and 'dict'

Магия Python заключается в том, что реализация и сложения, и «или», и других операторов находится в, так называемых, «магических методах», описанных для требуемых классов.
Для сложения это метод

__add__

Для «или» это метод

__or__

В названии каждого магического метода присутствуют символы подчеркивания, по два в начале и в конце соответствующего слова.

Поиск информации о встроенных классах Python.
Можно пытаться отыскать требуемую информацию в документации к встроенным типам на официальном сайте Python: https://docs.python.org/3.7/library/stdtypes.html.
Можно в терминале Python выполнить

>>> import builtins
>>> help(builtins)

Я предпочитаю использовать документацию именно той версии Python, которую я использую в данный момент, обращаясь к web-интерфейсу модуля pydoc.

Для этого в командной строке вводим, например:

python3 -m pydoc -p 3344

и открываем в браузере документацию модуля http://localhost:3344/builtins.html.

Встроенные классы, для которых реализованы наши методы:
__add__ __or__
bool bool
bytearray frozenset
bytes int
complex set
float
int
list
str
tuple

Девять встроенных классов реализуют операцию сложения и четыре реализуют «или».
Дополнительно можно попытаться порассуждать про частоту использования базовых классов в реальных проектах, про небазовые классы и реализацию указанных выше методов для них. Вероятно, это будет положительно оценено при собеседовании.

Ответ 1.
Второе выражение чаще будет приводить к возникновению ошибки.



Возможные дополнительные вопросы про подчеркивание на собеседовании


Какова роль подчеркивания в именовании объектов?


Ответ 2.
Символ подчеркивания может использоваться для разделения слов в разных стилях именования сущностей в Python:

  1. При именовании в нижнем регистре

    lower_case_with_underscores
  2. При именовании в верхнем регистре

    UPPER_CASE_WITH_UNDERSCORES

Некоторые имена, ограниченные символами подчеркивания, являются зарезервированными:

  1. С символом подчеркивания в начале имени не импортируется при общем импорте

    from module import *
  2. С двумя символами подчеркивания с обеих сторон имени считается системным, не рекомендуется использовать такие имена в других целях
  3. С двумя символами подчеркивания в начале имени считается приватным (не публичным) для класса


Какова роль отдельного символа подчеркивания?


Ответ 3.
В интерпретаторе, если символ подчеркивания не был явно использован в качестве переменной, хранит результат последнего неошибочного действия, выведенного на экран.

$ python3
Python 3.6.8 (default, Feb 14 2019, 22:09:48)
[GCC 7.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> _
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> 2 * 5
10
>>> _
10
>>> _ = 1
>>> _
1
>>> 2 * 5
10
>>> _
1

Это связано с тем, что sys.stdout экранируется с помощью displayhook. Возьмем пример из официальной документации:

def displayhook(value):
    if value is None:
        return
    # Set '_' to None to avoid recursion
    builtins._ = None
    text = repr(value)
    try:
        sys.stdout.write(text)
    except UnicodeEncodeError:
        bytes = text.encode(sys.stdout.encoding, 'backslashreplace')
        if hasattr(sys.stdout, 'buffer'):
            sys.stdout.buffer.write(bytes)
        else:
            text = bytes.decode(sys.stdout.encoding, 'strict')
            sys.stdout.write(text)
    sys.stdout.write("\n")
    builtins._ = value

Кроме того единичное подчеркивание принято использовать для удобства интернализации.