类属性的评估和发电机 [英] Class attribute evaluation and generators

查看:153
本文介绍了类属性的评估和发电机的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

究竟是如何评价的Python类属性?我已经跨越了一个有趣的怪癖(在Python 2.5.2),我想解释绊倒了。

How exactly does Python evaluate class attributes? I've stumbled across an interesting quirk (in Python 2.5.2) that I'd like explained.

我有其他,previously定义的属性的术语定义的某些属性的类。当我尝试使用一个生成器对象,Python中抛出一个错误,但如果我用一个简单的普通列表COM prehension,没有任何问题。

I have a class with some attributes that are defined in terms of other, previously defined attributes. When I try using a generator object, Python throws an error, but if I use a plain ordinary list comprehension, there's no problem.

下面是削减的例子。请注意,唯一不同的是布里使用发电机前pression,而切达使用列表COM prehension。

Here's the pared-down example. Note that the only difference is that Brie uses a generator expression, while Cheddar uses a list comprehension.

# Using a generator expression as the argument to list() fails
>>> class Brie :
...     base = 2
...     powers = list(base**i for i in xrange(5))
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in Brie
  File "<stdin>", line 3, in <genexpr>
NameError: global name 'base' is not defined

# Using a list comprehension works
>>> class Cheddar :
...     base = 2
...     powers = [base**i for i in xrange(5)]
... 
>>> Cheddar.powers
[1, 2, 4, 8, 16]

# Using a list comprehension as the argument to list() works
>>> class Edam :
...     base = 2
...     powers = list([base**i for i in xrange(5)])
...
>>> Edam.powers
[1, 2, 4, 8, 16]

(我的实际情况更复杂,我创建一个字典,但是这是我能找到的最小的例子。)

(My actual case was more complicated, and I was creating a dict, but this is the minimum example I could find.)

我唯一的猜测是,名单COM prehensions是在该行计算,但发生器前pressions在类结束后计算,指向其中的范围发生了变化。但我不知道为什么发生前pression不作为封闭和参考基地存放在该行的范围。

My only guess is that the list comprehensions are computed at that line, but the generator expressions are computed after the end of the class, at which point the scope has changed. But I'm not sure why the generator expression doesn't act as a closure and store the reference to base in the scope at the line.

有一个原因,如果是这样,我应该怎么思考类属性的评价机制?

Is there a reason for this, and if so, how should I be thinking of the evaluation mechanics of class attributes?

推荐答案

是的,这是一个有些冒险,这一点。类并没有真正引入新的范围,它只是那种看起来有点像它;构建这样的公开的差异。

Yeah, it's a bit dodgy, this. A class doesn't really introduce a new scope, it just sort of looks a little bit like it does; constructs like this expose the difference.

我们的想法是,当你使用一台发电机前pression它等同于一个lambda这样做:

The idea is that when you're using a generator expression it's equivalent to doing it with a lambda:

class Brie(object):
    base= 2
    powers= map(lambda i: base**i, xrange(5))

或明确作为一个函数语句:

or explicitly as a function statement:

class Brie(object):
    base= 2

    def __generatePowers():
        for i in xrange(5):
            yield base**i

    powers= list(__generatePowers())

在这种情况下,很显然,不在余地 __ generatePowers ;这两个异常的结果(除非你运气不好也有一个全球性的,在这种情况下,你会得到一个不正当)。

In this case it's clear that base isn't in scope for __generatePowers; an exception results for both (unless you were unlucky enough to also have a base global, in which case you get a wrongness).

这不适用于列表COM prehensions发生因他们是如何评价的一些内部细节,但该行为会消失在Python 3,这将对于这两种情况下也同样会失败。 一些讨论在这里。

This doesn't happen for list comprehensions due to some internal details on how they're evaluated, however that behaviour goes away in Python 3 which will fail equally for both cases. Some discussion here.

一个解决方法可以使用​​拉姆达与我们在坏日子nested_scopes前回依靠同样的技术可以了:

A workaround can be had using a lambda with the same technique we relied on back in the bad old days before nested_scopes:

class Brie(object):
    base= 2
    powers= map(lambda i, base= base: base**i, xrange(5))

这篇关于类属性的评估和发电机的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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