python奇怪的赋值行为,真的不理解啊!!!

问题描述

1、a变量为什么可以影响dict的值,a的内存分布看起来也不连续。

2、dict值为什么是嵌套形式

以下代码和结果均是完整数据:

 class test:
        def __init__(self):
            self.dict = {}
        def cs(self):
            a = self.dict
            print("a--->",id(a), a,"dict --->",id(self.dict),self.dict)
            for i in "糟老头子坏的很":
                a = a.setdefault(i,{})
                print("a--->",id(a), a,"dict --->",id(self.dict),self.dict)
    test().cs()

python奇怪的赋值行为,真的不理解啊!!!

个人预期

我预期的结果:

a---> 2775587132480 {} dict ---> 2775587132480 {}
a---> 2775592755648 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592757184 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592763520 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592763712 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592763776 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592763904 {} dict ---> 2775587132480 {'糟': {}}
a---> 2775592716288 {} dict ---> 2775587132480 {'糟': {}}

理解的执行步骤:
1、在内存里申请一个地址2775587132480(之后用后四位简称),用于存放{}。
2、然后dict和a通过=号先后进行赋值,把两个变量指向地址2408
3、a.setdefault(i,{})循环第一次的时候将键值对“’糟’: {}”存放到地址2408中,此时a和dict变量的值应该都是{‘糟’: {}}。
4、问题的关键在于a = a.setdefault(i,{})的赋值行为。”a.setdefault(i,{})”整体返回的结果是设置的默认值”{}”。也就是”a = a.setdefault(i,{})” 赋值结果等价于 “a = {}”,在内存中的操作是再申请一个字典对象的内存首地址5648,然后将a变量的2480地址指向新的地址5648。到这我认为没啥问题,但后面a和dict的地址不一样了,操作a的值为啥能影响到dict的值,并且dict还是以图中嵌套的形式,而不是{‘糟’: {},’老’:{}…}并联层级。
向诸位请教一下,有好的猜想,解释或者方法望不吝赐教,在此衷心感谢各位,确实想了一天没想明白,原生的方法我没办法调试,只能网上求助了。

Jason990420
1年前 评论
JinBB 1年前
huanhaifeihua (楼主) 1年前
huanhaifeihua (楼主) 1年前
讨论数量: 14
Jason990420
1年前 评论
JinBB 1年前
huanhaifeihua (楼主) 1年前
huanhaifeihua (楼主) 1年前
 class test:
        def __init__(self):
            self.dict = {}
        def cs(self):
            a = self.dict
            print("a--->",id(a), a,"dict --->",id(self.dict),self.dict)
            for ii,i in  enumerate("糟老头子坏的很"):
                a = a.setdefault(i,{"index":ii})
                print("a--->",id(a), a,"dict --->",id(self.dict),self.dict)
    test().cs()
1年前 评论
huanhaifeihua (楼主) 1年前

稍微改了下代码:

class test:
    def __init__(self):
        self.dict = {}

    def cs(self):
        a = self.dict
        print("a--->", id(a), a, "dict --->", id(self.dict), self.dict)
        for i in "糟老头子坏的很":
            b = a.setdefault(i, {})
            print("b--->", id(b), b, "a--->", id(a), a, "dict --->", id(self.dict), self.dict)


test().cs()

得到以下结果:

file

1年前 评论
huanhaifeihua (楼主) 1年前
Trace92 (作者) 1年前

我两说的是一回事,最大字典dict的首地址,我想表达的类似链表结构里第一个地址,b引用了汉字对应键值对里的value地址,但因为我的例子里默认值是个新的字典,所以我以汉字开头的字典代替了。我尝试过打印嵌套层里的地址,但我是引用到同一个值,打印不出来正确的结果,我其实想搞懂的是a = a.setdefault(i,{})实际的操作流程。b = a.setdefault(i,{})这就是一个b = {}引用,a赋值的过程,这个我是理解的。

1年前 评论
Trace92 1年前
huanhaifeihua (作者) (楼主) 1年前

@huanhaifeihua 运行下这段代码,打印的a,a对应的汉字,a的内存地址,self.dict各层的内存地址,你对比下:

class test:
    def __init__(self):
        self.dict_items = []
        self.dict = {}

    def cs(self):
        a = self.dict
        print("a--->", id(a), a, "dict --->", id(self.dict), self.dict)
        for k, i in enumerate("糟老头子坏的很"):
            a = a.setdefault(i, {})
            print("a--->", i, id(a), a, "dict --->", id(self.dict), self.dict)

        self.get_dict_items(self.dict)
        print("dict--->", self.dict)
        print("dict_items--->",self.dict_items)

    def get_dict_items(self, d):
        for k, v in d.items():
            if isinstance(v, dict):
                self.get_dict_items(v)
                self.dict_items.append({k: id(v)})


test().cs()
1年前 评论
huanhaifeihua (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!