嵌套列表理解范围 [英] Nested list comprehension scope

查看:70
本文介绍了嵌套列表理解范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

解释我的问题的最好方法是举一个例子:



example.py:

  A类(对象):
整数= [1、2、3]
单身= [i对于i代表整数]

B类(对象):
整数= [1、2、3]
对= [[i,j)对于i的整数,对于j的整数]

当我在python 2下运行时,它工作正常,但是在python 3下,我得到了 NameError 班级 B (而不是班级 A ):



<班级= lang-none prettyprint-override> $ python example.py
回溯(最近一次调用):
文件 example.py,第6行,在<中。 >
B类(对象):
文件 example.py,行8,在B中
对= [(i,j)表示i的整数,j表示整数的$] b在< listcomp>中的文件 example.py第8行中
对= [(i,j)表示i的整数,j表示整数的整数]
NameError:未定义全局名称'integers'

为什么只有类 B 会引发 NameError 以及为什么

解决方案

类作用域在Python 3中有点奇怪,但这是有充分理由的。

>

在Python 2中,迭代变量( i j 您的示例)从列表理解中泄漏出来,将被包括在外部范围内。这是因为它们是在Python 2的设计早期开发的,并且它们基于显式循环。作为这种意外情况的示例,请在未使用Python 2的地方检查 Bi Bj 的值



在Python 3中,列表理解被更改以防止这种泄漏。现在,它们通过一个函数(具有自己的作用域)实现,该函数被调用以产生列表值。



这样做的结果是,在类中,列表理解通常可以实现。看不到任何类变量。这与无法直接看到类变量的方法(仅通过 self 或显式类名)相似。例如,在下面的类中调用该方法将产生与您在列表理解中看到的相同的 NameError 异常:

  class Foo:
classvar = bar
def blah(self):
print(classvar)#引发 NameError:全局名称'classvar '未定义

但是有一个例外。列表理解的第一个 for 子句所迭代的序列是在内部函数之外求值的。这就是为什么您的 A 类可在Python 3中工作的原因。这样做是为了使生成器可以立即捕获不可迭代的对象(而不是仅当 next 被调用,它们的代码运行)。



但是对于内部 B 的二级理解中的c $ c>子句。



使用 dis 模块反汇编一些创建列表推导的函数:

  def f(lst):
返回[i为第一个i表示]

def g(lst):
返回[(i,j)为第一个i表示第一个j ]

这里是 f 的反汇编:

 >> dis.dis(f)
2 0 LOAD_CONST 1(位于0x0000000003CCA1E0的<代码对象< listcomp> ;,文件< pyshell#374>,第2行>)
3 LOAD_CONST 2('f 。< locals>。" listcomp>')
6 MAKE_FUNCTION 0
9 LOAD_FAST 0(lst)
12 GET_ITER
13 CALL_FUNCTION 1(1个位置,0个关键字对)
16 RETURN_VALUE

前三行显示 f 加载一个预编译的代码块并从中创建一个函数(将其命名为 f。< locals>< listcomp> )。



接下来的两行显示 lst 变量正在加载,并且迭代器由此产生。这发生在 f 的范围内,而不是内部函数的范围内。然后使用该迭代器作为参数调用< listcomp> 函数。



这与类 A 。它从类变量 integers 中获取迭代器,就像您可以在新成员的定义中使用对先前类成员的其他引用一样。



现在,比较 g 的反汇编,它通过在同一列表上重复两次来进行配对:

 >> dis.dis(g)
2 0 LOAD_CLOSURE 0(lst)
3 BUILD_TUPLE 1
6 LOAD_CONST 1(<代码对象< listcomp>位于0x0000000003CCA810,文件< pyshell#377> ;,第2行>)
9 LOAD_CONST 2('g。< locals>。< listcomp>')
12 MAKE_CLOSURE 0
15 LOAD_DEREF 0(第一个)
18 GET_ITER
19 CALL_FUNCTION 1(1个位置,0个关键字对)
22 RETURN_VALUE



<这次,它使用代码对象而不是基本函数来构建闭包。闭包是具有一些自由变量的函数,这些变量引用了封闭范围内的事物。对于 g 中的< listcomp> 函数,此方法很好用,因为其范围是正常的。但是,当您尝试在类B中使用相同类型的理解时,闭包会失败,因为类不允许类所包含的函数以这种方式查看其范围(如 Foo

值得注意的是,不仅内部序列值会导致此问题。如上一个问题链接如果在列表理解的其他地方引用了类变量,您将遇到同样的问题:

  C类:
num = 5
个产品= [i * i在范围(10)中的num]#引发Namenum约num

您没有,但是从多级列表推导中得到了一个错误,其中内部用于(或 if )子句仅引用前面循环的结果。这是因为这些值不是闭包的一部分,而只是< listcomp> 函数范围内的局部变量。

  D类:
嵌套= [[1、2、3],[4、5、6]]
展平= [内部嵌套项用于内部项目]#作品!

就像我说的那样,类作用域有点奇怪。


The best way to explain my question is with an example:

example.py:

class A(object):
    integers = [1, 2, 3]
    singles = [i for i in integers]

class B(object):
    integers = [1, 2, 3]
    pairs = [(i, j) for i in integers for j in integers]

When I run this under python 2 it works fine, but under python 3 I get a NameError for class B (but not class A):

$ python example.py
Traceback (most recent call last):
  File "example.py", line 6, in <module>
    class B(object):
  File "example.py", line 8, in B
    pairs = [(i, j) for i in integers for j in integers]
  File "example.py", line 8, in <listcomp>
    pairs = [(i, j) for i in integers for j in integers]
NameError: global name 'integers' is not defined

Why does only class B raise a NameError and why only under Python 3?

解决方案

Class scopes are a bit strange in Python 3, but its for a good reason.

In Python 2, the iteration variables (i and j in your examples) leaked out of list comprehensions and would be included in the outside scope. This is because they were developed early in Python 2's design, and they were based on explicit loops. As an example of how this is unexpected, check the values of B.i and B.j in Python 2 where you didn't get an error!

In Python 3, list comprehensions were changed to prevent this leaking. They are now implemented with a function (which has its own scope) that is called to produce the list value. This makes them work the same as generator expressions, which have always been functions under the covers.

A consequence of this is that in a class, a list comprehension usually can't see any class variables. This is parallel to a method not being able to see class variables directly (only though self or the explicit class name). For example, calling the method in the class below will give the same NameError exception you are seeing in your list comprehension:

class Foo:
    classvar = "bar"
    def blah(self):
        print(classvar) # raises "NameError: global name 'classvar' is not defined"

There is an exception however. The sequence being iterated over by the first for clause of a list comprehension is evaluated outside of the inner function. This is why your A class works in Python 3. It does this so that generators can catch non-iterable objects immediately (rather than only when next is called on them and their code runs).

But it doesn't work for the inner for clause in the two-level comprehension in class B.

You can see the difference if you disassemble some functions that create list comprehensions using the dis module:

def f(lst):
    return [i for i in lst]

def g(lst):
    return [(i, j) for i in lst for j in lst]

Here's the disassembly of f:

>>> dis.dis(f)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x0000000003CCA1E0, file "<pyshell#374>", line 2>) 
              3 LOAD_CONST               2 ('f.<locals>.<listcomp>') 
              6 MAKE_FUNCTION            0 
              9 LOAD_FAST                0 (lst) 
             12 GET_ITER             
             13 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             16 RETURN_VALUE       

The first three lines show f loading up a precompiled code block and creating a function out of it (it names it f.<locals>.<listcomp>). This is the function used to make the list.

The next two lines show the lst variable being loaded and an iterator being made from it. This is happening within f's scope, not the inner function's. Then the <listcomp> function is called with that iterator as its argument.

This is comparable to class A. It gets the iterator from the class variable integers, just like you can use other kinds of references to previous class members in the definition of a new member.

Now, compare the disassembly of g, which makes pairs by iterating over the same list twice:

>>> dis.dis(g)
  2           0 LOAD_CLOSURE             0 (lst) 
              3 BUILD_TUPLE              1 
              6 LOAD_CONST               1 (<code object <listcomp> at 0x0000000003CCA810, file "<pyshell#377>", line 2>) 
              9 LOAD_CONST               2 ('g.<locals>.<listcomp>') 
             12 MAKE_CLOSURE             0 
             15 LOAD_DEREF               0 (lst) 
             18 GET_ITER             
             19 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             22 RETURN_VALUE         

This time, it builds a closure with the code object, rather than a basic function. A closure is a function with some "free" variables that refer to things in the enclosing scope. For the <listcomp> function in g, this works just fine, since its scope is a normal one. However, when you try to use the same sort of comprehension in class B the closure fails, since classes don't let functions they contain see into their scopes in that way (as demonstrated with the Foo class above).

Its worth noting that not only inner sequence values cause this issue. As in the previous question linked to by BrenBarn in a comment, you'll have the same issue if a class variable is referred to elsewhere in the list comprehension:

class C:
    num = 5
    products = [i * num for i in range(10)] # raises a NameError about num

You don't, however get an error from multi-level list comprehensions where the inner for (or if) clauses only refer to the results of the preceeding loops. This is because those values aren't part of a closure, just local variables inside the <listcomp> function's scope.

class D:
    nested = [[1, 2, 3], [4, 5, 6]]
    flattened = [item for inner in nested for item in inner] # works!

Like I said, class scopes are a bit strange.

这篇关于嵌套列表理解范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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