【问题标题】:Accessing field: different value if its stored in a dict访问字段:如果存储在字典中,则值不同
【发布时间】:2015-11-02 01:56:38
【问题描述】:

我的问题看起来像这样。

# Module.py
class TestClass:
    name = "default"
    nameDict = {'name':"standard"}

    def __init__(self, name):
       self.name = name
       self.nameDict['name'] = name

然后我调用另一个模块的字段。该类是之前启动的,我像这样调用名称字段:

Module.TestClass("newName")

# Inside another function
print(Module.TestClass.name)
print(Module.TestClass.nameDict['name'])

这给出了:

default #name
newName #nameDict

我不明白为什么会有不同的值。有谁知道为什么会发生这种情况?

感谢您的宝贵时间。

【问题讨论】:

    标签: python dictionary scope


    【解决方案1】:

    这是一个有趣的问题;发生了很多事情。

    Module.TestClass 是您在“Module.py”中定义的类。使用Module.TestClass("newName"),您正在创建该类的实例,但您没有保存它。

    当您执行print(Module.TestClass.name) 时,您正在打印Module.TestClassname 属性的值,它是不是 实例。你之前给 instancename 不会改变 class' name 属性。

    真正有趣的是print(Module.TestClass.nameDict['name'])。即使您仍在访问 classnameDict 属性,它 也受到您之前创建的 instance 的影响。这是因为 Python 变量包含对对象的引用。定义类时,您将字典分配给类范围内的属性nameDict完全相同字典对象正被 和所有类的实例所指向。所以,如果你在一个地方改变它,它就会在任何地方都改变,因为它实际上是完全相同的字典

    这是一个很好的方法来做你想做的事情:

    # Module.py
    
    class TestClass(object):
        def __init__(self, name='default'):
            self.name = name
            self.nameDict = {'name': name}
    
    # other.py
    
    testclass_instance = Module.TestClass("newName")
    
    # Inside another function
    print(testclass_instance.name)  # newName
    print(testclass_instance.nameDict['name'])  # newName
    

    【讨论】:

    • 感谢您的精彩回答!我想我应该用一个实例来调用它,但我试图不这样做,因为我是如何编写代码的。我会重写它!
    • 很高兴我能帮上忙 :)。在这种情况下,您能否接受以便将问题标记为已回答?
    【解决方案2】:

    你看到的不同是因为 -

    1. 字符串是不可变的,而字典是可变的。

    2. 1234563
    3. 在您的 init 函数中,当您更改字典中 name 键的值时,您正在改变字典(与类变量相同的字典),因此它在类变量字典中反映为好吧。如果你在这里分配了一个新的字典,你仍然可以在类变量中看到旧的值。

    这基本上是因为当你创建一个实例时,类变量的引用被复制到实例中,引用仍然指向当时指向的任何类变量。因为在这种情况下,由于引用相同,所以对 dict 的更改会反映在类变量中。

    但是当你进行赋值时,你是在让变量指向一个新的引用,这不会导致类变量发生变化。

    一个简单的例子来说明这一点 -

    >>> class CA:
    ...     d = [1,2]
    ...
    >>> CA.d
    [1, 2]
    >>> c = CA()
    >>> c.d
    [1, 2]
    >>> id(c.d) == id(CA.d)
    True
    >>> c.d.append(3)
    >>> CA.d
    [1, 2, 3]
    >>> c.d = [1,2,3,4,5]
    >>> CA.d
    [1, 2, 3]
    >>> CA.d = [1,2,3,4,5,6]
    >>> CA.d
    [1, 2, 3, 4, 5, 6]
    >>> d = CA()
    >>> d.d
    [1, 2, 3, 4, 5, 6]
    >>> c.d
    [1, 2, 3, 4, 5]
    

    【讨论】:

    • 不知道将类变量复制到实例。这对我来说是关键的缺失点。谢谢。
    【解决方案3】:

    这种行为也让我感到惊讶:

    import Module
    
    # Inside another function
    print(Module.TestClass.name)             #<-- default
    print(Module.TestClass.nameDict['name']) #<-- standard
    
    foo = Module.TestClass("newName")
    
    print(Module.TestClass.name)             #<-- default
    print(Module.TestClass.nameDict['name']) #<-- newName
    

    你为什么要类变量?也许你想做:

    foo = Module.TestClass("newName")
    
    print(foo.name)
    print(foo.nameDict['name'])
    

    【讨论】:

    • 类和实例都共享同一个字典。这就是为什么仅仅创建一个实例会改变类的字典;它们实际上是同一个对象。
    • 感谢您的解释。 Python 提供了一大堆这样的陷阱。这对我来说更像是一种爱恨交织。
    【解决方案4】:

    因为TestClass.name是一个字符串对象,所以是不可变的,而TestClass.nameDict是一个可变字典对象。因此,当您执行self.name = name 时,会在具有“newName”值的实例上创建一个新名称变量,而当您执行self.nameDict['name'] = name 时,dict 类变量会发生突变。

    要验证此行为,您可以检查变量的 id:

    例子:-

    class TestClass(object):
    
        ...
    
        def __init__(self, newName):
            print(id(self.name)) #1
            self.name = newName
            print(id(self.name)) #2
    
            print(id(self.nameDict)) #3
            self.nameDict['name'] = newName
            print(id(self.nameDict)) #4
    

    1 与#2 不同,而#3 和#4 相同。

    更多参考:-Mutable and immutable objects in python

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-06-12
      • 2023-03-03
      • 1970-01-01
      • 2021-07-25
      • 1970-01-01
      • 2010-12-14
      • 2017-02-18
      • 2022-11-22
      相关资源
      最近更新 更多