2016-11-22 70 views
3

我想创建一个有多个类级变量的类,其中一些具有引用先前声明的类级变量的计算值。但是,我在某些时候很难引用这些变量。python类变量分配不规则

我第一次尝试:

#!/usr/bin/env python 
from decimal import Decimal 
import math 

class Foo(object): 
    NUM_BUCKETS = 10 
    BUCKET_SIZE = Decimal(1.0/NUM_BUCKETS) 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 

print Foo.BUCKET_LABELS 

结果:

> python test.py 
Traceback (most recent call last): 
    File "test.py", line 5, in <module> 
    class Foo(object): 
    File "test.py", line 8, in Foo 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 
    File "test.py", line 8, in <genexpr> 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 
NameError: global name 'BUCKET_SIZE' is not defined 

尝试通过类名来访问类变量无法正常工作或:

#!/usr/bin/env python 
from decimal import Decimal 
import math 

class Foo(object): 
    NUM_BUCKETS = 10 
    BUCKET_SIZE = Decimal(1.0/NUM_BUCKETS) 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(Foo.BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 

print Foo.BUCKET_LABELS 

结果:

> python test2.py 
Traceback (most recent call last): 
    File "test2.py", line 5, in <module> 
    class Foo(object): 
    File "test2.py", line 8, in Foo 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(Foo.BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 
    File "test2.py", line 8, in <genexpr> 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(Foo.BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 
NameError: global name 'Foo' is not defined 

用硬编码值替换对BUCKET_SIZE的引用可修复问题;即使是在同一线路的其它类级变量引用,它工作得很好:

#!/usr/bin/env python 
from decimal import Decimal 
import math 

class Foo(object): 
    NUM_BUCKETS = 10 
    BUCKET_SIZE = Decimal(1.0/NUM_BUCKETS) 
    BUCKET_LABELS = tuple("BUCKET_{}".format(int(Decimal(0.1) * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 

print Foo.BUCKET_LABELS 

结果:

> python test3.py 
('BUCKET_10', 'BUCKET_20', 'BUCKET_30', 'BUCKET_40', 'BUCKET_50', 'BUCKET_60', 'BUCKET_70', 'BUCKET_80', 'BUCKET_90', 'BUCKET_100') 

有谁知道在那个地方引用BUCKET_SIZE的正确方法是什么?这是Python本身的错误吗? (我运行的Python 2.7.5,顺便说一句。)

+2

这看起来像是生成器表达式范围的问题,因为当我将其更改为列表理解时,错误消失。 – TigerhawkT3

+1

另外NUM_BUCKETS在发生器中工作良好,只是在与BUCKET_SIZE部分问题 –

+1

本质上是一个愚蠢的[这个问题](http://stackoverflow.com/questions/20136955/python3-nested-list-comprehension-scope),除该问题在Python 3上,并使用列表理解。我没有看到任何使用Genexps的优秀候选人。 – user2357112

回答

2

首先,在其他的解决方案,通过简单的编辑这一行(注意括号):

BUCKET_LABELS = tuple(["BUCKET_{}".format(int(BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)]) 

现在,如果你对感兴趣,为什么它的工作方式与Python相同,为什么它不是bug ...嗯,它不是一个简单的问题:-):

[i * 2 for i in xrange (3)]是列表理解。它生成实际列表,其可用于这样的,例如:

>>> a = [i*2 for i in xrange(3)] 
>>> a 
[0, 2, 4] 

(I * 2对于i中的xrange(3))是一个发生器表达。它的工作原理相当类似的方式,但并不完全,因为它不会生成一个列表或元组,而是一个发电机:

>>> a = (i*2 for i in xrange(3)) 
>>> a 
<generator object <genexpr> at 0x02CEE058> 
>>> a.next() 
0 
>>> a.next() 
2 
>>> a.next() 
4 
>>> a.next() 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
StopIteration 
>>> a = (i*2 for i in xrange(3)) 
>>> tuple(a) 
(0, 2, 4) 
>>> tuple(a) 
() 

你可以在这里找到更多的信息,如果你是好奇:generator expressions。 tl; dr版本是不能直接访问生成器(你必须要求它生成其内容,例如使用next()),并且每个值只能生成一次(然后生成器移动到下一个,因此next()函数名称)。

所以,回到你的问题。在下面的表达式中,您实际上要求使用生成器表达式生成一个元组,这本身就很好。尽管如此,你可以通过使用Foo类变量来实现,在生成器的情况下,这些变量可能会有问题。

BUCKET_LABELS = tuple("BUCKET_{}".format(int(BUCKET_SIZE * i * 100)) for i in xrange(1, NUM_BUCKETS + 1)) 

特别是中,发电机实际上并不知道Foo.BUCKET_SIZE变量在所有一旦你让他产生一个元组出来的(发电机在其范围内的作品,违背列表)。这就是你得到这个错误的原因。

所以,一种解决方案只是简单地使用列表理解(无论如何都更容易处理/直观)。

PS:十进制()函数可能不会做你认为它的作用:

>>> NUM_BUCKETS = 10 
>>> print Decimal(1.0/NUM_BUCKETS) 
0.1000000000000000055511151231257827021181583404541015625 
>>> print round(1.0/NUM_BUCKETS, 2) 
0.1 

PPS:你为什么不与x范围得到一个错误的理由(1,NUM_BUCKETS + 1)的一部分,如果你对此感到好奇,是因为它在之前被评估为,所以,该类变量实际上被其生成器的值替换......它并不抱怨它。

+2

您提出的修复会在Python 3中破坏。通常最好是在类语句内部不要使用Genexps或理解。 – user2357112

+0

啊,是的,的确,我测试了Python 2.7中的所有内容。 – Deneb

+0

谢谢!这样可行。 (另外,我知道小数点分割的东西是错误的,最新版本将其更改为'Decimal(1)/ NUM_BUCKETS',它按预期工作。) – macdjord