我正在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]]
答案 0 :(得分:3)
在a
课程中,my_hand
,other_hand
和hands
变量都是 class 属性(即 static > attributes),而不是 instance 属性。
同样在您的clear
方法中,您重新分配了my_hand
和other_hand
,但hands
仍然引用了旧的list
,因此其内容并非如此改变。如果您使用的是python3,则应使用clear()
的{{1}}方法:list
和my_hand.clear()
,然后other_hand.clear()
将引用两个空hands
}}秒。
在python2上,你应该list
。
如果要分配实例属性,则必须执行del my_hand[:]
。
为了做到这一点,应该将赋值放在self.attribute = value
方法中,该方法是类的构造函数。
由于它目前代表您的代码,如果您创建两个__init__
实例,那么它们将共享手,这可能是您不想要的。
正确的代码是:
a
使用iPython输出示例:
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()
请注意,它适用于多个实例:
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
和my_hand
是类属性。为什么这很重要?这很重要,因为类属性在实例之间共享:
other_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
声明。正如我在评论中已经说过del
有两种不同的用法。该语句的完整语法为:
del
其中“sequence,of,del_targets”是逗号分隔的列表,我将其称为del 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]
(或name[a:slice]
)之类的表达式。
切片语法(带冒号的语法)用于指定索引范围:
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])
语句时,python调用对象的del
方法,传入索引或切片。这意味着:
__delitem__
表示:删除与切片del numbers[:]
中的索引对应的列表numbers
的所有元素。由于切片:
表示“序列中的所有索引”,因此结果将清空列表。请注意,它不从列表中删除引用。它只对其元素起作用。
您可以使用以下方式获得相同的效果:
:
这告诉python用numbers[:] = []
中的元素替换对应于切片:
的元素序列。由于[]
表示“所有元素”且:
为空,因此效果是从列表中删除所有元素。
此语法在引擎盖下调用[]
而不是list.__setitem__
,但结果是相同的。
不过您也可以这样做:list.__delitem__
然后numbers[:] = [2]
的所有元素都将被移除并将插入numbers
,从而生成列表2
}。
两者都有效,但我更喜欢[2]
语法,因为它明确了你的意图。
当你读到:
del
您知道该声明将删除某些内容。然后,您会看到下标del something[:]
,并且您了解它将从[:]
而不是参考something
中删除元素。但是当你看到:
something
首先你想,好吧这是一个任务。然后你看到something[:] = []
并且你明白你正在“覆盖”列表的内容。然后你看右边看[:]
,所以我们要用空列表覆盖元素......最后你明白语句将只删除列表中的所有元素。
答案 1 :(得分: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
)。
答案 2 :(得分:1)
作为OOP中的练习,您的代码很奇怪......您在实例方法中使用了类成员。方法中的类实例名为self
,而不是a
。将您的代码更改为
def clear(self):
self.hand = []
self.other_hand = []
并对代码的其他部分执行相同的操作。
您遇到的问题是另一个问题:重新分配a.hand
和a.other_hand
时,您正在创建两个新的空数组,因此a.hands
会一直指向{{1}中创建的旧数组}}
将代码更改为
h
或更惯用的
a.hand[:] = []
a.other_hand[:] = []
将解决这个问题。
在Python中,del a.hand[:]
del a.other_hand[:]
语法表示“所有元素”并允许切片(例如[:]
是a.hand[1:-1]
中除了第一个和最后一个元素之外的元素列表。