我花了很长时间在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])
所以,我的问题是双重的,真的:
答案 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))
就是你要找的。 p>
答案 3 :(得分:0)
好吧,如果您对身份感到好奇,可以随时使用is
运营商进行检查:
>>> help('is')
你的尝试不起作用的原因是你在循环之外创建字典,所以你当然会遇到问题:
>>> mydict = {}
>>> for x in xrange(3):
... d = mydict
... print d is mydict
...
...
True
True
True
无论您向d
或mydict
添加多少内容,mydict
始终都是同一个对象。
其他人评论说你应该使用副本,但他们未能解决你的根本问题 - 你似乎不理解引用类型。
在Python中,一切都是对象。有两种基本类型 - 可变和不可变。不可变对象是字符串,数字和元组之类的东西。您不能在内存中更改这些对象:
>>> x = 3
>>> y = x
>>> x is y
True
现在x
和y
引用相同的对象(不只是具有相同的值)。
>>> 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 Objects和Call By Object。
答案 4 :(得分:0)
两者中的任何一个都应该起作用:
result = db.query(query_params).list()
result = list(db.query(query_params))
db.query
和db.select
返回迭代器,您需要将其转换为列表。