如果装饰器名称是__double_leading_underscore类型,我不能在与装饰器相同的模块中声明的类中使用装饰器。
用一个例子来解释更容易:
# Just a pass-through
def __decorator(fn):
return fn
decorator = __decorator
class A(object):
@decorator
def test(self):
return 1
print(A().test())
# Prints 1
如果我使用@decorator
更改@__decorator
:
class A(object):
@__decorator
def test(self):
return 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in A
NameError: name '_A__decorator' is not defined
它试图从班级中找到__decorator
。
有没有办法保持命名约定但是引用模块而不是类?
答案 0 :(得分:2)
这是由于Python的名称错误。根据{{3}}
表单__spam的任何标识符(至少两个前导下划线, 最多一个尾随下划线)替换为文本 _classname__spam,其中classname是当前的类名,其中前导下划线被剥离。这种破坏是不加考虑的 到标识符的句法位置,只要它发生 在一个类的定义内。
强调补充。
如果解释程序在类@__decorator
中看到A
,它会忽略对decorator
的绑定,用__decorator
替换_A__decorator
并尝试评估那个标识符,它为您提供NameError
。
答案 1 :(得分:0)
如in this post所述,这是由于属性名称修饰所致。
OP发布的代码是一个有趣的案例,使我研究了名称管理的执行方式。事实证明,名称转换是在编译为Python字节码的过程中执行的,可以通过运行以下代码(在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
名称上。如果您的目标是暗示不应直接使用module属性,则只需一个下划线就足够了。但是,如果您真的想要前导双下划线,则可以添加两个以上的下划线以防止以noted in documentation的形式出现:
任何形式的__spam标识符(至少两个下划线, 最多一个下划线)在文字上被替换为 _classname__spam,其中classname是当前的类名,其中前导下划线被去除。