用這10個小技巧加速Python編程

編碼頗有趣,而Python編碼更有趣,由於有不少不一樣的方法能夠實現相同的功能。可是,大多數時候都有一些首選的實現方法,有些人將其稱爲Pythonic。些Pythonic的共同特徵是實現的代碼簡潔明瞭。算法

用Python或任何編碼語言進行編程不是像火箭同樣的科學,而主要是關於技巧。 若是有意嘗試使用Pythonic編碼,那麼這些技術將很快成爲咱們工具包的一部分,而且咱們會發如今項目中使用它們變得愈來愈天然。 所以,讓咱們探索其中的一些簡單技巧。編程

 

1.負索引

人們喜歡使用序列,由於當咱們知道元素的順序,咱們就能夠按順序操做這些元素。在Python中,字符串、元組和列表是最多見的序列數據類型。咱們可使用索引訪問單個項目。與其餘主流編程語言同樣,Python支持基於0的索引,在該索引中,咱們在一對方括號內使用零訪問第一個元素。此外,咱們還可使用切片對象來檢索序列的特定元素,以下面的代碼示例所示。微信

>>> # Positive Indexing... numbers = [1, 2, 3, 4, 5, 6, 7, 8]... print("First Number:", numbers[0])... print("First Four Numbers:", numbers[:4])... print("Odd Numbers:", numbers[::2])...First Number: 1First Four Numbers: [1, 2, 3, 4]Odd Numbers: [1, 3, 5, 7]

可是,Python經過支持負索引而進一步走了一步。具體來講,咱們可使用-1來引用序列中的最後一個元素,並向後計數。例如,最後一個元素的索引爲-2,依此類推。重要的是,負索引也能夠與切片對象中的正索引一塊兒使用。app

>>> # Negative Indexing... data_shape = (100, 50, 4)... names = ["John", "Aaron", "Mike", "Danny"]... hello = "Hello World!"...... print(data_shape[-1])... print(names[-3:-1])... print(hello[1:-1:2])...4['Aaron', 'Mike']el ol

 

2.檢查容器是否爲空

容器是指能夠存儲其餘數據的那些容器數據類型。一些常用的內置容器是元組,列表,字典和集合。在處理這些容器時,咱們常常須要在執行其餘操做以前檢查它們是否包含任何元素。確實,咱們能夠檢查這些容器的長度,該長度與已存儲項目的數量相對應。當長度爲零時,容器爲空。下面顯示了一個簡單的示例。編程語言

if len(some_list) > 0: # do something here when the list is not emptyelse: # do something else when the list is empty

可是,這不是最好的Pythonic方式。相反,咱們能夠簡單地檢查容器自己,它將在容器 True 包含元素時進行評估。儘管如下代碼向您展現了主要的容器數據類型,但這種用法也能夠應用於字符串(即,任何非空字符串都是 True )。函數

>>> def check_container_empty(container):... if container:... print(f"{container} has elements.")... else:... print(f"{container} doesn't have elements.")...... check_container_empty([1, 2, 3])... check_container_empty(set())... check_container_empty({"zero": 0, "one": 1})... check_container_empty(tuple())...[1, 2, 3] has elements.set() doesn't have elements.{'zero': 0, 'one': 1} has elements.() doesn't have elements.

 

3.使用Split()建立字符串列表

咱們常用字符串做爲特定對象的標識符。例如,咱們可使用字符串做爲字典中的鍵。在數據科學項目中,字符串一般是數據的列名。選擇多個列時,不可避免地須要建立一個字符串列表。確實,咱們可使用列表中的文字建立字符串。可是,咱們必須編寫成對的引號將每一個字符串括起來,這對於「懶惰」的人來講有點繁瑣。所以,我更喜歡利用字符串的 split() 方法來建立字符串列表,以下面的代碼片斷所示。工具

>>> # List of strings... # The typical way... columns = ['name', 'age', 'gender', 'address', 'account_type']... print("* Literals:", columns)...... # Do this instead... columns = 'name age gender address account_type'.split()... print("* Split with spaces:", columns)...... # If the strings contain spaces, you can use commas instead... columns = 'name, age, gender, address, account type'.split(', ')... print("* Split with commas:", columns)...* Literals: ['name', 'age', 'gender', 'address', 'account_type']* Split with spaces: ['name', 'age', 'gender', 'address', 'account_type']* Split with commas: ['name', 'age', 'gender', 'address', 'account type']

如上所示, split() 默認狀況下,該方法使用空格做爲分隔符,並根據字符串建立字符串列表。值得注意的是,當您建立包含某些包含空格的元素的字符串列表時,能夠選擇使用其餘類型的分隔符(例如,逗號)。性能

這種用法受到一些內置功能的啓發。例如,當你建立一個元組類,咱們能夠這樣作: Student = namedtuple(「Student」, [「name」, 「gender」, 「age」]) 。字符串列表指定了元組的「屬性」。可是,也能夠經過如下方式定義該類來本地支持它: Student = namedtuple(「Student」, 「name gender age」) 。對於另外一個實例,建立一個Enum類支持相同的替代解決方案。測試

 

4.三元表達

在許多用例中,咱們須要根據條件定義具備特定值的變量,而且咱們能夠簡單地使用if ... else語句來檢查條件。可是,它須要幾行代碼。若是僅處理一個變量的賦值,則可能須要使用三元表達式,該表達式檢查條件並僅用一行代碼便可完成賦值。此外,它的格式更短,從而使代碼更加簡潔。考慮如下示例。ui

# The typical wayif score > 90: reward = "1000 dollars"else: reward = "500 dollars"# Do this insteadreward = "1000 dollars" if score > 90 else "500 dollars"

有時,咱們能夠從已定義的函數中獲取一些數據,而且能夠利用這一點並編寫三元表達式的簡單操做,以下所示。

# Another possible scenario# You got a reward amount from somewhere else, but don't know if None/0 or notreward = reward_known or "500 dollars"# The above line of code is equivalent to belowreward = reward_known if reward_known else "500 dollars"

 

5.帶文件對象的語句

咱們常常須要從文件讀取數據並將數據寫入文件。最多見的方法是使用內置 open() 函數簡單地打開文件,該函數會建立一個咱們能夠操做的文件對象。

>>> # Create a text file that has the text: Hello World!...... # Open the file and append some new data... text_file0 = open("hello_world.txt", "a")... text_file0.write("Hello Python!")...... # Open the file again for something else... text_file1 = open("hello_world.txt")... print(text_file1.read())...Hello World!

在前面的代碼片斷中,咱們從一個文本文件開始,該文件的文本爲「 Hello World!」。而後,咱們將一些新數據附加到文件中。可是,過了一下子,咱們想再次處理該文件。當咱們讀取文本文件時,它仍然具備舊數據。換句話說,附加的文本不包括在文本文件中。

這是由於咱們首先沒有關閉文件對象。若是不關閉文件,則沒法保存更改。確實,咱們能夠 close() 在文件對象上顯式調用該方法。可是,咱們可使用「 with」語句執行此操做,該語句將自動爲咱們關閉文件對象,以下所示。完成對文件的操做後,咱們能夠經過訪問文件對象的 closed 屬性來驗證文件已關閉。

>>> with open("hello_world.txt", "a") as file:... file.write("Hello Python!")...... with open("hello_world.txt") as file:... print(file.read())...... print("Is file close?", file.closed)...Hello World!Hello Python!Hello Python!Is file close? True

用更籠統的術語來講,with語句是在Python中使用上下文管理器的語法。上一個示例涉及文件操做,由於這些文件是共享資源,咱們負責釋放這些資源。上下文管理器能夠幫助咱們完成工做。如前所示,文件操做結束後,將使用with語句自動關閉文件。

 

6.評估多個條件

一般,咱們須要評估多個條件。有幾種可能的方案。對於數值,咱們能夠對同一變量進行屢次比較。在這種狀況下,咱們能夠連接這些比較。

# Multiple Comparisons# The typical wayif a < 4 and a > 1: # do something here# Do this insteadif 1 < a < 4: # do somerthing here

在其餘一些狀況下,咱們能夠進行多個相等比較,而且可使用如下in關鍵字進行成員測試。

# The typical wayif b == "Mon" or b == "Wed" or b == "Fri" or b == "Sun": # do something here# Do this instead, you can also specify a tuple ("Mon", "Wed", "Fri", "Sun")if b in "Mon Wed Fri Sun".split(): # do something here

另外一種技術是使用內置的 all() any()函數 用於評估多個條件的功能。具體而言,該 all() 函數將評估 什麼時候迭代 中的元素所有爲 True ,所以該函數適合於替換一系列AND邏輯比較。另外一方面,該 any() 函數的計算結果爲True 當迭代中的 任何元素爲 True ,所以適合替換一系列OR邏輯運算。相關示例以下所示。

# The typical waysif a < 10 and b > 5 and c == 4: # do somethingif a < 10 or b > 5 or c == 4: # do something# Do these insteadif all([a < 10, b > 5, c == 4]): # do somethingif any([a < 10, b > 5, c == 4]): # do something

 

7.在函數聲明中使用默認值

在幾乎全部的Python項目中,大多數代碼都涉及建立和調用函數。換句話說,咱們不斷處理函數聲明和重構。在許多狀況下,咱們須要屢次調用一個函數。根據不一樣的參數集,該功能將略有不一樣。可是,有時一組參數可能比其餘一組更經常使用,在這種狀況下,咱們在聲明函數時應考慮設置默認值。考慮下面的簡單示例。

# The original form:def generate_plot(data, image_name): """This function creates a scatter plot for the data""" # create the plot based on the data ... if image_name: # save the image ...# In many cases, we don't need to save the imagegenerate_plot(data, None)# The one with a default valuedef generate_plot(data, image_name=None): pass# Now, we can omit the second parametergenerate_plot(data)

要注意的一件事是,若是在設置默認值時要處理可變數據類型(例如列表,集合),請確保使用None而不是構造函數(例如arg_name = [])。因爲Python在定義的位置建立函數對象,所以提供的空白列表將被函數對象「卡住」。換句話說,調用函數對象時不會當即建立它。相反,咱們將在內存中處理相同的函數對象,包括其最初建立的默承認變對象,這可能會致使意外行爲。

 

8.使用計數器進行元素計數

當咱們在列表、元組或字符串中有多個項目時(例如,多個字符),咱們常常想計算每項中有多少個元素。爲此,能夠爲此功能編寫一些乏味的代碼。

>>> words = ['an', 'boy', 'girl', 'an', 'boy', 'dog', 'cat', 'Dog', 'CAT', 'an','GIRL', 'AN', 'dog', 'cat', 'cat', 'bag', 'BAG', 'BOY', 'boy', 'an']... unique_words = {x.lower() for x in set(words)}... for word in unique_words:...  print(f"* Count of {word}: {words.count(word)}")...* Count of cat: 3* Count of bag: 1* Count of boy: 3* Count of dog: 2* Count of an: 5* Count of girl: 1

如上所示,咱們首先必須建立一個僅包含惟一單詞的集合。而後,咱們迭代單詞集,並使用該 count() 方法找出每一個單詞的出現狀況。可是,有一種更好的方法可使用 Counter 類來完成此計數任務。

>>> from collections import Counter...... word_counter = Counter(x.lower() for x in words)... print("Word Counts:", word_counter)...Word Counts: Counter({'an': 5, 'boy': 4, 'cat': 4, 'dog': 3, 'girl': 2, 'bag': 2})

該計數器類是在 collections 模塊中可用的。要使用該類,咱們只需建立一個 generator:,x.lower() for x in words 每一個項目都將被計數。如咱們所見,Counter對象是相似dict的映射對象,每一個鍵對應於單詞列表的惟一項,而值是這些項的計數。

此外,若是咱們有興趣找出單詞列表中最頻繁出現的項目,咱們能夠利用Counter對象的 most_common() 方法。如下代碼展現了這種用法。咱們只須要指定一個整數(N),便可從列表中找出最頻繁的N個項目。附帶說明,該對象還將與其餘序列數據一塊兒使用,例如字符串和元組。

>>> # Find out the most common item... print("Most Frequent:", word_counter.most_common(1))Most Frequent: [('an', 5)]>>> # Find out the most common 2 items... print("Most Frequent:", word_counter.most_common(2))Most Frequent: [('an', 5), ('boy', 4)]

 

9.按不一樣的訂單要求排序

在許多項目中,對列表中的項目進行排序是一項廣泛的任務。最基本的排序基於數字或字母順序,咱們可使用內置 sorted() 函數。默認狀況下,該 sorted() 函數將按升序對列表進行排序(實際上,它能夠是可迭代的)。若是將 reverse 參數指定爲 True ,則能夠按降序得到項目。一些簡單的用法以下所示。

>>> # A list of numbers and strings... numbers = [1, 3, 7, 2, 5, 4]... words = ['yay', 'bill', 'zen', 'del']... # Sort them... print(sorted(numbers))... print(sorted(words))...[1, 2, 3, 4, 5, 7]['bill', 'del', 'yay', 'zen']>>> # Sort them in descending order... print(sorted(numbers, reverse=True))... print(sorted(words, reverse=True))...[7, 5, 4, 3, 2, 1]['zen', 'yay', 'del', 'bill']

除了這些基本用法外,咱們還能夠指定 key 參數,以即可以對複雜項進行排序,例如元組列表。考慮這種狀況的如下示例。

>>> # Create a list of tuples... grades = [('John', 95), ('Aaron', 99), ('Zack', 97), ('Don', 92), ('Jennifer', 100), ('Abby', 94), ('Zoe', 99), ('Dee', 93)]>>> # Sort by the grades, descending... sorted(grades, key=lambda x: x[1], reverse=True)[('Jennifer', 100), ('Aaron', 99), ('Zoe', 99), ('Zack', 97), ('John', 95), ('Abby', 94), ('Dee', 93), ('Don', 92)]>>> # Sort by the name's initial letter, ascending... sorted(grades, key=lambda x: x[0][0])[('Aaron', 99), ('Abby', 94), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]

上面的代碼經過利用傳遞給 key 參數的lambda函數,向咱們展現了兩個高級排序的示例。第一個使用降序對項目進行排序,第二個使用默認的升序對項目進行排序。咱們要結合這兩個要求,若是考慮使用該 reverse 參數,則可能會獲得一個錯誤的排序樹,由於若是嘗試按多個條件進行排序,則反向參數將適用於全部參數。請參見下面的代碼段。

>>> # Requirement: sort by name initial ascending, and by grades, descending... # Both won't work... sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=True)[('Zoe', 99), ('Zack', 97), ('Jennifer', 100), ('John', 95), ('Dee', 93), ('Don', 92), ('Aaron', 99), ('Abby', 94)]>>> sorted(grades, key=lambda x: (x[0][0], x[1]), reverse=False)[('Abby', 94), ('Aaron', 99), ('Don', 92), ('Dee', 93), ('John', 95), ('Jennifer', 100), ('Zack', 97), ('Zoe', 99)]>>> # This will do the trick... sorted(grades, key=lambda x: (x[0][0], -x[1]))[('Aaron', 99), ('Abby', 94), ('Dee', 93), ('Don', 92), ('Jennifer', 100), ('John', 95), ('Zoe', 99), ('Zack', 97)]

如您所見,經過將 reverse 參數設置爲 True False ,都無效。取而代之的是,技巧是取反分數,所以,當您按默認的升序排序時,因爲這些值的取反,分數將反向排序。可是,此方法有一個警告,由於取反只能用於數字值,而不能用於字符串。

 

10.不要忘記defaultdict

字典是一種有效的數據類型,它使咱們可以以鍵值對的形式存儲數據。它要求全部鍵都是可哈希的,存儲這些數據可能涉及哈希表的使用。這種方法容許以O(1)效率實現數據檢索和插入。可是,應注意,除了內置的dict類型外,咱們還有其餘可用的字典。其中,我想討論defaultdict類型。與內置dict類型不一樣,defaultdict容許咱們設置默認工廠函數,該工廠函數在鍵不存在時建立元素。

>>> student = {'name': "John", 'age': 18}... student['gender']...Traceback (most recent call last): File "<input>", line 2, in <module>KeyError: 'gender'

假設咱們正在處理單詞,而且想要將與列表相同的字符分組,而且這些列表與做爲鍵的字符相關聯。這是使用內置dict類型的幼稚實現。值得注意的是,檢查dict對象是否具備 letter 鍵是相當重要的,由於若是鍵不存在,則調用該 append() 方法會引起 KeyError 異常。

>>> letters = ["a", "a", "c", "d", "d", "c", "a", "b"]... final_dict = {}... for letter in letters:...  if letter not in final_dict:...  final_dict[letter] = []...  final_dict[letter].append(letter)...... print("Final Dict:", final_dict)...Final Dict: {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']}

讓咱們看看如何使用defaultdict編寫更簡潔的代碼。儘管該示例很簡單,可是它只是爲咱們提供了有關defaultdict類的一些想法,這使咱們沒必要處理字典對象中不存在的鍵。

>>> from collections import defaultdict...... final_defaultdict = defaultdict(list)... for letter in letters:... final_defaultdict[letter].append(letter)...... print("Final Default Dict:", final_defaultdict)...Final Default Dict: defaultdict(<class 'list'>, {'a': ['a', 'a', 'a'], 'c': ['c', 'c'], 'd': ['d', 'd'], 'b': ['b']})

 

 

結論

在閱讀本文以前,咱們可能已經瞭解了一些技巧,可是但願仍然對這些技巧有所瞭解。在項目中實踐這些慣用用法將使您的Python代碼更具可讀性和性能。

 

交流羣

歡迎加入公衆號讀者羣一塊兒和同行交流,目前有SLAM、三維視覺、傳感器自動駕駛、計算攝影、檢測、分割、識別、醫學影像、GAN算法競賽等微信羣(之後會逐漸細分),請掃描下面微信號加羣,備註:」暱稱+學校/公司+研究方向「,例如:」張三 + 上海交大 + 視覺SLAM「。請按照格式備註,不然不予經過。添加成功後會根據研究方向邀請進入相關微信羣。請勿在羣內發送廣告,不然會請出羣,謝謝理解~

 

本文分享自微信公衆號 - 小白學視覺(NoobCV)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索