简体   繁体   English

字典和类的意外Python行为

[英]Unexpected Python behavior with dictionary and class

class test:
    def __init__(self):
        self.see=0
        self.dic={"1":self.see}

examine=test()
examine.see+=1
print examine.dic["1"]
print examine.see

this has as a result 0 and 1 and it makes no sense why. 结果为0和1,这是没有道理的。

    print id(examine.dic["1"])
    print id(examine.see)

they also have different memory addresses 他们也有不同的内存地址

However, if you use the same example but you have an array instead of variable in see. 但是,如果使用相同的示例,但是在see中有一个数组而不是变量。 You get the expected output. 您将获得预期的输出。

Any explanations? 有什么解释吗?

This gives the expected output: 这给出了预期的输出:

class test:
    def __init__(self):
        self.see=[0]
        self.dic={"1":self.see}

examine=test()
examine.see[0]+=1
print examine.dic["1"][0]
print examine.see[0]

Short answer: 简短答案:

Arrays/ list s are mutable whereas integers/ int s are not. 数组/ list是可变的,而整数/ int则不是。

lists are mutable (they can be changed in place), when you change a list the same object gets updated (the id doesn't change, because a new object is not needed). 列表是可变的(可以在适当位置更改),当您更改列表时,同一对象将被更新(ID不会更改,因为不需要新对象)。

Integers are immuable - this means to change the value of something, you have to create a new object, which will have a different id. 整数是不可改变的-这意味着要更改某些值,您必须创建一个新的对象,该对象将具有不同的ID。 Strings work the same way and you would have had the same "problem" if you set self.see = 'a' , and then did examine.see += ' b' 字符串以相同的方式工作,如果设置self.see = 'a' ,然后进行examine.see += ' b' ,那么您将遇到相同的“问题” examine.see += ' b'

>>> a = 'a'
>>> id(a)
3075861968L
>>> z = a
>>> id(z)
3075861968L
>>> a += ' b'
>>> id(a)
3075385776L
>>> id(z)
3075861968L
>>> z
'a'
>>> a
'a b'

In Python, names point to values; 在Python中,名称指向值; and values are managed by Python. 和值由Python管理。 The id() method returns a unique identifier of the value and not the name . id()方法返回的的唯一标识符,而不是名称

Any number of names can point to the same value . 任意数量的名称都可以指向相同的 This means, you can have multiple names that are all linked to the same id. 这意味着,您可以拥有多个都链接到相同ID的名称。

When you first create your class object, the name see is pointing to the value of an integer object, and that object's value is 1 . 首次创建类对象时,名称see指向整数对象的值,并且该对象的值为1 Then, when you create your class dic , the "1" key is now pointing to the same object that see was pointing to; 然后,当您创建类dic"1"键现在指向与see指向的对象相同的对象; which is 1 . 这是1

Since 1 (an object of type integer) is immutable - whenever you update it, the original object is replaced and a new object is created - this is why the return value of id() changes. 由于1 (整数类型的对象)是不可变的-每当您对其进行更新时,都会替换原始对象并创建一个新对象-这就是为什么id()的返回值会更改的原因。

Python is smart enough to know that there are some other names pointing to the "old" value, and so it keeps that around in memory. Python足够聪明,可以知道还有一些其他名称指向“旧”值,因此它将其保留在内存中。

However, now you have two objects; 但是,现在您有两个对象。 and the dictionary is still pointing to the "old" one, and see is now pointing to the new one. 和字典仍然指向“旧”之一, see现在指向新的。

When you use a list, Python doesn't need to create a new object because it can modify a list without destroying it; 当您使用列表时,Python无需创建新对象,因为它可以修改列表而不破坏列表。 because lists are mutable. 因为列表是可变的。 Now when you create a list and point two names to it, both the names are pointing to the same object. 现在,当您创建列表并指向两个名称时,两个名称都指向同一对象。 When you update this object (by adding a value, or deleting a value or changing its value) the same object is updated - and so everything pointing to it will get the "updated" value. 当您更新此对象(通过添加值,删除值或更改其值)时,将更新同一对象-因此指向该对象的所有对象都将获得“已更新”的值。

examine.dic["1"] and examine.see do indeed have different locations, even if the former's initial value is copied from the latter. examine.dic["1"]前者的初始值是从后者复制而来的, examine.see examine.dic["1"]examine.see确实具有不同的位置。

With your case of using an array, you're not changing the value of examine.see : you're instead changing examine.see[0] , which is changing the content of the array it points to (which is aliased to examine.dic["1"] ). 对于使用数组的情况,您并没有更改examine.see的值,而是更改了examine.see[0] ,后者正在更改其指向的数组的内容( 别名是用来examine.dic["1"] )。

When you do self.dic={"1":self.see} , the dict value is set to the value of self.see at that moment. 当您执行self.dic={"1":self.see} ,dict值将设置为此时的self.see值。 When you later do examine.see += 1 , you set examine.see to a new value. 稍后再examine.see += 1 ,可以将examine.see设置为新值。 This has no effect on the dict because the dict was set to the value of self.see ; 这对字典没有影响,因为字典被设置为self.see it does not know to "keep watching" the name self.see to see if is pointing to a different value. 它不知道要“继续监视”名称self.see ,看是否指向不同的值。

If you set self.see to a list, and then do examine.see += [1] , you are not setting examine.see to a new value, but are changing the existing value. 如果将self.see设置为列表,然后再进行examine.see += [1] ,则不是将examine.see设置为新值,而是在更改现有值。 This will be visible in the dict, because, again, the dict is set to the value , and that value can change. 这将在dict中可见,因为再次将dict设置为value ,并且该值可以更改。

The thing is that sometimes a += b sets a to a new value, and sometimes it changes the existing value. 问题是,有时a += ba到一个新的值,有时它改变了现有的值。 Which one happens depends on the type of a ; 哪一个发生取决于类型a ; you need to know what examine.see is to know what examine.see += something does. 你需要知道什么examine.see是知道examine.see += something一样。

Others have addressed the mutability/boxing question. 其他人已经解决了变异性/装箱问题。 What you seem to be asking for is late binding . 您似乎想要的是后期绑定 This is possible, but a little counterintuitive and there's probably a better solution to your underlying problem… if we knew what it was. 这是可能的,但是有点违反直觉,如果您知道这是什么,可能可以更好地解决您的根本问题。

class test:
  @property
  def dic(self):
    self._dic.update({'1': self.see})
    return self._dic
  def __init__(self):
    self.see = 0
    self._dic = {}

>>> ex=test()
>>> ex.see
0
>>> ex.see+=1
>>> ex.see
1
>>> ex.dic
{'1': 1}
>>> ex.see+=1
>>> ex.dic
{'1': 2}

In fact, in this contrived example it's even a little dangerous because returning self._dic the consumer could modify the dict directly. 实际上,在这个人为的示例中,这甚至有些危险,因为返回self._dic使消费者直接修改字典。 But that's OK, because you don't need to do this in real life. 但这没关系,因为您不需要在现实生活中这样做。 If you want the value of self.see , just get the value of self.see . 如果您想要self.see的价值,只需获取self.see的价值。

In fact, it looks like this is what you want: 实际上,这就是您想要的:

class test:
  _see = 0
  @property
  def see(self):
    self._see+=1
    return self._see

or, you know, just itertools.count() :P 或者,您知道itertools.count() :P

This solution worked for me. 这个解决方案对我有用。 Feel free to use it. 随意使用它。

class integer:
    def __init__(self, integer):
        self.value=integer
    def plus(self):
        self.value=self.value+1
    def output(self):
        return self.value

The solution replaces the mutable type int with a class whose address is used as reference. 该解决方案用其地址用作引用的类替换了可变类型int。 Furthermore you can make changes to the class object and the changes apply to what the dictionary points. 此外,您可以对类对象进行更改,并且更改将应用​​于字典所指向的内容。 It is somewhat a pointer/datastructure. 它有点像一个指针/数据结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM