调试 instance/class 属性

Debug the instance/class attribute

我正在尝试让以下代码按下面评论中的预期工作。例如,我将使用以下命令创建三个 Dog 实例(狗:buddy、pascal 和 kimber)。但是,当 运行 buddy.teach('sit') 时,它会向 buddy 和 pascal 实例添加 'sit' 技巧。您能否操纵代码,以便为实例对象而不是 class 对象定义属性?

请只编辑代码(不要更改下面评论中的命令)。

class Dog:
    def __init__(self, name, tricks=set()):
        self.name = name
        self.tricks = tricks
    def teach(self, trick):
        self.tricks.add(trick)
# Change the broken code above so that the following lines work:
# 
# buddy = Dog('Buddy')
# pascal = Dog('Pascal')
# kimber = Dog('Kimber', tricks={'lie down', 'shake'})
# buddy.teach('sit')
# pascal.teach('fetch')
# buddy.teach('roll over')
# kimber.teach('fetch')
# print(buddy.tricks)  # {'sit', 'roll over'}
# print(pascal.tricks)  # {'fetch'}
# print(kimber.tricks)  # {'lie down', 'shake', 'fetch'}

给你:

class Dog:
    def __init__(self, name, tricks=set()):
        self.name = name
        self.tricks = set(tricks)
    def teach(self, trick):
        self.tricks.add(trick)

您的 __init()__ 具有以下定义:

def __init__(self, name, tricks=set()):

set() 是一个对象,它为 class 定义 实例化一次并在每个实例中共享。所以每个使用默认 tricks 的实例都共享相同的技巧集(!)。

为了防止这种情况,常见的模式是:

class Dog:
    def __init__(self, name, tricks=None):
        self.name = name
        self.tricks = set()
        if tricks:
            self.tricks = tricks
    def teach(self, trick):
        self.tricks.add(trick)

问题出在 __init__ 中的默认参数 tricks=set()

默认参数仅在定义 function/method 时计算一次。因此,创建了一个空的 set,每当调用 __init__ 而不指定 tricks.

时,它将用作默认参数

当你执行buddy = Dog('Buddy')时,这个空集被用作tricks,而在self.tricks = tricks中,你使buddytricks属性引用

稍后,您再次执行 pascal = Dog('Pascal'),但未指定 trick,因此使用作为默认参数创建的同一集合,以及 pascaltrick 属性也会参考。

因此,两个实例共享同一组技巧。

如果在创建实例时为 tricks 传递一个值就没有问题:它将是一个独立的集合。 如果此默认值不可变,也不会有任何问题(请参阅 “Least Astonishment” and the Mutable Default Argument.

为了避免使用可变的默认参数,经典的解决方案如下:

 class Dog:
    def __init__(self, name, tricks=None):
        if tricks is None:
            tricks = set()
        self.name = name
        self.tricks = tricks
        
    def teach(self, trick):
        self.tricks.add(trick)
        
buddy = Dog('Buddy')
pascal = Dog('Pascal')
kimber = Dog('Kimber', tricks={'lie down', 'shake'})
buddy.teach('sit')
pascal.teach('fetch')
buddy.teach('roll over')
kimber.teach('fetch')
print(buddy.tricks)  # {'sit', 'roll over'}
print(pascal.tricks)  # {'fetch'}
print(kimber.tricks)  # {'lie down', 'shake', 'fetch'}

输出:

{'roll over', 'sit'}
{'fetch'}
{'lie down', 'fetch', 'shake'}