word 代码块_如何优雅的写好 Pythonic 代码?
(點擊上方公眾號,可快速關注一起學Python)
Python 與其它語言(比如Java或者C++)相比有比較大的區別,其中最大的特點就是非常簡潔。如果按照其它語言的思路來寫Python代碼,則會使得代碼繁瑣復雜,并且容易出現Bug。在Python語言中,有個詞很火,Pythonic。有的同學可能不明白這個詞的意義,小編的理解就是用Python的寫法寫代碼,而非是其它語言的通用的寫法,寫出Python的特點,寫出Python的風格。
下面,就通過幾個示例來看一下不同思維的Python代碼的差異。
1、變量值交換
這個問題最常見,大家從最開始寫Java及C++等語言代碼都會遇到這個問題。通常是通過一個臨時變量來實現的:
tmp = aa = b
b = tmp
而Python中可以直接交換兩個變量,即:
a, b = b, a2、列表推導式
列表推導式是Java及C++等語言沒有的特性,能夠很簡潔的實現for循環,可以應用于列表,集合或者字典。
例如我們要求20以內的整除3的數的平方的列表,可以用如下代碼實現:
numbers = []for x in xrange(20):
? ?if x % 3 == 0:
? ? ? ?numbers.append(x*x)
而通過列表推導式一行代碼即可實現:
numbers = [x*x for x in range(20) if x % 3 == 0]列表推導式也可以用于集合和字典,將[...]變為{...}即可。集合和字典的實現如下所示:
集合:
numbers = {x * x for x in range(0, 20) if x % 3 == 0}字典:
numbers = {x: x * x for x in range(0, 20) if x % 3 == 0}3、字符串拼接
這是一個老生常談的問題,當我們需要將數個字符串拼接的時候,習慣性的使用 "+" 作為連接字符串的手段。
然而,由于像字符串這種不可變對象在內存中生成后無法修改,合并后的字符串會重新開辟出一塊內存空間來存儲。因此每合并一次就會單獨開辟一塊內存空間,這樣會占用大量的內存空間,嚴重影響代碼的效率。
words = ['I', ' ', 'love', ' ', 'Python', '.']sentence = ''
for word in words:
? ?sentence += '' + word
解決這個問題的辦法是使用字符串連接的join,Python寫法如下:
words = ['I', ' ', 'love', ' ', 'Python', '.']sentence = ''.join(words)
4、如何快速翻轉字符串
Java或者C++等語言的寫法是新建一個字符串,從最后開始訪問原字符串:
a = 'I love Python.'reverse_a = ''
for i in range(0, len(a)):
? ?reverse_a += a[len(a) - i - 1]
而Python則將字符串看作list,而列表可以通過切片操作來實現反轉:
a = 'I love Python.'reverse_a = a[::-1]
5、for/else語句
在C語言或Java語言中,我們尋找一個字符是否在一個list中,通常會設置一個布爾型變量表示是否找到:
cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']tofind = 'Shanghai'
found = False
for city in cities:
? ?if tofind == city:
? ? ? ?print 'Found!'
? ? ? ?found = True
? ? ? ?break
if not found:
? ?print 'Not found!'
而Python中的通過for...else...會使得代碼很簡潔,注意else中的代碼塊僅僅是在for循環中沒有執行break語句的時候執行:
cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']tofind = 'Shanghai'
for city in cities:
? ?if tofind == city:
? ? ? ?print 'Found!'
? ? ? ?break
else:
? ?# 執行else中的語句意味著沒有執行break
? ?print 'Not found!'
另外,while以及try關鍵字都可以和else搭配使用,具體可以參考小編之前寫的文章 ?Python中else塊那點事。
6、迭代對象善用enumerate類
enumerate類接收兩個參數,其中一個是可以迭代的對象,另外一個是開始的索引。比如,我們想要打印一個列表的索引及其內容,可以用如下代碼實現:
cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']index = 0
for city in cities:
? ?index = index + 1
? ?print index, ':', city
而通過使用enumerate則極大簡化了代碼,這里索引設置為從1開始(默認是從0開始):
cities = ['BeiJing', 'TianJin', 'JiNan', 'ShenZhen', 'WuHan']for index, city in enumerate(cities, 1):
? ?print index, ":", city
7、通過lambda來定義函數
lambda可以返回一個可以調用的函數對象,會使得代碼更為簡潔。若不使用lambda則需要單獨定義一個函數:
def f(x):? ?return x * x
map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
使用lambda后僅僅需要一行代碼:
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])這里注意,lambda生成的是一個可以像其他函數一樣使用的函數對象,即
def f(x):? ?return x * x
等價于??
lambda x: x * x8、應用上下文管理
在打開文件時,通常是通過捕獲異常來進行實現的,并且在finally模塊中對文件來進行關閉:
try:? ?file = open('python.txt')
? ?for line in file:
? ? ? ?print line
except:
? ?print "File error!"
finally:
? ?file.close()
而通過上下文管理中的with語句可以讓代碼非常簡潔:
with open('python.txt') as file:? ?for line in file:
? ? ? ?print line
具體原理可以參考小編之前寫的?with語句那點事。
9、使用裝飾器
裝飾器在Python中應用特別廣泛,其特點是可以在具體函數執行之前或者之后做相關的操作,比如:執行前打印執行函數的相關信息,對函數的參數進行校驗;執行后記錄函數調用的相關流水日志等。使用裝飾器最大的好處是使得函數功能單一化,僅僅處理業務邏輯,而不附帶其它功能。
在函數調用前打印時間函數名相關的信息,不使用裝飾器可以用如下代碼實現:
from time import ctimedef foo():
? ?print('[%s] ?%s() is called' % (ctime(), foo.__name__))
? ?print('Hello, Python')
這樣寫的問題是業務邏輯中會夾雜參數檢查,日志記錄等信息,使得代碼邏輯不夠清晰。所以,這種場景需要使用裝飾器:
from time import ctimedef deco(func):
? ?def decorator(*args, **kwargs):
? ? ? ?print('[%s] ?%s() is called' % (ctime(), func.__name__))
? ? ? ?return func(*args, **kwargs)
? ?return decorator
@deco
def foo():
? ?print('Hello, Python')
如果想深入理解裝飾器,可以閱讀小編之前寫的?深入理解Python中的裝飾器。
10、使用生成器
生成器與列表最大的區別就是,列表是一次性生成的,需要較大的內存空間;而生成器是需要的時候生成的,基本不占用內存空間。生成器分為生成器表達式和生成器函數。
先看一下列表:
l = [x for x in range(10)]改為生成器只需要將[...]變為(...),即
g = (x for x in range(10))至于生成器函數,是通過yield關鍵字來實現的,我們以計算斐波那契數列為例,使用列表可以用如下代碼來實現:
def fib(max):? ?n, a, b = 0, 0, 1
? ?fibonacci = []
? ?while n < max:
? ? ? ?fibonacci.append(b)
? ? ? ?a, b = b, a + b
? ? ? ?n = n + 1
? ?return fibonacci
而使用生成器則變為:
def fib(max):? ?n, a, b = 0, 0, 1
? ?while n < max:
? ? ? ?yield b
? ? ? ?a, b = b, a + b
? ? ? ?n = n + 1
對生成器感興趣的可以深入閱讀小編之前寫的文章?生成器那點事?及?深入理解生成器及協程。
11、Counter的使用
通常的詞頻統計中,我們的思路是:
需要一個字典,key值存儲單詞,value存儲對應的詞頻。當遇到一個單詞,判斷是否在這個字典中,如果是,則詞頻加1;如果否,則字典中新增這個單詞,同時對應的詞頻設置為1。
對應的Python代碼實現如下:
#統計單詞出現的頻次def computeFrequencies(wordList):
? ?#詞頻字典
? ?wordfrequencies = {}
? ?for word in wordList:
? ? ? ?if word not in wordfrequencies:
? ? ? ? ? ?# 單詞不在單詞詞頻字典中, 詞頻設置為1
? ? ? ? ? ?wordfrequencies[word] = 1
? ? ? ?else:
? ? ? ? ? ?# 單詞在單詞詞頻字典中, 詞頻加1
? ? ? ? ? ?wordfrequencies[word] = wordfrequencies[word] ?+ 1
? ? return wordfrequencies
有沒有更簡單的方式呢?答案是肯定的,就是使用Counter。collection 中的 Counter 類就完成了這樣的功能,它是字典類的一個子類。Python代碼變得無比簡潔:
# 統計單詞出現的頻次def computeFrequencies(wordList):
? ?#詞頻字典
? ?wordfrequencies = Counter(wordList)
? ?return wordfrequencies
感興趣的可以深入閱讀小編之前寫的文章?Python中的詞頻統計。
12、鏈式比較
在實際數字比較中,我們可能需要多次比較多次,比如我們判斷學習成績是否位于某個區間:
x = 79>>> x < 80 and x > 70
True
而更Pythonic的寫法變身鏈式比較,即:
x = 79>>> 80 < x < 90
False
>>> 70 < x < 80
True
這種寫法給人的感受也更為直觀易懂。
13、函數返回多個值
在Java語言中,當函數需要返回多個值時,通常的做法是生成一個Response對象,然后將要返回的值寫入對象內部。而Python不需要這樣做,可以直接返回多個值:
def f():? ?error_code = 0
? ?error_desc = "成功"
? ?return error_code, error_desc
使用的時候也會非常簡單:
code, desc = f()print code, desc
14、使用*運算符
*運算符和** 運算符完美的解決了將元組參數、字典參數進行? unpack,從而簡化了函數定義的形式,如:
def fun(*args):? ?for eacharg in args:
? ? ? ?print 'tuple arg:', eacharg
fun('I', 'love', 'Python')
運行的結果為:
tuple arg: I
tuple arg: love
tuple arg: Python
感興趣的可以深入閱讀小編之前寫的文章?Python 中的*args和**kwargs。
15、找出列表中出現最多的數
這是經常會遇到的一個問題。解決這個問題的其中一個思路是按照標題11提供的詞頻統計的方法,先統計詞頻,然后遍歷字典,找出具有最大詞頻的數字。有沒有更簡潔的方式?
當然,Python代碼如下:
num = [1, 3, 3, 4, 5, 6, 3, 6, 6, 6]print ?max(set(num),key=num.count)
這些Pythonic的代碼是否讓你耳目一新?你還能寫出哪些Pythonic的代碼?在留言區跟大家分享一下吧!
提醒福利
之前給大家推薦的《Python核心技術與實戰》還在優惠期,原價¥99,限時優惠¥68,不要錯過了。既然你要學Python為什么不跟著知名公司的一線程序員學吶?100天,帶你進階高手。掃碼免費試看
(完)
看完本文有收獲?請轉發分享給更多人
關注「Python那些事」,做全棧開發工程師
點「在看」的人都變好看了哦總結
以上是生活随笔為你收集整理的word 代码块_如何优雅的写好 Pythonic 代码?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (学)咱们换一种说法!
- 下一篇: C++Primer中文版(第4版)第四章