7

以下は、私の部分的なクラス定義です。

class Trial:
    font = pygame.font.Font(None, font_size)
    target_dic = {let: font.render(let, True, WHITE, BG) for let in list("ABCDEFGHJKLMNPRSTUVWX")}

部分クラス定義の最後の行でtarget_dic = {let: font.render(let, True, WHITE, BG) for let in list("ABCDEFGHJKLMNPRSTUVWX")エラーが返されます: グローバル名 'font' が定義されていません。けっこうだ。

ただし、次のテスト ケースを試してみましたが、エラーは発生しませんでした。

class x:
    dat = 1
    datlist = [dat for i in range(10)]

最初のケースが機能しないのはなぜですか? font辞書理解に至るまでにメンバは存在しないのですか?

これらの操作をに移動する必要があります__init__か、またはクラス オブジェクトの作成時にリストを 1 回だけ定義することは可能ですか?

編集:

わかりやすくするために、Trial オブジェクトの作成に費やす時間を削減するために、クラス オブジェクトの作成時にリストを作成できるようにしたいと考えています。

4

3 に答える 3

4

いくつかの間違った道をたどるほうがよいので、部分的な答えです。

私があなたの作業サンプルを取り戻し、辞書内包表記を入れると:

class x:
    dat = 1
    datlist = {i:dat for i in range(10)}

私もこれを取得します:

>>> NameError: global name 'dat' is not defined

したがって、辞書内包表記は、クラスステートメントの実行中に一時的な辞書の使用を隠しているように見えますが、リスト内包表記はそうではありません。

これに関するドキュメントには、これ以上の情報はありません...

@interjay コメントに基づいて編集: クラス構築がスコープ基準を満たさない場合は、この投稿で対処します。簡単に言うと、2.x ではリスト内包表記にバグがあり、クラス メンバーが表示されますが、表示されるべきではありません。

于 2012-05-28T17:06:28.670 に答える
1

クラス デコレータはクラスが作成された後に呼び出されるため、クラス デコレータを使用して、クラス本体でクラス属性を参照するという制限を回避し、作成したばかりのクラスを効果的に「後処理」して、欠落している属性を追加することができます。

def trial_deco(cls):
    cls.target_dic = {let: cls.font.render(let, True, WHITE, BG) for let in "ABCDEFGHJKLMNPRSTUVWXZ"}
    return cls

@trial_deco
class Trial:
    font = pygame.font.Font(None, font_size)
    target_dic = None  # redefined by decorator

別の方法は、属性を最初にデータ記述子 (別名「プロパティ」) にすることです。これは、属性が最初に読み取られたときに (のみ) 呼び出されます。これは、ディスクリプタを計算された値で上書きすることによって実現されるため、一度だけ発生します。

class AutoAttr(object):
    """ data descriptor for just-in-time generation of instance attribute """
    def __init__(self, name, font, antialias, color, background):
        self.data = name, font, antialias, color, background

    def __get__(self, obj, cls=None):
        name, font, antialias, color, background = self.data
        setattr(obj, name, {let: font.render(let, antialias, color, background)
                                for let in "ABCDEFGHJKLMNPRSTUVWXZ"})
        return getattr(obj, name)

class Trial(object):
    font = pygame.font.Font(None, fontsize)    
    target_dic = AutoAttr('target_dic', font, TRUE, WHITE, BG)

メタクラスを使用したり、__new__メソッドを定義したりするなど、このようなことを行う他の方法もありますが、どれもうまく機能するか、複雑さが軽減されるかは疑わしいです。

于 2012-05-28T17:45:10.620 に答える
-1

クラスの作成時に使用できる名前はグローバル変数であり、クラスのトップ レベルで既に定義されている名前です (つまり、名前が定義されると、クラス コードのさらに下で使用できるようになります)。

クラス レベルのオブジェクトは、インスタンスを介して取得したり、クラス オブジェクトを介して取得したりできない場合があることに注意してください。最も顕著なのは、クラス作成中、 a の結果はdefメソッドではなく関数のままです。

OPのコードに関連して、この例はfontクラスレベルで定義されることを示しています:

class Trial:
    font = 'foo'
    target_dic = dict((lambda fnt:((let,fnt) for let in "ABCDEFGHJKLMNPRSTUVWX"))(font))
    target_two = []
    for let in "ABCDEFGHJKLMNPRSTUVWX":
        target_two.append(let)

print(Trial.target_dic)
print(Trial.target_two)

これにより、次の結果が得られます。

{'A': 'foo', 'C': 'foo', 'B': 'foo', 'E': 'foo', 'D': 'foo', 'G': 'foo', 'F': 'foo', 'H': 'foo', 'K': 'foo', 'J': 'foo', 'M': 'foo', 'L': 'foo', 'N': 'foo', 'P': 'foo', 'S': 'foo', 'R': 'foo', 'U': 'foo', 'T': 'foo', 'W': 'foo', 'V': 'foo', 'X': 'foo'}
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'X']

(http://ideone.com/aC7VY)

font期待どおり、ラムダに渡すことができます。

問題は、クラス スコープで使用可能な名前ではありません ( fontis available there )。問題は、内包表記が独自の新しいスコープを導入することです (python 3): http://docs.python.org/py3k/reference/expressions.html#displays-for-lists-sets-and-dictionaries

于 2012-05-28T16:42:24.817 に答える