2016-11-22 10 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)のdupeその質問はPython 3にあり、リストの理解を使用しています。私はgenexpsを使って良い偽候補者を見ません。 – user2357112

答えて

2

まず、他の人の間で解決策は、単純にこの1行を編集して(括弧に注意してください):

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

、もしあなたがについて興味があるのなら、はPythonのように動作し、なぜバグではないのでしょうか? (3)]はリストの理解です。

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

ジェネレータ式である(iは(3)はxrangeでiについて2 *):これは、例えば次のように使用することができる実際のリストを生成します。それはむしろ、同じように動作しますが、ないまさに、それはリストやタプルを生成しないためではなく、発電機:

>>> 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()などを使用してコンテンツを生成するように要求する必要があります)、各値は1回しか生成できません次のものへ、したがってnext()関数名へ)。

だから、あなたの問題に戻ります。以下の式では、ジェネレータ式を持つタプルを生成するように実際に要求していますが、それ自体は問題ありません。それにもかかわらず、Fooのクラス変数を使用して行います。これは、ジェネレータの場合、問題になる可能性があります。特に

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

あなたは(発電機がリストに反して、その範囲に内で動作)それからタプルを生成するために彼に尋ねると、発電機は、実際には全くFoo.BUCKET_SIZE変数について知っていません。このため、このエラーが発生します。

したがって、1つの解決策は、単にリストの理解を使用することです(これは、扱いやすく、直感的です)。

PS:

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

PPS:あなたははxrangeとエラーを取得しない理由(1、NUM_BUCKETS + 1)の部分を小数()関数は、おそらくあなたはそれがないと思う何をしません。あなたがそれについて興味があれば、それはの前にのジェネレータが構築されていると評価されているので、そのクラス変数はジェネレータの値に置き換えられます。

+2

あなたの提案された修正はPython 3で壊れます。class文の中でgenexpsやcomprehensionsを全く使わないのが一般的です。 – user2357112

+0

ああ、確かに、私はPython 2.7でそれをすべてテストしました。 – Deneb

+0

ありがとう!それは動作します。 (また、小数除算が間違っていることを知っています。最新バージョンでは、期待通りに動作する 'Decimal(1)/ NUM_BUCKETS'に変更されています) – macdjord

関連する問題