在Python中识别引用

时间:2012-01-25 19:01:10

标签: python memory reference web.py

我花了很长时间在python中调试一个问题,使用web.py框架,我想知道将来检查这种事情的方法。

简而言之,web.py的数据库类的一个方法是返回一个存储对象,我只能猜测,它是对内存中值的引用,而不是副本。结果是,无论我尝试做什么,我都无法将多个查询结果附加到列表中,因为列表中只包含同一行数据的副本。

要解决这个问题,我必须创建另一个字典并将web.py存储对象中的所有值显式转换为另一种数据类型(我只是使用字符串使其工作),以便强制创建一个新对象。

在代码中,这就是发生的事情:

>>> result = db.query(query_params)
>>> for row in result:
>>>     print row
...     result_list.append(row)
{"result" : "from", "row" : "one"}
{"result" : "from", "row" : "two"}
>>> print result_list
[{"result":"from", "row" : "two"}, {"result" : "from", "row" : "two}]

这使我了解到这是某种参考问题。该列表存储了“行”的位置,而不是它的副本。

我尝试的第一件事就是:

 copy = {}
 for row in result:
    for value in row:
        copy[value] = row[value]
    result_list.append(copy)

但这会导致同样的问题。 我只是通过调整上面的内容来找到解决方案:

            copy[value] = str(row[value])

所以,我的问题是双重的,真的:

  1. 是否有一种简单的方法可以说明存储和/或传播的内容?
  2. 有没有办法明确请求副本而不是引用?

5 个答案:

答案 0 :(得分:3)

使用copy。它允许在python中简单地浅层和深层复制对象:

import copy    
result_list.append(copy.copy(row))

答案 1 :(得分:3)

欢迎使用Python的工作原理。很容易识别哪些对象在Python中通过引用传递:所有对象都是。只是很多(整数,字符串,元组)是不可变的,所以你不会真正注意到多于一个name指向它们,因为您无法更改它们。

如果您需要副本,请明确制作副本。通常可以使用类型的构造函数来完成:

newlist = list(oldlist)
newdict = dict(olddict)

列表也可以使用切片进行操作,您经常会看到:

newlist = oldlist[:]

词典有copy()方法:

newdict = olddict.copy()

这些是“浅”副本:制作了一个新容器,但里面的项目仍然是原始引用。举例来说,这可能会让您感到厌烦。存在一个名为copy的模块,其中包含用于复制几乎任何对象的函数,以及一个deepcopy函数,它还将包含的对象复制到任何深度。

答案 2 :(得分:1)

我对web.py一无所知,但很可能result_set.append(dict(row))就是你要找的。

另请参阅:copy.copy() and copy.deepcopy()

答案 3 :(得分:0)

好吧,如果您对身份感到好奇,可以随时使用is运营商进行检查:

>>> help('is')

你的尝试不起作用的原因是你在循环之外创建字典,所以你当然会遇到问题:

>>> mydict = {}
>>> for x in xrange(3):
...     d = mydict
...     print d is mydict
...
...
True
True
True

无论您向dmydict添加多少内容,mydict始终都是同一个对象。

其他人评论说你应该使用副本,但他们未能解决你的根本问题 - 你似乎不理解引用类型。

在Python中,一切都是对象。有两种基本类型 - 可变和不可变。不可变对象是字符串,数字和元组之类的东西。您不能在内存中更改这些对象:

>>> x = 3
>>> y = x
>>> x is y
True

现在xy引用相同的对象(不只是具有相同的值)。

>>> x += 4

因为3是一个整数且不可变的,所以这个操作不会修改x中存储的值,它实际上做的是添加3和4并发现它导致值为7,所以现在x“指向”7。

>>> y
3
>>> x
7
>>> x is y
False
>>> y = x
>>> x is y
True

使用可变对象,您可以在适当的位置修改它们:

>>> mylist = [1,2,3]
>>> mylist[0] = 3
>>> mylist
[3, 2, 3]

如果你不再考虑Python中的变量作为在变量中存储值,而是将它们视为名称标签,那么你会有更多的时间 - 你知道那些说“Hello,My Name Is”的人吗?

基本上,代码中发生的事情是每次通过循环返回的对象都是相同的。

以下是一个例子:

>>> def spam_ref():
...     thing = []
...     count = 0
...     while count < 10:
...         count += 1
...         thing.append(count)
...         yield thing
...
>>> for a in spam_ref():
...  print a
...
[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> spam = []
>>> for a in spam_ref():
...      spam.append(a)
...

因此,如果您查看循环的输出,您认为spam现在包含

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

但事实并非如此:

>>> for a in spam:
...  print a
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

因为列表是可变的,所以生成器函数能够在适当的位置修改它。在你的情况下,你有一个正在返回的字典,正如其他人提到的那样,你需要使用一些复制字典的方法。

如需进一步阅读,请在effbot上查看Python ObjectsCall By Object

答案 4 :(得分:0)

两者中的任何一个都应该起作用:

result = db.query(query_params).list()
result = list(db.query(query_params))

db.querydb.select返回迭代器,您需要将其转换为列表。