Python - class 属性的意外行为
Python - Unexpected behaviour of class attributes
用简单的文字编辑
代码:
class temp:
attr1 = 0
attr2 = []
t1 = temp()
t2 = temp()
t1.attr1 = 50
t1.attr2.append(50)
print(t1.attr1)
print(t1.attr2)
print(t2.attr1)
print(t2.attr2)
输出:
50
[50]
0
[50]
我只在 attr2
对象 t1
上调用了 append
,但是 append
改变了两个对象的 attr2
。如果 attr2
是共享的(class 属性)那么为什么 attr1
值对于 t1
和 t2
是不同的。是什么导致了这种意外行为?
老问题
我正在为二十一点编写 python 代码。我写的代码如下
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.cards)
print(p1.cards)
dealer.hit()
print(dealer.cards)
print(p1.cards)
print(dealer.total)
print(p1.total)
Game = False
这段代码输出结果如下
Welcome to BlackJack ....
Enter the amount you currrently have for the game55
[]
[]
[45]
[45]
6
0
如您所见,我只在 dealer
对象上调用了一次 hit()
,但它将它附加到 dealer
和 [=] 的 cards
属性29=] 对象。但是 total
属性不同。谁能解释是什么导致了这种意外行为?
我明白你的问题了。您需要将所有卡片与玩家卡片区分开来。因此,与其将所有内容都命名为卡片,我建议这样做:
class Player:
all_cards = []
total = 0
amount = 0
并将 __init__
更新为:
def __init__(self, money=0):
self.amount = money
self.player_cards = []
在进行附加操作时,将其附加到all_cards
和player_cards
。无论如何,你只打印球员卡,你可以看到不同的卡列表。
完整代码如下:
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
all_cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.player_cards = []
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.player_cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.player_cards.append(no)
self.all_cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.player_cards)
print(p1.player_cards)
dealer.hit()
print(dealer.player_cards)
print(p1.player_cards)
print(dealer.total)
print(p1.total)
Game = False
发生这种情况是因为列表是一个可变对象,它仅在定义 class 时创建一次,这就是为什么当您创建两个实例时它会共享。因此,要解决这个问题,我们可以像我上面提到的那样使用构造函数。当我们将列表放在构造函数中时,每当对象被实例化时,新列表也会被创建。
当您执行 t1.attr1 = 50
时,您将 attr1
重新绑定到 t1
对象的属性命名空间中的新值。它以前允许您访问 class 命名空间中绑定的值,但是当您绑定一个新值时,您会从 class 中隐藏该值(仅针对该实例)。
相比之下,当您执行 t1.attr2.append(50)
时,您正在改变现有列表(它绑定在 class 命名空间中,但在所有实例中都可见),没有重新绑定变量的发生。这就是您在 t2
中看到变化的原因。变量 t1.attr2
和 t2.attr2
都是对同一对象的引用(您可以使用 is
运算符验证:t1.attr2 is t2.attr2
)。
一般来说,如果您不希望所有实例共享这些变量,那么为 class 变量使用列表或其他可变值通常不是一个好主意。但这并不被禁止,因为有时您确实确实想要共享行为。
用简单的文字编辑
代码:
class temp:
attr1 = 0
attr2 = []
t1 = temp()
t2 = temp()
t1.attr1 = 50
t1.attr2.append(50)
print(t1.attr1)
print(t1.attr2)
print(t2.attr1)
print(t2.attr2)
输出:
50
[50]
0
[50]
我只在 attr2
对象 t1
上调用了 append
,但是 append
改变了两个对象的 attr2
。如果 attr2
是共享的(class 属性)那么为什么 attr1
值对于 t1
和 t2
是不同的。是什么导致了这种意外行为?
老问题
我正在为二十一点编写 python 代码。我写的代码如下
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.cards)
print(p1.cards)
dealer.hit()
print(dealer.cards)
print(p1.cards)
print(dealer.total)
print(p1.total)
Game = False
这段代码输出结果如下
Welcome to BlackJack ....
Enter the amount you currrently have for the game55
[]
[]
[45]
[45]
6
0
如您所见,我只在 dealer
对象上调用了一次 hit()
,但它将它附加到 dealer
和 [=] 的 cards
属性29=] 对象。但是 total
属性不同。谁能解释是什么导致了这种意外行为?
我明白你的问题了。您需要将所有卡片与玩家卡片区分开来。因此,与其将所有内容都命名为卡片,我建议这样做:
class Player:
all_cards = []
total = 0
amount = 0
并将 __init__
更新为:
def __init__(self, money=0):
self.amount = money
self.player_cards = []
在进行附加操作时,将其附加到all_cards
和player_cards
。无论如何,你只打印球员卡,你可以看到不同的卡列表。
完整代码如下:
from random import randint
from IPython.display import clear_output
deck = ["S","D","C","H"]
class Player:
all_cards = []
total = 0
amount = 0
def __init__(self,money=0):
self.player_cards = []
self.amount = money
def busted(self):
return self.total > 21
def showCards(self):
for i in self.player_cards:
print("| {}{} |".format(i%13,deck[i//13]),end = " ")
print()
def hit(self):
no = randint(1,53)
self.player_cards.append(no)
self.all_cards.append(no)
if no % 13 == 1:
if self.total + 11 > 21:
self.total+=1
else:
self.total+=11
else:
self.total += (no%13 if no%13 <= 10 else 10)
dealer = Player(10000)
p1 = Player(0)
print("Welcome to BlackJack ....")
while True:
try:
p1.amount = int(input("Enter the amount you currrently have for the game"))
except:
print("invalid Value")
continue
else:
break
Game = True
while Game:
print(dealer.player_cards)
print(p1.player_cards)
dealer.hit()
print(dealer.player_cards)
print(p1.player_cards)
print(dealer.total)
print(p1.total)
Game = False
发生这种情况是因为列表是一个可变对象,它仅在定义 class 时创建一次,这就是为什么当您创建两个实例时它会共享。因此,要解决这个问题,我们可以像我上面提到的那样使用构造函数。当我们将列表放在构造函数中时,每当对象被实例化时,新列表也会被创建。
当您执行 t1.attr1 = 50
时,您将 attr1
重新绑定到 t1
对象的属性命名空间中的新值。它以前允许您访问 class 命名空间中绑定的值,但是当您绑定一个新值时,您会从 class 中隐藏该值(仅针对该实例)。
相比之下,当您执行 t1.attr2.append(50)
时,您正在改变现有列表(它绑定在 class 命名空间中,但在所有实例中都可见),没有重新绑定变量的发生。这就是您在 t2
中看到变化的原因。变量 t1.attr2
和 t2.attr2
都是对同一对象的引用(您可以使用 is
运算符验证:t1.attr2 is t2.attr2
)。
一般来说,如果您不希望所有实例共享这些变量,那么为 class 变量使用列表或其他可变值通常不是一个好主意。但这并不被禁止,因为有时您确实确实想要共享行为。