2014-09-04 59 views
0

在Python中,我在處理itertools groupby模塊時遇到了這個奇怪的現象。爲什麼這個分配的對象與原始對象共享相同的內存空間?

在Python中,變量賦值意味着分配新的變量自己的內存,而不是一個指向原來的內存(從我的理解,如果這是不正確,請讓我知道):

y = 7 
x = y  
y = 9 

x will still be 7 

然而,當我是與groupby模塊一起工作時,我使用此模塊將具有相同密鑰的項目分組到一個組中。我想要兩個組,因爲對原始組的重申是無用的,因爲內存已經被修改了。例如:

for key, group in groupby(rows, lambda x: x[0]): 

    data = [thing[1] for thing in group] #accesses 1st attribute of element 
    data2 = [thing[2] for thing in group] # would yield [] as group is empty 

所以,我想這個代替:

for key, group in groupby(rows, lambda x: x[0]): 
    #create a copy of group to reiterate over 
    toup = group 

    print toup #<itertools._grouper object at 0x1039a8850> 
    print group #<itertools._grouper object at 0x1039a8850> 

    data = [thing[1] for thing in group] #accesses 1st attribute of element 
    data2 = [thing[2] for thing in toup] 

數據2應該進入第二個項目,但收益率[],因爲他們都共享同一個存儲

我的問題是,爲什麼會發生這種情況?不應該指派組來toup意味着toup會在不同的十六進制地址位置有組內存的副本?

另外我能做些什麼來繞過這個問題,所以我不必寫兩個groupby迭代?

+1

它取決於變量的類型。如果將整數和字符串分配給另一個變量,則實例對象不是 - 變量將變爲對實例對象的引用。嘗試「a = []; b = a;打印(a是b)」 - 這將打印爲True。 – 2014-09-04 16:20:17

+2

*「變量賦值意味着賦給新變量自己的內存,而不是指向原始內存的指針」* - 不正確,這不是Python名稱的工作方式(請參閱http://nedbatchelder.com/text/names.html) 。使用'toup = group [:]'創建一個副本。 – jonrsharpe 2014-09-04 16:23:02

+1

@這是不正確的(首先,Python沒有真正的基元,例如整數*就是*實例);不同之處在於例如整數是不可變的,而例如列表是可變的。 'a = 1; b = a; a是b'仍然*是'真',只是'b + = 1'不會影響'a'(因爲整數是不可變的)。 – jonrsharpe 2014-09-04 16:24:14

回答

0

在Python中,變量賦值意味着原來的內存分配新的變量自己的內存,而不是一個指針的

Python有可變的(如列表,迭代器,只是一切)和不可變對象(如整數和字符串)。在任何情況下,賦值都不會複製對象。使用不可變對象時,對它們的所有操作都會生成一個新實例,所以不會遇到像修改可變類型一樣修改整數或字符串的問題。

我的問題是爲什麼會發生這種情況?不應該指派組來toup意味着toup會在不同的十六進制地址位置有組內存的副本?

這兩個變量都會指向同一個對象。當你遍歷一個迭代器並耗盡迭代器時,遍歷第二個變量會給你一個空序列。

3

幽州:

在Python中,變量賦值意味着(從我 理解,如果這是不正確,請讓我知道)分配新的變量及其 自己的記憶,而不是一個指針到原來的記憶:

這是不正確的。 Python名稱可以具有(在時間上)像C變量的方面,並且也可以具有(有時)像C指針的方面。試圖說他們就像一個或另一個只是混淆。別。將它們視爲Python的獨特和習慣用法。

Python'變量'應該更多地被認爲是名稱。多於一個可能即使您不打算他們指向相同的內存位置。

實施例:

>>> y=7 
>>> x=7 
>>> x is y 
True 
>>> id(x) 
140316099265400 
>>> id(y) 
140316099265400 

及(由於interning,以下可以是真實參見PEP 237關於短整型的實習,但是這是一個實現細節):

>>> x=9 
>>> y=5+4 
>>> x is y 
True 

Python is運算符通過比較它們的內存地址返回True,如果兩者是相同的對象。 id函數返回該地址。

考慮作爲最後一個例子:

>>> li1=[1,2,3] 
>>> li2=[1,2,3] 
>>> li1==li2 
True 
>>> li1 is li2 
False 

即使LI1 LI2 ==,他們必須是單獨的列表,如果你換一個,因爲在這個例子中,否則都將改變:

(請務必瞭解所有Python程序員遲早會做出的另一個classic mistake,這是由多次引用單個可變對象引起的,然後期望一個引用像單個對象一樣操作。)

正如jonrsharpe在評論中指出的那樣,請閱讀Ned Batchelders出色的Facts and myths about Python Names and ValuesHow to Think Like a Pythonista以獲得更詳細的概述。

+1

你真的應該在第二個例子中提到實習 - 這不會發生在'300 is 60 * 5'(或者在所有Python實現中 - 這是CPython的細節)。 – jonrsharpe 2014-09-04 16:27:52

+0

謝謝你的解釋.. – user2117728 2014-09-04 17:11:37

+0

「Python名稱可以有方面,(在時間)像C變量,也可以有方面(有時)像C指針」他們總是像C指針。沒有通用的方法來按值複製變量。 – 2015-09-11 02:59:42

相關問題