为什么一个类变量没有在列表理解中定义,而另一个是? [英] Why is one class variable not defined in list comprehension but another is?

查看:28
本文介绍了为什么一个类变量没有在列表理解中定义,而另一个是?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚阅读了这个问题的答案:从类定义中的列表推导式访问类变量

它帮助我理解为什么以下代码会导致 NameError: name 'x' is not defined:

A 类:x = 1数据 = [0, 1, 2, 3]new_data = [i + x for i in data]打印(新数据)

NameError 的发生是因为 x 没有在列表理解的特殊范围内定义.但我无法理解为什么下面的代码没有任何错误.

A 类:x = 1数据 = [0, 1, 2, 3]new_data = [i for i in data]打印(新数据)

我得到输出[0, 1, 2, 3].但我期待这个错误:NameError: name 'data' is not defined 因为我期待就像在前面的例子中一样,名称 x 没有在列表推导中定义范围,类似地,名称 data 也不会在列表推导式的范围内定义.

你能帮我理解为什么 x 没有在列表推导的范围内定义,而 data 是吗?

解决方案

data 是列表推导式的;它是传递给创建的嵌套作用域的唯一参数.

列表推导式中的所有内容都在单独的范围内运行(基本上作为函数),除了用于最左边的 for 循环的可迭代对象.你可以在字节码中看到这一点:

<预><代码>>>>定义 foo():...返回 [i for i in data]...>>>dis.dis(foo)2 0 LOAD_CONST 1(<代码对象<listcomp>在0x105390390,文件<stdin>",第2行>)3 LOAD_CONST 2 ('foo..')6 MAKE_FUNCTION 09 LOAD_GLOBAL 0(数据)12 GET_ITER13 CALL_FUNCTION 1(1 个位置,0 个关键字对)16 RETURN_VALUE

代码对象像函数一样被调用,iter(data) 作为参数传入(CALL_FUNCTION使用 1 个位置参数执行,即 GET_ITER 结果).

代码对象寻找那个参数:

<预><代码>>>>dis.dis(foo.__code__.co_consts[1])2 0 BUILD_LIST 03 LOAD_FAST 0 (.0)>>6 FOR_ITER 12(到 21)9 STORE_FAST 1 (i)12 LOAD_FAST 1 (i)15 LIST_APPEND 218 JUMP_ABSOLUTE 6>>21 RETURN_VALUE

LOAD_FAST 调用是指传入的第一个也是唯一一个位置参数;此处未命名是因为从未有函数定义为其命名.

在列表推导式(或集合或字典推导式,或生成器表达式,就此而言)中使用的任何其他名称要么是局部变量、闭包或全局变量,而不是参数.

如果您返回我对该问题的回答,请查找标题为(小)例外的部分;或者,为什么某一部分仍然可以工作;我试图在那里涵盖这一特定点:

<块引用>

无论 Python 版本如何,都会在周围范围内执行推导式或生成器表达式的一部分.那将是最外层可迭代对象的表达式.

I just read the answer to this question: Accessing class variables from a list comprehension in the class definition

It helps me to understand why the following code results in NameError: name 'x' is not defined:

class A:
    x = 1
    data = [0, 1, 2, 3]
    new_data = [i + x for i in data]
    print(new_data)

The NameError occurs because x is not defined in the special scope for list comprehension. But I am unable to understand why the following code works without any error.

class A:
    x = 1
    data = [0, 1, 2, 3]
    new_data = [i for i in data]
    print(new_data)

I get the output [0, 1, 2, 3]. But I was expecting this error: NameError: name 'data' is not defined because I was expecting just like in the previous example the name x is not defined in the list comprehension's scope, similarly, the name data would not be defined too in the list comprehension's scope.

Could you please help me to understand why x is not defined in the list comprehension's scope but data is?

解决方案

data is the source of the list comprehension; it is the one parameter that is passed to the nested scope created.

Everything in the list comprehension is run in a separate scope (as a function, basically), except for the iterable used for the left-most for loop. You can see this in the byte code:

>>> def foo():
...     return [i for i in data]
... 
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x105390390, file "<stdin>", line 2>)
              3 LOAD_CONST               2 ('foo.<locals>.<listcomp>')
              6 MAKE_FUNCTION            0
              9 LOAD_GLOBAL              0 (data)
             12 GET_ITER
             13 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             16 RETURN_VALUE

The <listcomp> code object is called like a function, and iter(data) is passed in as the argument (CALL_FUNCTION is executed with 1 positional argument, the GET_ITER result).

The <listcomp> code object looks for that one argument:

>>> dis.dis(foo.__code__.co_consts[1])
  2           0 BUILD_LIST               0
              3 LOAD_FAST                0 (.0)
        >>    6 FOR_ITER                12 (to 21)
              9 STORE_FAST               1 (i)
             12 LOAD_FAST                1 (i)
             15 LIST_APPEND              2
             18 JUMP_ABSOLUTE            6
        >>   21 RETURN_VALUE

The LOAD_FAST call refers to the first and only positional argument passed in; it is unnamed here because there never was a function definition to give it a name.

Any additional names used in the list comprehension (or set or dict comprehension, or generator expression, for that matter) are either locals, closures or globals, not parameters.

If you go back to my answer to that question, look for the section titled The (small) exception; or, why one part may still work; I tried to cover this specific point there:

There's one part of a comprehension or generator expression that executes in the surrounding scope, regardless of Python version. That would be the expression for the outermost iterable.

这篇关于为什么一个类变量没有在列表理解中定义,而另一个是?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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