当字典被覆盖时,如何修复类变量/常量?

时间:2018-12-20 11:49:08

标签: python class variables

我是python的新手,它尝试使用默认值并为类实例自定义它们。

因此,在此示例中,我定义了一个DEFAULT_STRING和一个DEFAULT_SETTINGS变量,可以使用customize()对其进行自定义,并且应将其重置为reset()的默认值。

这对于DEFAULT_STRING来说很好用。但是以某种方式customize()会覆盖DEFAULT_SETTINGS字典。

class TestClass:
  DEFAULT_STRING = 'default string'

  DEFAULT_SETTINGS = {
    'key1': 'value1',
    'key2': 'value2'
  }

  def __init__(self):
    self.reset()

  def customize(self):
    self.string = 'custom string'
    self.settings['key2'] = 'CUSTOM VALUE'

  def reset(self):
    self.string = TestClass.DEFAULT_STRING
    self.settings = TestClass.DEFAULT_SETTINGS

test = TestClass()
test.customize()

print(test.DEFAULT_STRING) # default string
print(test.string) # custom string 
print(test.DEFAULT_SETTINGS) # {'key1': 'value1', 'key2': 'CUSTOM VALUE'} 
print(test.settings) # {'key1': 'value1', 'key2': 'CUSTOM VALUE'} 

test.reset()

print(test.DEFAULT_STRING) # default string
print(test.string) # default string 
print(test.DEFAULT_SETTINGS) # {'key1': 'value1', 'key2': 'CUSTOM VALUE'} 
print(test.settings) # {'key1': 'value1', 'key2': 'CUSTOM VALUE'} 

我很确定有一个非常简单的解决方案,但是我莫名其妙地陷入了困境……

感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

您需要了解什么

self.settings = TestClass.DEFAULT_SETTINGS

确实。

该语句使settings在实例的名称空间中成为名称。 它指向类的名称空间中的DEFAULT_SETTINGS名称所指向的对象。

这意味着该语句不会创建新对象。

注意:self.string也会发生这种情况。但是在那种情况下,self.string是一个字符串,它是不可变的。这意味着当您为相同的名称分配新值时,将创建一个新对象。

我们来看一个例子:

>>> class C:
...  S = 'asdasd'
... 
>>> o = C()
>>> o.s = C.S
>>> o.s
'asdasd'
>>> o.s is C.S
True
>>> o.s = 'pasdpdspa'
>>> o.s is C.S
False
>>> o.s = C.S
>>> C.S = 'popopo'
>>> o.s
'asdasd'

另一方面,字典是可变的,并且就地进行了修改。

因此,如注释中所建议,为了使其正常工作,您需要复制此类字典,例如与

self.settings = dict(TestClass.DEFAULT_SETTINGS))

甚至更好

from copy import deepcopy
self.settings = deepcopy(self.DEFAULT_SETTINGS)

注意:如果字典是嵌套的,则需要后者。 dict仅制作浅表副本,因此并非在所有情况下都有效。

让我们用一个例子来展示它:

>>> d = {1:10, 2: {21: 'abc', 22: 'def'}}
>>> d
{1: 10, 2: {21: 'abc', 22: 'def'}}
>>> d2 = dict(d)
>>> d is d2
False
>>> d[2] is d2[2]
True
>>> d2[2] = 99
>>> d
{1: 10, 2: {21: 'abc', 22: 'def'}}
>>> d2 = dict(d)
>>> d2[2][21] = 99
>>> d
{1: 10, 2: {21: 99, 22: 'def'}}

>>> d = {1:10, 2: {21: 'abc', 22: 'def'}}
>>> d2 = deepcopy(d)
>>> d2[2] is d[2]
False
>>> d2[2][21] = 99
>>> d
{1: 10, 2: {21: 'abc', 22: 'def'}}

注2:您可能会注意到,名称 可以通过实例(即

)访问类的名称空间中的DEFAULT_SETTINGS
self.DEFAULT_SETTINGS

代替

TestClass.DEFAULT_SETTINGS

我建议对Python的不可变和可变类型,就地修改,名称空间和名称解析,继承等进行一些研究