【问题标题】:python update class's list or dictionary when variables change elsewhere当变量在其他地方发生变化时,python更新类的列表或字典
【发布时间】:2015-09-29 12:53:41
【问题描述】:

在外部更改后如何更新包含其他 self.variables 的 self.dictionary 或 self.list?

例如,我们有以下测试类:

class Test():
   def __init__(self):
       self.some_number = 0
       self.other_number = 0
       self.lst=[self.some_number, self.other_number]
       self.dic={'some_number': self.some_number, 'other_number': self.other_number}

   def update_values(self):
       self.number = 2
       self.other_number = 3

然后更新值:

test = Test()                  

print(test.dic)
print(test.lst)

test.update_values()

print(test.dic) 
print(test.lst)

结果没有变化:

{'other_number': 0, 'some_number': 0}
[0, 0]
{'other_number': 0, 'some_number': 0}
[0, 0]

我实际上认为,字典和列表保存了对这些变量的引用,这些变量指向内存中的某个位置。因此,如果这些改变,那也应该改变。但情况似乎并非如此。有什么解释吗?

【问题讨论】:

  • 为什么你的类实例复制了相同的值?这似乎是一个坏主意并且容易出错。如果只使用 dict,则可以使用 dict.keys() 或 dict.values() 获取名称或值。如果顺序与列表一样重要,请使用 collections.OrderedDict。
  • 无顺序并不重要。这个想法是在一个 dic 中额外收集所有内容,例如以便稍后对其进行漂亮的打印操作。

标签: python arrays oop object dictionary


【解决方案1】:

如果您想在使用self.lstself.dic 时获得的内容中反映更新后的值,请将它们设为property

class Test():
    def __init__(self):
        self.some_number = 0
        self.other_number = 0

    def update_values(self):
        self.some_number = 2
        self.other_number = 3

    @property
    def lst(self):
        return [self.some_number, self.other_number]

    @property
    def dic(self):
        return {'some_number': self.some_number,
                'other_number': self.other_number}
>>> t = Test()
>>> t.lst
[0, 0]
>>> t.dic                                                          
{'some_number': 0, 'other_number': 0}
>>> t.update_values()
>>> t.lst                                                          
[2, 3]
>>> t.dic 
{'some_number': 2, 'other_number': 3}

【讨论】:

  • 公平点; OTOH,这确实会调用方法调用的开销并在每次访问时动态重建列表/字典。这对于小型容器来说可能没问题,但如果它们有很多元素,那就不行了。
  • @PM2Ring 我无法想象一个具有如此多属性的类,以至于重建列表或字典的成本会成为一个问题......
  • 如果python动态重建list/dic,旧数据怎么办? python会从内存复制到内存吗?还是只是重新分配?
  • @xaratustra:我链接到的 Ned Batchelder 文章应该可以回答您的问题。但与此同时,我会在答案的末尾添加一些信息。
【解决方案2】:

是的,名称是参考。但是当在update_values 你做self.number = whatever 时,你重新绑定 self.number 指向一个不同的整数。 self.lst 仍然包含对原始整数的引用。

如果整数是可变的,您可以改变self.number 的值,这将反映在self.lst 中。但他们不是,所以你不能。如果您使用其他类型的对象,那将是可见的。例如:

def __init__(self):
    self.first = [0]
    self.lst = [self.first]

def update_values(self):
    self.first[0] += 1

这里我们改变 self.first,而不是重新绑定它,所以 self.lst 将反映变化。

【讨论】:

    【解决方案3】:

    整数是不可变的,所以这行不通。但是你可以使用列表来实现你想要的:

    class Test(object):
       def __init__(self):
           self.some_number = [0]
           self.other_number = [0]
           self.lst=[self.some_number, self.other_number]
           self.dic={'some_number': self.some_number, 
               'other_number': self.other_number}
    
       def update_values(self):
           self.some_number[0] = 2
           self.other_number[0] = 3
    
    
    test = Test()
    
    print(test.dic)
    print(test.lst)
    
    test.update_values()
    
    print(test.dic) 
    print(test.lst)       
    

    输出

    {'some_number': [0], 'other_number': [0]}
    [[0], [0]]
    {'some_number': [2], 'other_number': [3]}
    [[2], [3]]
    

    请注意,我们必须改变 some_numberother_number 列表,即修改它们的内容。如果我们简单地用新列表替换它们,那么我们就不会得到想要的传播:

    #This doesn't do what we want because it replaces the old list
    #objects with new ones.
    def update_values(self):
        self.some_number = [2]
        self.other_number = [3]
    

    Python 名称绑定的工作方式起初可能有点令人困惑,尤其是当您来自其他语言时。 IMO,最好试着用它自己的术语来理解这个成语,而不是试图从引用、指针等方面来理解它。

    我建议你看看 SO 老手 Ned Batchelder 的Facts and myths about Python names and values;该文章也可以在 YouTube 上以视频形式提供。


    在你问的评论中:

    如果python动态重建list/dic,旧的会怎样 数据? Python会从内存复制到内存吗?还是只是重新分配?

    Python 变量,无论是简单变量还是self.some_number 之类的属性,都不是保存值的容器。这是一个绑定到对象的名称。 a=0 创建一个值为 0 的 int 对象,并将该对象绑定到本地范围内的名称 a。如果您随后执行a=2,则会创建一个值为2 的int 对象并绑定到a,并回收之前的0 对象。

    (为了提高效率,-5 和 256(含)范围内的小整数被缓存,超出该范围的整数对象在需要时创建并在不再需要时销毁,即当不再有任何名称时绑定到他们)。

    当你这样做时

    a = 1234
    b = 5678
    mylist = [a, b]
    

    创建了两个整数对象并将其绑定到名称ab。 IOW,a1234 对象的名称,b5678 对象的名称。有时这是通过说a 持有对1234 整数对象的引用来描述的。但是 IMO 最好(也更简单)将 a 视为字典中的键,将 1234 整数对象作为关联值。

    然后mylist = [a, b] 使mylist[0] 成为1234 对象的备用名称,mylist[1] 成为5678 对象的备用名称。 12345678 对象本身不会复制到内存中。从本质上讲,Python 列表是一个指向它所拥有的对象的指针数组(添加了一些机制,使列表成为适当的 Python 对象)。

    在 mkrieger1 的代码中,每次检索 Test.lst 属性时都会创建一个新的列表对象;没有“旧列表”,除非您以某种方式通过绑定将其保存在某处。

    我希望这是有帮助的,而不是太混乱。 :)

    【讨论】:

    • 谢谢,我不知道这个。我要检查你发送的文章。只有我必须处理列表中的数字这一事实才使我最终使用上面建议的@property 解决方案,因为 dic 相对较小。
    猜你喜欢
    • 2020-11-17
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    • 1970-01-01
    • 2018-06-29
    相关资源
    最近更新 更多