'переопределено'
>>> t2._bar
'переопределено'
>>> t2.__baz
AttributeError:
"'ExtendedTest' object has no attribute '__baz'"
Постойте, почему при попытке проверить значение
t2.__baz
мы получаем
исключение
AttributeError
? Искажение имени наносит очередной удар!
Оказывается, что этот объект вообще не имеет атрибута
__baz
:
>>> dir(t2)
['_ExtendedTest__baz', '_Test__baz', '__class__',
'__delattr__', '__dict__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__le__', '__lt__',
'__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__',
'__weakref__', '_bar', 'foo', 'get_vars']
Как видите, имя
__baz
превратилось в
_ExtendedTest__baz
, чтобы предот-
вратить случайное изменение. Но первоначальное имя
_Test__baz
по-
прежнему на месте:
>>> t2._ExtendedTest__baz
'переопределено'
>>> t2._Test__baz
42
Искажение имени с двойным символом подчеркивания для программи-
ста совершенно очевидно. Взгляните на следующий пример, который это
подтверждает:
class ManglingTest:
def __init__(self):
self.__mangled = 'Привет'
2 .4 . Подчеркивания, дандеры и другое 51
def get_mangled(self):
return self.__mangled
>>> ManglingTest().get_mangled()
'Привет'
>>> ManglingTest().__mangled
AttributeError:
"'ManglingTest' object has no attribute '__mangled'"
Распространяется ли искажение на имена методов? Конечно! Искажение
имен затрагивает все имена, которые в контексте класса начинаются с двух
символов подчеркивания (или «дандеров»):
class MangledMethod:
def __method(self):
return 42
def call_it(self):
return self.__method()
>>> MangledMethod().__method()
AttributeError:
"'MangledMethod' object has no attribute '__method'"
>>> MangledMethod().call_it()
42
Вот еще один, пожалуй, вызывающий удивление, пример искажения имен
в действии:
_MangledGlobal__mangled = 23
class MangledGlobal:
def test(self):
return __mangled
>>> MangledGlobal().test()
23
В этом примере я назначил
_MangledGlobal__mangled
глобальной пе-
ременной. Затем к этой переменной я обратился в контексте класса
MangledGlobal
. Из-за искажения имен я смог сослаться на глобальную
переменную
_MangledGlobal__mangled
просто как на
__mangled
внутри
метода
test()
класса.
52 Глава 2 • Шаблоны для чистого Python
Интерпретатор Python автоматически расширил имя
__mangled
до
_MangledGlobal__mangled
, потому что оно начинается с двух символов
подчеркивания. Это показывает, что искажение имен точно не связано
с атрибутами класса. Оно относится к любому имени, начинающемуся
с двух символов подчеркивания, которое используется в контексте
класса.
Уф-ф! Многовато, надо переварить.
Буду с вами честен: я написал эти примеры и объяснения не сразу из
головы. Чтобы это сделать, мне потребовалось время на исследование
и редактирование. Я использую Python много лет, однако правила и вот
такие особые случаи, как этот, не крутятся у меня в мозгу постоянно.
Для программиста иногда самым важным навыком является умение
«распознавать шаблоны» (образы, паттерны) и понимать, где их нужно
искать. Если в этом месте вы чувствуете себя несколько подавленными,
не волнуйтесь. Отдохните и поэкспериментируйте с несколькими при-
мерами из этого раздела.
Пусть эти принципы впитаются как следует, чтобы вы наконец осознали
общую идею искажения имен и некоторые другие формы поведения, ко-
торые я вам показал. Если однажды вы столкнетесь с ними «в полях», то
хотя бы будете знать, что именно искать в документации.
Экскурс: что такое дандеры?
Если вы слышали разговор опытных питонистов о Python или при-
сутствовали при обсуждении на конференциях, то, возможно, слышали
термин дандер (dunder). Вам интересно, что же это такое? Ладно, вот
ответ.
В сообществе Python двойные символы подчеркивания часто называют
«дандерами» (dunders — это сокращение от англ. double underscores). При-
чина в том, что в исходном коде Python двойные символы подчеркивания
встречаются довольно часто, и, чтобы не изнурять свои жевательные
мышцы, питонисты нередко сокращают термин «двойное подчеркивание»,
сводя его до «дандера».
2 .4 . Подчеркивания, дандеры и другое 53
Например, переменная
__baz
будет произноситься как «дандер baz». Ана-
логичным образом, метод
__init__
звучит как «дандер init», хотя будет
логичным предположить, что так: «дандер init дандер».
Но это всего лишь еще одна из причуд среди прочих согласованных пра-
вил именования. Для разработчиков Python это все равно что секретное
рукопожатие.
4 . Двойной начальный и замыкающий символ
подчеркивания: __var__
Пожалуй, это удивляет, но искажение имен не применяется, если имя
начинается и заканчивается двойными символами подчеркивания. Ин-
терпретатор Python не трогает переменные, окруженные префиксом
и постфиксом, которые состоят из двойных символов подчеркивания:
class PrefixPostfixTest:
def __init__(self):
self.__bam__ = 42
>>> PrefixPostfixTest().__bam__
42
Однако имена, у которых есть начальный и замыкающий двойной символ
подчеркивания, в языке зарезервированы для специального применения.
Это правило касается таких имен, как метод
__init__
для конструкторов
объектов или метод
__call__
, который делает объекты вызываемыми.
Эти дандер-методы часто упоминаются как магические методы, однако
в сообществе Python многим разработчикам, включая меня, это слово не
нравится. Такое название подразумевает, что применение дандер-методов
не приветствуется, и это абсолютно не соответствует действительности.
В Python они представляют собой ключевое функциональное средство
и должны применяться по мере необходимости. В них нет ничего «маги-
ческого» или тайного.
Тем не менее в контексте согласованных правил именования лучше воз-
держаться от использования имен, которые начинаются и заканчиваются
54 Глава 2 • Шаблоны для чистого Python
двойными символами подчеркивания, в своих собственных программах —
во избежание конфликтов с последующими версиями языка Python.
5 . Одинарный символ подчеркивания: _
По договоренности одинарный автономный символ подчеркивания ино-
гда используется в качестве имени, чтобы подчеркнуть, что эта перемен-
ная временная или незначительная.
Например, в приведенном ниже цикле нам не нужен доступ к нарастающе-
му индексу, и мы можем применить «
_
», чтобы показать, что этот символ
подчеркивания является лишь временным значением:
>>> for _ in range(32):
... print('Привет, Мир.')
Одинарные символы подчеркивания также можно применять в распа-
ковке выражений, обозначая таким образом «неважную» переменную,
чтобы проигнорировать отдельные значения. И снова: смысл одинарного
подчеркивания существует только по договоренности, и оно не запускает
особых форм поведения в синтаксическом анализаторе Python. Одинар-
ный символ подчеркивания — это просто имя допустимой переменной,
которое иногда используется с этой целью.
В следующем ниже примере исходного кода я распаковываю кортеж в от-
дельные переменные, но я заинтересован только в значениях полей
color
и
mileage
. Однако для того, чтобы выражение распаковки было успешным,
мне нужно назначить переменным все содержащиеся в кортеже значения.
Именно тут в качестве переменной-заполнителя пригодится символ «
_
»:
>>> car = ('красный', 'легковой автомобиль', 12, 3812.4)
>>> color, _, _, mileage = car
>>> color
'красный'
>>> mileage
3812.4
>>> _
12
2 .4 . Подчеркивания, дандеры и другое 55
Помимо его применения в качестве временной переменной, символ «
_
»
является специальной переменной в большинстве интерпретаторов Python,
работающих в цикле чтение-вычисление-печать (REPL). Она представляет
в них результат последнего выражения, вычисленного интерпретатором.
Это удобно, если вы работаете в сеансе интерпретатора и хотите получить
доступ к результату предыдущего вычисления:
>>> 20 + 3
23
>>> _
23
>>> print(_)
23
Это также удобно, если вы конструируете объекты на лету и хотите взаи-
модействовать с ними, не назначая им имени перед этим:
>>> list()
[]
>>> _.append(1)
>>> _.append(2)
>>> _.append(3)
>>> _
[1, 2, 3]
Ключевые выводы
Do'stlaringiz bilan baham: |