在您的a
类中my_hand
,other_hand
和hands
变量都是类属性(即静态属性),而不是实例属性。
同样在您的clear
方法中,您重新分配my_hand
and other_hand
,但hands
仍引用旧list
的 s,因此其内容不会改变。如果您使用的是 python3,那么您应该使用s:的clear()
方法,然后将引用两个空的 s。在 python2 上你应该做.list
my_hand.clear()
other_hand.clear()
hands
list
del my_hand[:]
如果要分配实例属性,则必须执行self.attribute = value
. 为此,应将赋值放在__init__
作为类的构造函数的方法中。
就目前您的代码而言,如果您创建两个实例,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
在两个实例之间共享。
我在这里对正在发生的事情进行更多解释,以避免写太多评论。
首先,在您的代码中,my_hand
andother_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
有两种不同的用途。该语句的完整语法是:
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]
或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])
当使用del
带有这样一个表达式的语句时,python 调用__delitem__
对象的方法,传入索引或切片。这意味着:
del numbers[:]
numbers
意思是:删除列表中与 slice 中的索引相对应的所有元素:
。由于切片:
意味着“序列中的所有索引”,结果是清空列表。请注意,它不会从列表中删除引用。它只作用于它的元素。
您可以使用以下方法获得相同的效果:
numbers[:] = []
这告诉 python 将与切片对应的元素序列替换:
为[]
. 由于:
表示“所有元素”并且[]
为空,因此效果是从列表中删除所有元素。此语法调用list.__setitem__
而不是list.__delitem__
在后台调用,但结果是相同的。但是,您也可以这样做:numbers[:] = [2]
然后numbers
将删除的所有元素并2
插入 ,从而生成 list [2]
。
两者都有效,但是我更喜欢del
语法,因为它明确了您的意图。当你阅读:
del something[:]
您知道该语句将删除某些内容。然后你会看到下标[:]
,你就会明白它会从引用中删除元素,something
而不是从引用中删除something
。然而,当你看到:
something[:] = []
首先你想,好吧,这是一个任务。然后你看到[:]
并且你明白你正在“覆盖”列表的内容。然后你看右边,看到[]
,所以我们要用一个空列表覆盖元素……最后你明白了这个语句会简单地从列表中删除所有元素。