Python 访问字段:如果存储在dict中,则为不同的值

Python 访问字段:如果存储在dict中,则为不同的值,python,dictionary,scope,Python,Dictionary,Scope,我的问题看起来像这样 # Module.py class TestClass: name = "default" nameDict = {'name':"standard"} def __init__(self, name): self.name = name self.nameDict['name'] = name 然后我从另一个模块调用字段。该类在之前启动,我调用name字段,如下所示: Module.TestClass("newNam

我的问题看起来像这样

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

    def __init__(self, name):
       self.name = name
       self.nameDict['name'] = name
然后我从另一个模块调用字段。该类在之前启动,我调用name字段,如下所示:

Module.TestClass("newName")

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

default #name
newName #nameDict
我不明白为什么会有不同的价值观。有人知道为什么会这样吗


谢谢您的时间。

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

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

你所看到的差异是因为——

  • 字符串是不可变的,而字典是可变的

  • 在init函数中,如果执行-self.name=name,则会在实例中为name创建一个新的引用,该引用不会更改类变量name所持有的引用

  • 在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]
    

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

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

    当您打印(Module.TestClass.name)时,您正在打印
    Module.TestClass
    name
    属性的值,该属性是类,不是该类的实例。您之前给出实例的
    name
    不会更改类的“
    name
    属性

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

    下面是一个很好的方法来完成您想要完成的事情:

    # 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
    

    这是因为
    TestClass.name
    是一个字符串对象,因此是不可变的,而
    TestClass.nameDict
    是一个可变的字典对象。因此,当您执行
    self.name=name
    操作时,将在实例上创建一个具有“newName”值的新name变量,而当您执行
    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将相同。
    进一步参考:-

    类和实例都共享相同的dict。这就是为什么仅仅创建一个实例就会更改类的dict;它们实际上是同一个物体。谢谢你的解释。Python提供了一大堆这样的陷阱。对我来说,这更像是一种爱恨。谢谢你的回答!我想我应该用一个实例来调用它,但我试图不这样做,因为我是如何编写代码的。我会重写它!很高兴我能帮忙:)。在这种情况下,您是否可以接受,以便将问题标记为已回答?不知道如何将类变量复制到实例。这对我来说是关键的缺失点。非常感谢。
    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