【问题标题】:Python class's dictionary's value is changing when assigned to a different variable当分配给不同的变量时,Python 类的字典的值会发生变化
【发布时间】:2020-11-17 03:21:58
【问题描述】:

我是 Python 新手,我有一个类似的类:

class UserInfo():
    def __init__(self):
        self.level = 1
        self.type = 'A'
        self.info = {
            'name ' : 'John',
            'age ' : 20,
            'gender  ' : 'Male',
            'job' : 'student',
        }

    def getInfo(self):
        return self.info

等等……

然后我创建一个类的实例:

ui = UserInfo()

# display original class info
print(ui.getInfo())
# {
#    'name ' : 'John',
#    'age ' : 20,
#    'gender  ' : 'Male',
#    'job' : 'student',
# }

myinfo = ui.getInfo()
# change "myinfo"
myinfo.update({
    'name ' : 'May',
    'age ' : 18,
    'gender  ' : 'Female',
    'job' : 'lecturer',
})

但是当我再次尝试显示原始班级信息时,我得到:

print(ui.getInfo())
# {
#   'name ' : 'May',
#   'age ' : 18,
#   'gender  ' : 'Female',
#   'job' : 'lecturer',
# }

我期待的是

print(myinfo)
# {
#   'name ' : 'May',
#   'age ' : 18,
#   'gender  ' : 'Female',
#   'job' : 'lecturer',
# }

print(ui.getInfo())
# {
#    'name ' : 'John',
#    'age ' : 20,
#    'gender  ' : 'Male',
#    'job' : 'student',
# }

为什么我更新myinfo 但它也改变了类的值? 如何避免这种情况?我只想修改myinfo


编辑 1:

感谢大家回答了我的问题。 我刚刚尝试使用return self.info.copy() 当我尝试做myinfo.update

时它工作正常

但是现在我有另一个问题,当我的字典包含另一个字典时,如果我尝试编辑里面的字典,它就不能再工作了:

class UserInfo():
        def __init__(self):
            self.level = 1
            self.type = 'A'
            self.info = {
                'name ' : 'John',
                'age ' : 20,
                'gender  ' : 'Male',
                'job' : 'student',
                'favorite' : {
                    'food' : 'pizza',
                    'sport' : 'badminton',
                    'game' : 'none',
                },
            }

        def getInfo(self):
            return self.info.copy()
    ui = UserInfo()
    print(ui.getInfo())
    # {
    #   'name ': 'John',
    #   'age ': 20,
    #   'gender  ': 'Male',
    #   'job': 'student',
    #   'favorite': {
    #       'food': 'pizza',
    #       'sport': 'badminton',
    #       'game': 'none'
    #   }
    #}

    myinfo = ui.getInfo()
    myinfo['favorite'].update({'game' : 'mobile'})
    print(ui.getInfo())
    #   {
    #     'name ': 'John',
    #     'age ': 20,
    #     'gender  ': 'Male',
    #     'job': 'student',
    #     'favorite': {
    #       'food': 'pizza',
    #       'sport': 'badminton',
    #       'game': 'mobile'
    #     }
    #   }

【问题讨论】:

  • ... 因为update 就地改变了字典,而您的实例属性info 仍然指的是同一个字典对象。你可以把getInfo 写成return self.info.copy()
  • 感谢您的回复。当我做myinfo.update 时,它与返回.copy() 工作正常,但是当字典包含另一个字典时,它不再工作(我已经编辑了我的帖子)
  • 因为在这种情况下您需要创建一个深拷贝。最好的解决方案是不要围绕这样的可变状态设计程序
  • @juanpa.arrivillaga 哦,我明白了,我会再次查看我的代码,非常感谢您的回复!

标签: python function class return


【解决方案1】:

你只复制返回的字典怎么样?

myInfo = ui.getInfo()

编辑

要回答您的后续问题,

当您使用复制时,它会返回具有相同对象的不同字典。这意味着如果您更改字典 B 的元素 A,它是一个可变对象,例如字典,那么字典 A 在 B 和 B 的副本中都会发生变化。

为避免这种情况,您需要一个同时复制所有元素的副本。在 python 中有一种方法可以做到这一点:deepcopy()。

因此,为了获得所需的结果,请执行以下操作:

myInfo = ui.getInfo().deepcopy()

【讨论】:

  • 或与myinfo = ui.getInfo().copy()一步完成
【解决方案2】:

发生的情况是,当您返回 self.info 时,您并没有返回 self.info 中字典的 ,您实际上返回的是名称为 self.info 的字典对象本身指着。更改新变量的原因也会更改类对象中的信息,因为该变量现在只是 self.info 的另一个名称。您可以通过返回字典的副本来解决此问题,这将创建一个与原始字典分开的新字典对象。这可以通过copy() 方法完成,该方法返回字典的新副本,如下所示:

class UserInfo():
    def __init__(self):
        self.level = 1
        self.type = 'A'
        self.info = {
            'name ' : 'John',
            'age ' : 20,
            'gender  ' : 'Male',
            'job' : 'student',
        }

    def getInfo(self):
        return self.info.copy()

【讨论】:

  • 感谢您的回复。当我做myinfo.update 时,它与返回.copy() 工作正常,但是当字典包含另一个字典时,它不再工作(我已经编辑了我的帖子)
【解决方案3】:

防止这种情况发生的一种方法是让 getInfo 返回 info 的副本而不是 info 本身,另一种方法(如 Green05 的回答所述)是复制 getInfo 的结果。

第一个解决方案:

class UserInfo():
    def __init__(self):
        self.level = 1
        self.type = 'A'
        self.info = {
            'name ' : 'John',
            'age ' : 20,
            'gender  ' : 'Male',
            'job' : 'student',
        }

    def getInfo(self):
        return self.info.copy()

第二种解决方案: myInfo = ui.getInfo().copy()

发生这种情况的原因是因为 Python 允许在外部更改类中的属性,而不管它们是否被别名为其他东西。 ui.info 是 myInfo 的别名,所以更改 myInfo 实际上只是更改 ui.info。

【讨论】:

  • 感谢您的回复。当我做myinfo.update 时,它可以正常使用返回.copy(),但是当字典包含另一个字典时,它不再工作(我已经编辑了我的帖子)
【解决方案4】:

Python 字典是可变的。可变对象在 Python 中通过引用传递,因此当您更新引用时,更改会反映在原始对象中。您可以通过创建对象的副本来解决此问题。

import copy

ui = UserInfo()
myinfo = copy.copy(ui.getInfo())

myinfo.update({
    'name ' : 'May',
    'age ' : 18,
    'gender  ' : 'Female',
    'job' : 'lecturer',
})

print(myinfo)
# {
#   'name ' : 'May',
#   'age ' : 18,
#   'gender  ' : 'Female',
#   'job' : 'lecturer',
# }

print(ui.getInfo())
# {
#    'name ' : 'John',
#    'age ' : 20,
#    'gender  ' : 'Male',
#    'job' : 'student',
# }

【讨论】:

  • "可变对象通过引用传递" 不,它们不是。 Python 使用 single 评估策略,不考虑类型,即通过对象共享调用,有时也称为“通过赋值调用”。 Python 从不使用引用调用(也不使用值调用)
【解决方案5】:

您的变量myinfo 被分配给ui 实例的self.info。所以当你修改myinfo时,你也在修改ui的self.info

我不太确定你想用 myinfo 做什么。如果你想要两个不同的UserInfo,你需要创建另一个这样的实例

ui = UserInfo()

ui2 = UserInfo()

myinfo = ui2.getInfo()

myinfo.update({
    'name ' : 'May',
    'age ' : 18,
    'gender  ' : 'Female',
    'job' : 'lecturer',
})

print(ui.getInfo())
print(myinfo)

输出

{'name ': 'John', 'age ': 20, 'gender  ': 'Male', 'job': 'student'}
{'name ': 'May', 'age ': 18, 'gender  ': 'Female', 'job': 'lecturer'}

如果您只想获得不同的信息,那么您需要使用返回内容的副本。

【讨论】:

    【解决方案6】:

    您正在接收相同的类,因为您正在更改最初输入的值。我建议将字典作为属性传递给类,如下所示:

    class UserInfo():
       def __init__(self, dict_info):
          self.level = 1
          self.type = 'A'
          self.info = dict_info
    
       def getInfo(self):
          return self.info
    
    # pass the dictionary through the class instance
    dict_info = {
            'name ' : 'John',
            'age ' : 20,
            'gender  ' : 'Male',
            'job' : 'student',
            }   
    
    ui = UserInfo(dict_info)
    
    # display original class info
    print(dict_info)
    
    # Display changed class info
    myinfo = ui.getInfo()
    # change "myinfo"
    myinfo.update({
       'name ' : 'May',
       'age ' : 18,
       'gender  ' : 'Female',
        'job' : 'lecturer',
    })
    
    ui.getInfo()
    

    如果您在方法中返回值,我也不会打印它(就像您在 print(ui.getInfo) 中所做的那样。您也可以只创建一个新字典(制作副本)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-17
      • 2012-11-05
      • 2023-03-24
      • 1970-01-01
      • 2013-05-09
      • 1970-01-01
      相关资源
      最近更新 更多