识别Python中的引用
我刚刚花了很长时间使用web.py框架在python中调试了一个问题,这让我想知道将来如何检查这类问题 简言之,web.py的数据库类的方法之一是返回一个存储对象,我只能猜测它是对内存中某个值的引用,而不是副本。结果是,无论我尝试做什么,我都无法将一个查询的多个结果附加到一个列表中,因为该列表只包含同一行数据的副本 为了解决这个问题,我必须创建另一个字典,并将web.py存储对象中的所有值显式转换为另一个数据类型(我只是使用字符串使其工作),以便强制它创建一个新对象 在代码中,发生了以下情况:识别Python中的引用,python,memory,reference,web.py,Python,Memory,Reference,Web.py,我刚刚花了很长时间使用web.py框架在python中调试了一个问题,这让我想知道将来如何检查这类问题 简言之,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])
所以,我的问题是双重的,真的:
import copy
result_list.append(copy.copy(row))
我对web.py一无所知,但最有可能的结果集是append(dict(row))
另请参见:欢迎了解Python的工作原理。在Python中很容易识别哪些对象是通过引用传递的:所有对象都是。只是很多对象(整数、字符串、元组)是不可变的,所以当有多个名称指向它们时,您不会真正注意到,因为您无法更改它们 如果需要副本,请显式复制。通常,这可以使用类型的构造函数完成:
newlist = list(oldlist)
newdict = dict(olddict)
列表也可以使用一个片段来完成,您经常会看到:
newlist = oldlist[:]
字典有一个copy()
方法:
newdict = olddict.copy()
这些是“浅”副本:制作了一个新容器,但其中的项目仍然是原始参考。举例来说,这会让你心痛不已。存在一个名为
copy
的模块,该模块包含用于复制几乎任何对象的函数,以及一个deepcopy
函数,该函数还将复制包含的对象到任何深度。如果您对身份感兴趣,您可以始终使用is
操作符进行检查:
>>> help('is')
您的尝试不起作用的原因是因为您在循环之外创建字典,所以您当然会遇到问题:
>>> mydict = {}
>>> for x in xrange(3):
... d = mydict
... print d is mydict
...
...
True
True
True
无论您向d
或mydict
添加多少次内容,mydict
始终是同一个对象
其他人评论说您应该使用copy,但他们没有解决您的根本问题—您似乎不理解引用类型
在Python中,一切都是对象。有两种基本类型-可变和不可变。不可变对象是字符串、数字和元组等。不允许您更改内存中的这些对象:
>>> x = 3
>>> y = x
>>> x is y
True
现在x
和y
指的是同一个对象(不仅仅是具有相同的值)
因为3是一个整数且不可变,所以此操作不会修改存储在x
中的值,它实际做的是将3和4相加,并发现其结果是值7,因此现在x
将“指向”7
对于可变对象,可以在位修改它们:
>>> mylist = [1,2,3]
>>> mylist[0] = 3
>>> mylist
[3, 2, 3]
如果您不再将Python中的变量看作是在变量中存储值,而将它们看作是名称标记,那么您将有更轻松的时间—您知道说“Hello,我的名字是”的那些吗
因此,代码中发生的本质是,通过循环返回的对象每次都是相同的
下面是一个例子:
>>> 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)
...
但事实并非如此:
>>> 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。两种方法中的任何一种都可以:
result = db.query(query_params).list()
result = list(db.query(query_params))
db.query
和db.select
returniterator,需要将其转换为list 我不知道有这样一个模块。非常感谢。谢谢你的赘言。我不知道python中的所有内容都是通过引用传递的。这显然是一件非常有益的事情!
[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]
result = db.query(query_params).list()
result = list(db.query(query_params))