https://habr.com/ru/post/459672/Недавно мне довелось послушать доклад о хороших и плохих практиках программирования на языке Си. В нем, в частности, была затронута тема расшифровки
забавно выглядящего программного кода (
смайликов в Си). После чего последовал спор о целесообразности использования такого запутанного кода для проверки навыков кандидата на должность программиста при собеседованиях. Спор не привел к единому мнению.
Рассмотрим возможный вопрос по
смайликам при собеседовании на должность, подразумевающую знание языка программирования Python.
Задача
Имеются два выражения:
_+_
_|_
Какое из этих двух выражений чаще будет приводить к возникновению ошибки и почему?
Замечания к выражениямСледует отметить, что данные выражения плохо читаются, такие выражения не целесообразно использовать в рабочем коде, а можно использовать только в качестве головоломки.
Также нужно помнить, что
смайлики в задаче написаны не
питонично (не соответствуют стандарту стиля
PEP 8).
Решение
В Python символ подчеркивания не является
ключевым словом, поэтому, в общем случае, его можно использовать для присвоения некоторых значений переменных.
Задача сводится к рассмотрению случаев появления ошибок при сложении и при «побитовом или» (для краткости - просто «или») одной переменной. Т.е. к поиску встроенных типов (классов) Python для которых реализована одна операция и к поиску встроенных типов где реализована другая операция.
В консоли Python легко проверить, что существуют такие значения подчеркивания при которых:
В вычислении каждого выражения нет ошибки>>> _ = 10
>>> _+_
20
>>> _ = 10
>>> _|_
10
В вычислении первого выражения нет ошибки>>> _ = '10'
>>> _+_
'1010'
>>> _ = '10'
>>> _|_
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'str' and 'str'
В вычислении второго выражения нет ошибки>>> _ = {1, 0}
>>> _+_
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'set' and 'set'
>>> _|_
{0, 1}
В вычислении каждого выражения - ошибка>>> _ = {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.
Встроенные классы, для которых реализованы наши методы:
Девять встроенных классов реализуют операцию сложения и четыре реализуют «или».
Дополнительно можно попытаться порассуждать про частоту использования базовых классов в реальных проектах, про небазовые классы и реализацию указанных выше методов для них. Вероятно, это будет положительно оценено при собеседовании.
Ответ 1.Второе выражение чаще будет приводить к возникновению ошибки.
Возможные дополнительные вопросы про подчеркивание на собеседовании
Какова роль подчеркивания в именовании объектов?
Ответ 2.Символ подчеркивания может использоваться для разделения слов в
разных стилях именования сущностей в Python:
- При именовании в нижнем регистре
lower_case_with_underscores
- При именовании в верхнем регистре
UPPER_CASE_WITH_UNDERSCORES
Некоторые имена, ограниченные символами подчеркивания, являются
зарезервированными:
- С символом подчеркивания в начале имени не импортируется при общем импорте
from module import *
- С двумя символами подчеркивания с обеих сторон имени считается системным, не рекомендуется использовать такие имена в других целях
- С двумя символами подчеркивания в начале имени считается приватным (не публичным) для класса
Какова роль отдельного символа подчеркивания?
Ответ 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
Кроме того единичное подчеркивание принято использовать для удобства
интернализации.