2012-01-25 59 views
2

我剛剛花了很長時間在python中使用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]) 

所以,我的問題是雙重的,真的:

  1. 有一種簡單的方法來告訴某事被存儲和/或傳遞?
  2. 有沒有辦法顯式請求一個副本,而不是參考?

回答

3

使用copy。它允許簡單的淺海和對象的深拷貝在Python:

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

我不知道這樣的模塊存在。謝謝! –

+0

@TomThorogood很高興我可以幫你 –

3

歡迎到Python是如何工作的。很容易確定哪些對象是通過Python中的引用傳遞的:全部都是。只是很多(整數,字符串,元組)是不可變的,所以當多個名字指向它們時,你並沒有注意到,因爲你不能改變它們。

如果您需要複印件,請明確複印。這通常可以使用該類型的構造函數來完成:

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

列表也可以切片,你會經常看到這樣做:

newlist = oldlist[:] 

字典有copy()方法:

newdict = olddict.copy() 

這些是「淺」副本:製作一個新的容器,但裏面的項目仍然是原始參考。這可以讓你咬住你,例如,列表的列表。存在一個名爲copy的模塊,其中包含用於複製幾乎所有對象的函數以及一個也將複製包含對象到任意深度的函數。

+0

謝謝你的額外言辭。我不知道python中的所有內容都是通過引用傳遞的。這顯然是一件非常有用的事情! –

0

好吧,如果你好奇的身份可以隨時查詢使用is操作:

>>> help('is') 

的原因,你的嘗試沒有工作是因爲你創造了外循環的字典,這樣的當然,你會遇到問題:

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

不管你有多少次添加的東西dmydictmydict永遠是同一個對象。

其他人已經評論說你應該使用副本,但他們沒有解決你的根本問題 - 你似乎不瞭解引用類型。

在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中引用變量作爲變量存儲值,而不是將它們當作名稱標籤 - 你知道那些說「你好,我的名字是」的那些?

所以,基本上你的代碼中發生的事情是,通過循環返回的對象每次都是相同的。

下面是一個例子:

>>> 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] 

因爲列表是可變的,所以生成器函數能夠修改它。在你的情況下,你有一個正在被返回的字典,所以正如其他人提到的那樣,你需要使用一些複製字典的方法。

如需進一步閱讀,請查閱Python ObjectsCall By Object

0

兩個任選其一:

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

db.querydb.select回迭代器,你需要將其轉換成列表。