Как отмечалось в этом посте это связано с искажением имени атрибута.
Код, опубликованный OP, является интересным случаем, который заставил меня исследовать, как осуществляется управление именами.Оказывается, искажение имени выполняется во время компиляции в байт-код Python, что можно увидеть, выполнив этот код ( run в Python 3.7 ):
import dis
# I want the source code, not just class object.
a_def = '''
class A:
__mangled = 'aiya!'
def p(self):
print(self.__mangled)
'''
print(dis.dis(a_def, depth=2)) # In Python 3.7 they added `depth` argument so nested `code object`s will be printed.
Байт-код:
2 0 LOAD_BUILD_CLASS
2 LOAD_CONST 0 (<code object A at 0x7f4b3f6ddd20, file "<dis>", line 2>)
4 LOAD_CONST 1 ('A')
6 MAKE_FUNCTION 0
8 LOAD_CONST 1 ('A')
10 CALL_FUNCTION 2
12 STORE_NAME 0 (A)
14 LOAD_CONST 2 (None)
16 RETURN_VALUE
Disassembly of <code object A at 0x7f4b3f6ddd20, file "<dis>", line 2>:
2 0 LOAD_NAME 0 (__name__)
2 STORE_NAME 1 (__module__)
4 LOAD_CONST 0 ('A')
6 STORE_NAME 2 (__qualname__)
3 8 LOAD_CONST 1 ('aiya!')
10 STORE_NAME 3 (_A__mangled)
5 12 LOAD_CONST 2 (<code object p at 0x7f4b3f6dde40, file "<dis>", line 5>)
14 LOAD_CONST 3 ('A.p')
16 MAKE_FUNCTION 0
18 STORE_NAME 4 (p)
20 LOAD_CONST 4 (None)
22 RETURN_VALUE
Disassembly of <code object p at 0x7f4b3f6dde40, file "<dis>", line 5>:
6 0 LOAD_GLOBAL 0 (print)
2 LOAD_FAST 0 (self)
4 LOAD_ATTR 1 (_A__mangled)
6 CALL_FUNCTION 1
8 POP_TOP
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
Это объясняет, почему __decorator
в теле класса вызывает поиск _A__decorator
, потому что он "жестко закодирован" в байт-коде.
Единственный способ вызвать __decorator
в теле класса - этоиспользуйте один из следующих вызовов:
import sys
__mangled = '???'
class A():
# All following calls look horrible.
print(globals()['__mangled'])
print(eval('__mangled'))
this_module = sys.modules['__main__']
print(getattr(this_module, '__mangled'))
Как уже отмечалось, каждый вызов выглядит ужасно, и проблема заключается в __mangled
имени.Если ваша цель - намекнуть, что атрибут модуля не должен использоваться напрямую, достаточно одного подчеркивания.Но если вы действительно хотите, чтобы начальное двойное подчеркивание начиналось, вы можете добавить более двух конечных подчеркиваний, чтобы предотвратить искажение, как указано в документации :
Любой идентификатор вида __spam (как минимум два ведущихподчеркивания, самое большее одно заключительное подчеркивание ) текстуально заменяется на _classname__spam, где classname - это имя текущего класса с удаленными начальными подчеркиваниями.