Pythonic 是什麼意思?


在我理解,Pythonic 就是很 Python 的 Python 代碼。比如「這很知乎」一般用來表示「知乎社區特有的現象」,「這很百度」表示「百度公司特有的行為」,「很 Python 」的代碼就是 「 Pythonic 」。也就是說,Python 很明顯區別於其它語言的(&優雅&)寫法。

比如,在 C-Like 語言中一般這樣交換兩個數字 a,b:

int t = a;
a = b;
b = t

而在 Python 中,一般這樣寫:

a, b = b, a

類似地,Python 一般這樣判斷數字在某個區間:

0 &< a &< 10

當處理文件時,一般不必 try-except 來安全地善後,而是通過 context manager:

with open(path, mode) as fp:
pass # do something

再比如 for-else、try-else 等等等等很多,諸如此類 Python 特有的,你稱之為特性也好、語法糖也罷的東西,就是 Pythonic。

其實,只要認真閱讀一遍 PEP 8,並盡量遵守,你的代碼就足夠 Pythonic 了:PEP 8 -- Style Guide for Python Code。


這是我在怎樣才能寫出pythonic的代碼? - 程序員 這個問題下的答案,剛好可以用來回答這個問題,希望對大家有幫助。

首先,我們要回答什麼是Pythonic?

我們先來看一下,Python程序員每天津津樂道的the zen of python(Python之禪)

&>&>&> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren"t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you"re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it"s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let"s do more of those!

我簡單翻譯幾句:

優美勝於醜陋
明了勝於晦澀
簡介勝於複雜
……
可讀性很重要
……

難道只有我一個人覺得這是正確的廢話嗎?難道只有我一個人覺得這是正確的廢話嗎?難道只有我一個人覺得這是正確的廢話嗎?

它只告訴你什麼是好,什麼是不好,但是,卻沒有告訴你通往成功彼岸的方法。從這個角度來說,我更推薦:

Pythonic一直以來都是只能意會,不能言傳的東西,然而,為了幫助新同學理解,我準備給出確切的定義:Pythonic就是以Python的方式寫出簡潔優美的代碼!

首先,不管用什麼語言,你都應該努力寫出簡潔優美的代碼,如果不能,那我推薦你看看《重構》和《代碼整潔之道》,雖然這兩本書使用的是java語言,但是,並不影響作者要傳遞的思想。

比如,我們知道,Python裡面的字元串、列表和元組,都稱之為序列,序列支持索引操作和切片操作,切片操作可以指定起點、終點和步長,步長也可以為負數。我們看下面的切片:

L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
L[-2::-2]
L[-2:2:-2]
L[2:2:-2]

誰能快速的回答我上面幾個切片的答案?沒有人!所以,Python也有很多語法,但是,並不能亂用。就這個例子來說:

  • 在同一個切片操作中,不要同時使用start、end和stride
  • 盡量使用stride為正數,且不要帶start和end索引的切割操作

在你已經能夠寫出簡潔優美的代碼的前提下,要寫出Pythonic的代碼,還需要對Python的語言有比較好的了解。我舉幾個在Python裡面與在C、C++和Java裡面有顯著差別的例子。
amp;https://pic2.zhimg.com/v2-5cce87074cf21707a1342b57032a1471_b.png&" dw="133" dh="184" w="133"amp;>

1. 交換兩個數字在其他語言裡面

t = a
a = b
b = t

在Python語言裡面

a, b = b, a

2. 列表推導

列表推導是C、C++、Java裡面沒有的語法,但是,是Python裡面使用非常廣泛,是特別推薦的用法。

與列表推導對應的,還有集合推導和字典推導。我們來演示一下。

  • 列表:30~40 所有偶數的平方

[ i*i for i in range(30, 41) if i% 2 == 0 ]

  • 集合:1~20所有奇數的平方的集合

{ i*i for i in range(1, 21) if i % 2 != 0 }

  • 字典:30~40 所有奇數的平方

{ i:i*i for i in range(30, 40) if i% 2 != 0 }

再舉兩個實用的例子:

  • 當前用戶home目錄下所有的文件列表

[ item for item in os.listdir(os.path.expanduser("~")) if os.path.isfile(item) ]

  • 當前用戶home目錄下所有的目錄列表

[ item for item in os.listdir(os.path.expanduser("~")) if os.path.isdir(item) ]

  • 當前用戶home目錄下所有目錄的目錄名到絕對路徑之間的字典

{ item: os.path.realpath(item) for item in os.listdir(os.path.expanduser("~")) if os.path.isdir(item) }

3. 上線文管理器

我們要打開文件進行處理,在處理文件過程中可能會出錯,但是,我們需要在處理文件出錯的情況下,也順利關閉文件。

Java風格/C++風格的Python代碼:

myfile= open(r"C:miscdata.txt")
try:
for line in myfile:
...use line here...
finally:
myfile.close()

Pythonic的代碼:

with open(r"C:miscdata.txt") as myfile:
for line in myfile:
...use line here...

這裡要說的是,上下文管理器是Python裡面比較推薦的方式,如果用try...finally而不用with,就會被認為不夠Pythonic。此外,上線文管理器還可以應用於鎖和其他很多類似必須需要關閉的地方。

4. 裝飾器

裝飾器並不是Python特有的,只是,在Python裡面應用非常廣泛,我們來看一個例子。

考慮這樣一組函數,它們在被調用時需要對某些參數進行檢查,在本例中,需要對用戶名進行檢查,以判斷用戶是否有相應的許可權進行某些操作。

class Store(object):
def get_food(self, username, food):
if username != "admin":
raise Exception("This user is not allowed to get food")
return self.storage.get(food)

def put_food(self, username, food):
if username != "admin":
raise Exception("This user is not allowed to put food")
self.storage.put(food)

顯然,代碼有重複,作為一個有追求的工程師,我們嚴格遵守DRY(Don』t repeat yourself)原則,於是,代碼被改寫成了這樣:

def check_is_admin(username):
if username != "admin":
raise Exception("This user is not allowed to get food")

class Store(object):
def get_food(self, username, food):
check_is_admin(username)
return self.storage.get(food)

def put_food(self, username, food):
check_is_admin(username)
return self.storage.put(food)

現在代碼整潔一點了,但是,有裝飾器能夠做的更好:

def check_is_admin(f):
def wrapper(*args, **kwargs):
if kwargs.get("username") != "admin":
raise Exception("This user is not allowed to get food")
return f(*arg, **kargs)
return wrapper

class Storage(object):
@check_is_admin
def get_food(self, username, food):
return self.storage.get(food)

@check_is_admin
def put_food(self, username, food):
return storage.put(food)

在這裡,我們使用裝飾器,就可以把參數檢查和業務邏輯完全分離開來,讓代碼顯得更加清晰。這也是比較Pythonic的代碼。

5. 動態類型語言

我們再來看一個例子,該例子充分演示了動態類型語言與靜態類型語言編程之間的差異。

在這個例子中,我們會收到很多不同的請求,對於不同的請求,調用不同的請求處理函數,這個需求如此常見,相信大家應該見過這樣的代碼:

if (cmd == "a")
processA()
else if (cmd == "b")
processB()
else if (cmd == 『c")
processC()
else if (cmd == "d")
processD()
……
else
raise NotImplementException

在Python裡面,我們可以先判斷一個類,有沒有這個函數,如果有,則獲取這個函數,然後再調用。所以,我們的代碼可以寫成這樣:

class A:
def fetch_func(self, action_name):
func= getattr(self, action_name, None)
return func

def execute(self, action, msg):
func= self.fetch_func(action)
if func is None:
return False, "Action not found"
return func(action, msg)

結論:所謂的Pythonic,其實並沒有大家想的那麼神秘,最終目的都是寫出簡潔優美的代碼。寫出簡潔優美代碼的思想在各個語言中都是一樣的。如果你用其他編程語言寫不出簡潔優美的代碼,那麼,你也沒辦法用Python寫出簡介優美的代碼。如果你能用其他語言寫出很好的代碼,那麼,還是需要了解Python這門語言特有的一些語法和語言特性,充分利用Python裡面比較好語言特性。這樣,就能夠寫出Pythonic的代碼了。


簡單說就是符合 Python 語言編程理念的代碼風格。
具體的例子可以參考這個:
http://www.189works.com/article-41429-1.html


引用自Code Style的一段話
Moreover, when a veteran Python developer (a Pythonista) points to portions of code and says they are not 「Pythonic」, it usually means that these lines of code do not follow the common guidelines and fail to express the intent in what is considered the best (hear: most readable) way


What is Pythonic?

簡譯如下:
Pythonic 是什麼意思?
我們心裡明白,但當真開口說出來,卻覺得張不開嘴。就如同你問我「生命」是什麼?我只能期期艾艾。真的要說 Pythonic 是什麼,我會說, Pythonic 是 Python 一些慣用法。下面僅舉幾個例子。
比如,在c語言中,對一個數組的所有元素做操作,一般如此做:

for (i=0; i &< mylist_length; i++) { do_something(mylist[i]); }

但在Python中,我們一般如此做:

for element in mylist:
do_something(element)

再比如說,在c語言中,我們習慣於改變函數的參數:

void foo(int* a, float* b) {
*a = 3;
*b = 5.5;
}

...
int alpha;
int beta;
foo(alpha, beta);

但在 Python 的世界裡,我們這樣:

def foo():
return 3, 5.5

alpha, beta = foo()



假如你有個列表,你想讓列表裡的值都乘以二。

li = [1,2,3,4,5,6,7,8,9]
for i in range(len(li)):
li[i] *=2

這是一種寫法,當然還有很多類似的寫法。

但是還有一種寫法挺好看。

li = [2*x for x in li]

或者

li = list(map(lambda x:2*x,li))

後兩種看起來就比第一種好一些,對吧。這就是Pythonic的一個簡單的例子,還有很多很多。不過說實話真正寫代碼的時候,除非對性能有巨大的影響,否則用自己順手那個就行了。

比如如果習慣列表表達式:

li = [x for x in range(1000000000000000000000000)]

更建議寫成

li = (x for x in range(1000000000000000000000000))

這不僅僅是Pythonic的問題了,這是能不能運行的問題。


《計算機程序的構造和解釋》的作者哈爾·阿伯爾森曾這樣說:

Programs must be written for people to read, and only incidentally for machines to execute.

好的代碼具有可讀性,Python之禪有一句話是:

Readability counts.(我也不知道怎麼翻譯才優雅,自己意會吧)

所謂Pythonic就是很具有Python風格的代碼,是的,Python有自己獨特的語言風格,很多時候,特別是有其它編程經驗的Python初學者還是按照固有的思維習慣來寫,比如:

變數交換

&>&>&> a = 1
&>&>&> b = 2
&>&>&> tmp = a
&>&>&> a = b
&>&>&> b = tmp

pythonic

a, b = b, a

字元串鏈接

names = ["raymond", "rachel", "matthew", "roger",
"betty", "melissa", "judith", "charlie"]

s = names[0]
for name in names[1:]:
s += ", " + name
print(s)

pythonic

print(", ".join(names))

鏈式比較操作

age = 18
if age &> 18 and age &< 60: print("young man")

pythonic

if 18 &< age &< 60: print("young man")

條件判斷

if attr == True:
do_something()

if len(values) != 0: # 判斷列表是否為空
do_something()

pythonic

if attr:
do_something()

if values:
do_something()

還有很多Pythonic寫法,需要你慢慢去發現,真正的Pythonic在可讀性與程序效率上能找到平衡點


寫出來的代碼像一個人用簡短的語言解釋邏輯一樣


1.寫出來的代碼就像偽代碼
2.代碼風格充分體現Python自身特色
3.對標準庫有充分的理解


讀起來還以為是 松下。。(逃


用python的方式去寫代碼 組織邏輯 以及對象行為

已經上升到方法論了

It teaches you HOW TO THINK.

和簡單即美一樣


比如最簡單的比較三個整數大小,用C的話是兩兩比較或者寫個兩個函數或者兩個宏求最大最小值,Python比較部分一句話sorted(a)[::2]就行了,這裡我懶了把輸入處理堆在一起, 注意這本身與Pythonic相悖。

sorted(map(int, input().split()))[::2]

Also in cpp:

int a[3];
std::cin &>&> a[0] &>&> a[1] &>&> a[2];
std::sort(a, a + 3);
std::cout &<&< a[0] &<&< " " &<&< a[2] &<&< std::endl;


官方推薦的Python代碼書寫的最佳實踐!


什麼什麼onic結尾一般就是什麼什麼風格的意思啊,比如羅曼尼克


Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren"t special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you"re Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it"s a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let"s do more of those!


毫無意義的問題,說真的


推薦閱讀:

如何學習 Python GUI 編程?
Python 網路編程需要學習哪些網路相關的知識?
Python新手,在哪裡才能閱讀到大量的基礎Python常式?
TCP如何保持長連接,並識別不同請求的?
你為什麼不用unity引擎?

TAG:程序員 | Python | 編程 |