薛定谔的变量:如果你正在检查它的存在,__class__ 单元格会神奇地出现吗? [英] Schrödinger's variable: the __class__ cell magically appears if you're checking for its presence?

查看:25
本文介绍了薛定谔的变量:如果你正在检查它的存在,__class__ 单元格会神奇地出现吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这里有一个惊喜:

<预><代码>>>>B类:...打印(当地人())... def foo(self):...打印(当地人())... print(__class__ in locals().values())...{'__module__': '__main__', '__qualname__': 'B'}>>>B().foo(){'__class__': <class '__main__.B'>, 'self': <__main__.B 对象在 0x7fffe916b4a8>}真的

似乎解析器显式检查了仅提及 __class__ 的内容?否则我们应该得到类似

NameError: name '__class__' 未定义

实际上,如果您修改为只检查密钥,即检查 locals() 中的 '__class__',那么我们在范围内只有 self 符合预期.

这个变量被神奇地注入作用域是怎么发生的?我的猜测是这与 super 有关 - 但我没有使用 super,所以如果不需要,编译器为什么会在此处创建隐式闭包引用?

解决方案

这是 Python 3 的无参数 super 实现中的一个奇怪的交互.在方法中访问 super 会触发添加一个隐藏的 __class__ 闭包变量,该变量引用定义该方法的类.解析器通过将 __class__ 添加到方法的符号表中,在方法中加载名称 super 的特殊情况,然后其余相关代码都查找 __class__ 而不是 super.但是,如果您尝试自己访问 __class__,所有寻找 __class__ 的代码都会看到它并认为它应该进行 super 处理!>

这里是添加名称 __class__ 如果看到 super 到符号表:

case Name_kind:如果 (!symtable_add_def(st, e->v.Name.id,e-> v.Name.ctx == 加载?使用:DEF_LOCAL))VISIT_QUIT(st, 0);/* 特例 super:它算作 __class__ 的使用 */if (e->v.Name.ctx == Load &&st->st_cur->ste_type == FunctionBlock &&!PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) {如果 (!GET_IDENTIFIER(__class__) ||!symtable_add_def(st, __class__, USE))VISIT_QUIT(st, 0);}休息;

这里是drop_class_free,设置ste_needs_class_closure:

静态整数drop_class_free(PySTentryObject *ste, PyObject *free){内部资源;如果 (!GET_IDENTIFIER(__class__))返回0;res = PySet_Discard(free, __class__);如果 (res <0)返回0;如果(资源)ste->ste_needs_class_closure = 1;返回 1;}

检查编译器部分ste_needs_class_closure 并创建隐式单元格:

if (u->u_ste->ste_needs_class_closure) {/* 创建一个隐式 __class__ 单元格.*/_Py_IDENTIFIER(__class__);PyObject *元组,*名称,*零;内部资源;断言(u-> u_scope_type == COMPILER_SCOPE_CLASS);断言(PyDict_Size(u-> u_cellvars)== 0);名称 = _PyUnicode_FromId(&PyId___class__);如果(!名称){compiler_unit_free(u);返回0;}...

有更多相关代码,但包含所有代码太多了.Python/compile.cPython/symtable.c 是如果你想看更多,去哪里看.

如果您尝试使用名为 __class__ 的变量,您可能会遇到一些奇怪的错误:

 类 Foo:def f(自我):__类__ = 3极好的()Foo().f()

输出:

回溯(最近一次调用最后一次):文件./prog.py",第 6 行,在 <module> 中文件./prog.py",第 4 行,在 f 中运行时错误:super():找不到 __class__ 单元格

__class__ 的赋值意味着__class__ 是一个局部变量而不是一个闭包变量,所以闭包单元super() 需要的是'在那里.

def f():__类__ = 2Foo类:def f(自我):打印(__类__)Foo().f()F()

输出:

.Foo'>

即使在封闭作用域中有一个实际的 __class__ 变量,__class__ 的特殊情况意味着你得到的是类而不是封闭作用域的变量值.

There's a surprise here:

>>> class B:
...     print(locals())
...     def foo(self):
...         print(locals())
...         print(__class__ in locals().values())
...         
{'__module__': '__main__', '__qualname__': 'B'}
>>> B().foo()
{'__class__': <class '__main__.B'>, 'self': <__main__.B object at 0x7fffe916b4a8>}
True

It seems like the mere mention of __class__ is explicitly checked by the parser? Otherwise we should get something like

NameError: name '__class__' is not defined

Indeed, if you modify to only check the key instead, i.e. check for '__class__' in locals(), then we only have self in scope as expected.

How does it happen that this variable gets magically injected into scope? My guess is this is something to do with super - but I didn't use super, so why does the compiler create an implicit closure reference here if it isn't needed?

解决方案

This is a weird interaction in Python 3's implementation of no-argument super. An access to super in a method triggers the addition of a hidden __class__ closure variable referring to the class that defines the method. The parser special-cases a load of the name super in a method by also adding __class__ to the method's symbol table, and then the rest of the relevant code all looks for __class__ instead of super. However, if you try to access __class__ yourself, all the code looking for __class__ sees it and thinks it should do the super handling!

Here's where it adds the name __class__ to the symbol table if it sees super:

case Name_kind:
    if (!symtable_add_def(st, e->v.Name.id,
                          e->v.Name.ctx == Load ? USE : DEF_LOCAL))
        VISIT_QUIT(st, 0);
    /* Special-case super: it counts as a use of __class__ */
    if (e->v.Name.ctx == Load &&
        st->st_cur->ste_type == FunctionBlock &&
        !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) {
        if (!GET_IDENTIFIER(__class__) ||
            !symtable_add_def(st, __class__, USE))
            VISIT_QUIT(st, 0);
    }
    break;

Here's drop_class_free, which sets ste_needs_class_closure:

static int
drop_class_free(PySTEntryObject *ste, PyObject *free)
{
    int res;
    if (!GET_IDENTIFIER(__class__))
        return 0;
    res = PySet_Discard(free, __class__);
    if (res < 0)
        return 0;
    if (res)
        ste->ste_needs_class_closure = 1;
    return 1;
}

The compiler section that checks ste_needs_class_closure and creates the implicit cell:

if (u->u_ste->ste_needs_class_closure) {
    /* Cook up an implicit __class__ cell. */
    _Py_IDENTIFIER(__class__);
    PyObject *tuple, *name, *zero;
    int res;
    assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
    assert(PyDict_Size(u->u_cellvars) == 0);
    name = _PyUnicode_FromId(&PyId___class__);
    if (!name) {
        compiler_unit_free(u);
        return 0;
    }
    ...

There's more relevant code, but it's too much to include all of it. Python/compile.c and Python/symtable.c are where to look if you want to see more.

You can get some weird bugs if you try to use a variable named __class__:

class Foo:
    def f(self):
        __class__ = 3
        super()

Foo().f()

Output:

Traceback (most recent call last):
  File "./prog.py", line 6, in <module>
  File "./prog.py", line 4, in f
RuntimeError: super(): __class__ cell not found

The assignment to __class__ means __class__ is a local variable instead of a closure variable, so the closure cell super() needs isn't there.

def f():
    __class__ = 2
    class Foo:
        def f(self):
            print(__class__)

    Foo().f()

f()

Output:

<class '__main__.f.<locals>.Foo'>

Even though there's an actual __class__ variable in the enclosing scope, the special-casing of __class__ means you get the class instead of the enclosing scope's variable value.

这篇关于薛定谔的变量:如果你正在检查它的存在,__class__ 单元格会神奇地出现吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆