1

練習用に OOP でカード ゲームを開発していますが、これまでに書いたものには奇妙な動作があります。. . clear メソッドを使用して手を空にすると、個々の手が空であることを示しますが、手の変数 (両手を示す) を見ると、空ではありません。私の質問は、なぜですか、笑?以下にコードを配置し、結果を以下に示します。. 事前に助けてくれてありがとう。

import random

class a:
    my_hand = []
    other_hand = []
    hands = [my_hand, other_hand]

    def h(self):
        for rounds in range(5):
            for hand in a.hands:
                hand.append(rounds)

    def shuffle(self):
        random.shuffle(a.my_hand)
        random.shuffle(a.other_hand)

    def clear(self):
        a.my_hand = []
        a.other_hand = []


x = a()
x.h()
x.shuffle()


x.hands
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]]

x.clear()
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]]

x.my_hand
[]

x.other_hand
[]

x.hands
[[1, 0, 4, 2, 3], [3, 4, 2, 0, 1]]
4

3 に答える 3

3

aクラスではmy_hand、変数はすべてクラス属性 (つまりother_hand静的属性) であり、インスタンス属性ではありません。hands

また、メソッドでandclearを再割り当てしますが、古いs を引き続き参照するため、その内容は変更されません。python3 を使用している場合は、 s:およびのメソッドを使用する必要があります。その後、2 つの空の が参照されます。python2 で行う必要があります。my_handother_handhandslistclear()listmy_hand.clear()other_hand.clear()handslistdel my_hand[:]

インスタンス属性を割り当てたい場合は、する必要がありますself.attribute = value。そのためには__init__、クラスのコンストラクターであるメソッド内に割り当てを配置する必要があります。

現在のコードでは、 の 2 つのインスタンスを作成するとa、それらはハンドを共有します。これはおそらく望ましくないことです。

正しいコードは次のようになります。

import random

class ClassesUseCamelCase(object):
    # if you are using python2 do *not* forget to inherit object.
    def __init__(self):
        self.my_hand = []
        self.other_hand = []
        self.hands = [self.my_hand, self.other_hand]

    def init_hands(self):
        for round in range(5):
            for hand in self.hands:
                hand.append(round)
        # or simpler:
        # for hand in self.hands:
        #     hand.extend(range(5))

    def shuffle(self):
        random.shuffle(self.my_hand)
        random.shuffle(self.other_hand)

    def clear(self):
        self.my_hand.clear()    # or del self.my_hand[:] in python2
        self.other_hand.clear()

iPython を使用したサンプル出力:

In [2]: inst = ClassesUseCamelCase()

In [3]: inst.init_hands()

In [4]: inst.shuffle()

In [5]: inst.hands
Out[5]: [[3, 2, 4, 0, 1], [3, 1, 4, 0, 2]]

In [6]: inst.clear()

In [7]: inst.hands
Out[7]: [[], []]

複数のインスタンスで機能することに注意してください。

In [9]: inst.init_hands()

In [10]: other = ClassesUseCamelCase()

In [11]: other.init_hands()

In [12]: inst.hands, other.hands
Out[12]: ([[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]])

In [13]: inst.shuffle(); other.shuffle()
    ...: 

In [14]: inst.hands
Out[14]: [[0, 1, 3, 2, 4], [1, 4, 0, 3, 2]]

In [15]: other.hands
Out[15]: [[1, 4, 2, 0, 3], [1, 4, 3, 2, 0]]

handsコードは2 つのインスタンス間で共有されます。


コメントを書きすぎないように、何が起こっているのかについてもう少し説明を加えます。

まず第一に、あなたのコードではmy_handother_handクラス属性です。なぜこれが重要なのですか?クラス属性はインスタンス間で共有されるため、これは重要です:

In [1]: class MyClass(object):
   ...:     my_hand = []
   ...:     other_hand = []
   ...:     hands = [my_hand, other_hand]
   ...:     

In [2]: instance_one = MyClass()

In [3]: instance_two = MyClass()

In [4]: instance_one.my_hand.append(1)

In [5]: instance_two.my_hand   # ops!
Out[5]: [1]

を呼び出すとclear、クラス属性が変更されるため、それらを使用するすべてのインスタンスが変更されます。これは通常、望ましくないことです。

インスタンスには他のインスタンス属性がありません。これは、実際にはすべてのインスタンスが等しいことを意味します。それらはすべて同じように機能し、同じデータを提供します。唯一の違いは、彼らのアイデンティティです。ID を使用しない場合、クラスと複数のインスタンスを持つことは、単純に 1 つのオブジェクトを使用することができたので、意味がありません。

インスタンスに、他のインスタンスのデータから独立した独自のデータを持たせたい場合は、インスタンス属性を使用する必要があります。

del声明について。コメントで既に述べたように、del2 つの異なる用途があります。ステートメントの完全な構文は次のとおりです。

del sequence, of, del_targets

ここで、「sequence, of, del_targets」は、私が呼ぶもののカンマ区切りのリストですdel_targets。によってdel_target挙動が変わります。

それが識別子である場合、スコープからその参照delを削除し、識別子によって参照されたオブジェクトの参照カウントを減らします。

例えば:

a = b = []
# Now that empty list has two references: a and b
del b    # now it has only one reference: a
print(b)   # This raises a NameError because b doesn't exist anymore
del a      # Now the empty list doesn't have references

オブジェクトに参照がない場合は破棄されるため、del a上記の後、空のリストはインタープリターによって破棄されます。

ターゲットは、「添え字」、つまりname[key-or-index]or name[a:slice](or name[start:stop:step]) のような式にすることもできます。スライス構文 (コロン付きのもの) は、インデックスの範囲を指定するために使用されます。

In [17]: numbers = list(range(10))  # [0, 1, ..., 9]

In [18]: numbers[::2], numbers[1::2], numbers[2:7:3]
Out[18]: ([0, 2, 4, 6, 8], [1, 3, 5, 7, 9], [2, 5])

delこのような式でステートメントを使用すると、python は__delitem__オブジェクトのメソッドを呼び出し、インデックスまたはスライスを渡します。この意味は:

del numbers[:]

numbers手段:スライス内のインデックスに対応するリストのすべての要素を削除します:。スライス:は「シーケンス内のすべてのインデックス」を意味するため、結果はリストを空にします。リストから参照が削除されないことに注意してください。それはその要素にのみ作用します。

以下を使用して同じ効果を得ることができます。

numbers[:] = []

これは、スライス:に対応する要素のシーケンスを の要素に置き換えるように python に指示し[]ます。:は「すべての要素」を意味し、空であるため[]、効果はリストからすべての要素を削除することです。この構文は内部ではlist.__setitem__なく呼び出しますlist.__delitem__が、結果は同じです。ただし、次のこともできます。numbers[:] = [2]その後、 のすべての要素numbersが削除され2挿入されて list になります[2]

どちらも機能しdelますが、意図を明確にするため、構文を好みます。あなたが読むとき:

del something[:]

ステートメントが何かを削除しようとしていることがわかります。次に、下付き文字を見て、参照ではなく[:]から要素を削除することを理解します。ただし、次の場合:somethingsomething

something[:] = []

まず、これは課題だと思います。次に が表示[:]され、リストの内容を「上書き」しようとしていることがわかります。次に、右側を見て を見て[]、要素を空のリストで上書きします...最終的に、ステートメントがリストからすべての要素を単純に削除することがわかります。

于 2013-11-09T08:39:49.000 に答える
1

OOP の演習として、コードは奇妙です...インスタンス メソッドでクラス メンバーを使用しています。メソッド内のクラス インスタンスの名前selfは ではなくaです。コードを次のように変更します

 def clear(self):
     self.hand = []
     self.other_hand = []

コードの他の部分についても同じことを行います。

ただし、直面している問題は別のものです。再割り当て時に2つの新しい空の配列を作成a.handしているため、 で作成された古い配列を指し続けます。a.other_handa.handsh

コードを次のように変更する

a.hand[:] = []
a.other_hand[:] = []

またはより慣用的に

del a.hand[:]
del a.other_hand[:]

その問題を解決します。

Python では、[:]構文は「のすべての要素」を意味し、スライスも可能です (たとえば、最初と最後を除く要素a.hand[1:-1]のリストですa.hand)。

于 2013-11-09T06:39:08.430 に答える
1

新しい空のリストを作成していますが、handsまだ古いリストを参照しています。

handsが新しい空のリストを参照していることを確認するだけです。

def clear(self):
    a.my_hand = []
    a.other_hand = []
    a.hands = [a.my_hand, a.other_hand]

または、すべての要素を削除して、リスト自体を変更することもできます。

def clear(self):
    del a.my_hand[:]
    del a.other_hand[:]

参考までに、すべての関数はメンバー メソッドとして定義されていますが、変数は常に静的です。selfの代わりに参照aするか、メソッドを静的にします ( を使用@staticmethod)。

于 2013-11-09T06:34:47.543 に答える