2016-07-06 75 views
8

正如你已經理解我是初學者,並且試圖理解寫這個函數的「Pythonic方式」建立在。 我知道其他線程可能包含對此的部分答案,但我不知道要查找什麼,因爲我不明白這裏發生了什麼。瞭解此行:list_of_tuples = [(x,y)for x,y,data_one中的標籤]

這行是我的朋友給我發,以提高我的代碼的代碼是:

import numpy as np 

#load_data: 
def load_data(): 
    data_one = np.load ('/Users/usr/... file_name.npy') 
    list_of_tuples = [] 
    for x, y, label in data_one: 
     list_of_tuples.append((x,y)) 
    return list_of_tuples 

print load_data() 

「改良版」:

import numpy as np 

#load_data: 
def load_data(): 
    data_one = np.load ('/Users/usr.... file_name.npy') 
    list_of_tuples = [(x,y) for x, y, label in data_one] 
    return list_of_tuples 

print load_data() 

我在想:

  1. 這裏發生了什麼?
  2. 這是一種好還是壞的方式?因爲它是「Pythonic」,我認爲它不會 與其他語言一起工作,所以也許最好是習慣於更一般的方式?
+1

這是一個列表理解。它更清潔,更pythonic。在谷歌上檢查它。而且,它也會更快。這是一個更好的方法。 – acushner

+1

是不是從非改進版本中清楚的「這裏發生了什麼」? – mgilson

+0

https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions –

回答

15
list_of_tuples = [(x,y) for x, y, label in data_one] 

(x, y)tuple < - 聯教程。

這是一個列表comprehension

[(x,y) for x, y, label in data_one] 
# ^        ^
# |  ^comprehension syntax^ | 
# begin list      end list 

data_oneiterable並且是必要的列表理解。在封面下他們是循環,必須迭代某些東西。

x, y, label in data_one告訴我,我可以從可迭代的data_one遞送的每個元素中「解開」這三個項目。這就像for循環的局部變量一樣,每次迭代都會變化。

總的來說,這個說:

讓元組看起來就像(x, y)從哪裏獲得x, y, and label從可迭代data_one交付每個項目的列表。將每個xy放入名爲list_of_tuples的列表中的一個元組中。是的,我知道我「解壓縮」label並從未使用它,我不在乎。

+4

我喜歡你的解釋,只需要添加一件事:python支持'_'爲「我知道你有其他東西需要解開,我不關心,所以把它們扔掉:'[(x,y)對於x,y,_ in data_one]'是有效的,儘管可讀性較差。 – TemporalWolf

+6

@TemporalWolf Python沒有這樣的「無關」語法,'_'只是一個常規的變量名。 –

+4

@StefanoSanfilippo當你是正確的,因爲它不是一個特殊的語法,值得注意的是,許多編輯認識到'_'變量是一個「不關心」的佔位符,並且抑制了與它有關的「未使用的變量」警告。 – ApproachingDarknessFish

3

這被稱爲列表理解。它類似於循環,通常可以完成相同的任務,但會生成一個包含結果的列表。一般格式是[operation for variable in iterable]。例如,

[x**2 for x in range(4)]將導致[0, 1, 4, 9]

通過在一個列表理解中使用多個函數,變量和iterables,它們也可以變得更復雜(就像上面那樣)。例如,

[(x,y) for x in range(5) for y in range(10)]

你可以在這個here找到更多的閱讀。

+0

來自我的很好的示例+1。 – piRSquared

+0

謝謝加1給你太piRSquared –

8

這兩種方式都是正確的和工作。你或許可以將第一種方式與C語言和其他語言的方式聯繫起來。這是,你基本上運行一個for循環來遍歷所有的值,然後將它附加到你的元組列表。

第二種方法更pythonic,但也是一樣的。如果你看看[(x,y) for x, y, label in data_one](這是一個列表理解),你會看到你也在相同的數據上運行for循環,但是結果將是(x, y),所有這些結果將形成一個列表。所以它實現了同樣的事情。

第三種方法(作爲評論的響應添加)使用切片方法。

我準備了類似你一個小例子:

data = [(1, 2, 3), (2, 3, 4), (4, 5, 6)] 

def load_data(): 
    list_of_tuples = [] 
    for x, y, label in data: 
     list_of_tuples.append((x,y)) 
    return list_of_tuples 

def load_data_2(): 
    return [(x,y) for x, y, label in data] 

def load_data_3(): 
    return [t[:2] for t in data] 

他們都做同樣的事情,並返回[(1, 2), (2, 3), (4, 5)]但它們運行時是不同的。這就是爲什麼列表理解是一個更好的方法來做到這一點。

當我運行的第一個方法load_data()我得到:

%%timeit 
load_data() 
1000000 loops, best of 3: 1.36 µs per loop 

當我運行第二個方法load_data_2()我得到:

%%timeit 
load_data_2() 
1000000 loops, best of 3: 969 ns per loop 

當我運行第三種方法load_data_3()我得到:

%%timeit 
load_data_3() 
1000000 loops, best of 3: 981 ns per loop 

The second方式,列表理解,更快!

+0

優秀的演示..謝謝! –

+0

時間是從我的好點+1。 – piRSquared

+0

你能澄清你對'%% timeit'的使用嗎? – TemporalWolf

4

動作本質上是相同的。在較新的Python解釋器中,列表理解中變量的範圍較窄(在理解之外無法看到x)。

list_of_tuples = [] 
for x, y, label in data_one: 
    list_of_tuples.append((x,y)) 

list_of_tuples = [(x,y) for x, y, label in data_one] 

這種操作經常發生,Python開發人員認爲值得使用特殊語法。有一個類似的功能,但我認爲列表的理解更清晰。

Python開發者喜歡這種語法,足以將其擴展到生成器和字典和集合。它們允許嵌套和條件分句。

兩種表格都使用元組拆開包裝x,y,label in data_one

這些剪輯在做什麼? data_one顯然是包含3個元素的元組列表(或子列表)。這段代碼創建了一個包含2個元素元組的新列表 - 3個元素中的2個。我認爲在列表理解中更容易看到這一點。

熟悉這兩者是明智的。有時候這種行爲太複雜了,無法用理解形式來表達。

理解的另一個特點 - 它不允許副作用(或者至少它是合併它們更棘手)。這在某些情況下可能是缺陷,但通常會使代碼更清晰。

+0

很好的洞察動機。 – piRSquared

5

「改進」版本使用list comprehension。這使得代碼declarative(描述你想要的)而不是imperative(描述如何得到你想要的)。

聲明式編程的優點是實現細節大多被省略,底層類和數據結構可以以最佳方式執行操作。例如,python解釋器可以在上面的示例中做出的一個優化是預先分配數組list_of_tuples的正確大小,而不是在append()操作期間不得不連續調整數組的大小。

爲了讓你開始使用列表解析,我將解釋我通常開始編寫它們的方式。有關列表L寫入是這樣的:

output = [x for x in L] 

對於每個元素在L,一個變量被萃取(中心x),並且可以用於形成輸出列表(x左側)。以上表達式實際上什麼都不做,並且outputL相同。勢在必行,它是類似於:

output = [] 
for x in L: 
    output.append(x) 

從這裏不過,你可能知道,每個x實際上是可以用tuple assignment解壓一個元組:

output = [x for x, y, label in L] 

這將創建一個新的列表,包含只有列表中每個元組的x元素。

如果你想裝在輸出列表不同的元組,你只包它的左側:

output = [(x,y) for x, y, label in L] 

這基本上是你最終在你的優化版本。

你可以使用list包容其他有用的東西,如符合特定條件的只有插入值:

output = [(x,y) for x, y, label in L if x > 10] 

這裏是關於列表理解的有用的教程,你可能會感興趣:http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/