標籤:

Python有哪些黑魔法?


Python的黑魔法當然是各種自省和動態綁定了。

舉個例子,Python可以重新綁定解釋器的excepthook,這樣當程序異常結束時就可以做一些自定義的處理,我自己就一直拿這個配合ipdb進行debug。用以下代碼聲明一個ExceptionHook:

class ExceptionHook :
instance = None

def __call__(self, *args, **kwargs) :
if self.instance is None:
from IPython.core import ultratb
self.instance = ultratb.FormattedTB(mode = "Plain", color_scheme = "Linux", call_pdb = 1)
return self.instance(*args, **kwargs)

然後

import sys
sys.exceptionhook = ExceptionHook()

重設完exceptionhook後,一旦你的代碼拋出異常,整個解釋器的環境都會被ipdb接管,然後就可以像交互模式下那樣使用了。通常我會在裡面查一下棧,把必要的對象pickle一下,這樣以後復現錯誤也比較容易。

由於IPython是非GUI的程序,所以即便在SSH里也可以使用這招,完美解決SSH缺少IDE難以debug的窘境。

動態綁定的另一個用處,就是當程序依賴一個修改過的庫時,可以把修改的部分剝離出來,在運行時動態綁定到對應的庫上去就行。如果修改的是成員方法,需要這樣綁定:

from types import MethodType
def _foo(self, ...):
pass
obj.foo = MethodType(_foo, obj)

順帶提一下,pickle也是個非常好用的工具,儘管序列化並不是python的專利。pickle可以用來保存各種運行過程中的對象:

import pickle
pickle.dump(xxx, open("xxx.dump", "w"))
yyy = pickle.load(open("yyy.dump"))

pickle可以減少很多工作量,尤其是在復現bug時,把正確部分的運行結果pickle下來,這樣每次可以從pickle的位置開始運行。跑多個相似的baseline時也有很好的效果。不足的是pickle比較吃硬碟,pickle一堆東西後很容易就十幾個G了,而且pickle不能序列化動態生成的對象,比如lambda表達式或者上面提到的動態綁定產生的成員方法。

自省方面,Python可以通過dir()和help()函數分別取得對象下成員的列表和幫助,這個在找不到庫文檔的時候非常好用。只要開發者在函數下面寫了注釋,就能在help中看到。

除了上面提到的這些特性,python還有一堆小trick,其他回答里也提到了一些。雖然其中很多是語法糖,不過用好它們可以讓程序更pythonic:

  1. 類中用__slots__將成員靜態化,可以節省大量內存。
  2. 裝飾器,常見用途如函數計時,亦可用來產生新的函數簽名。函數簽名會影響傳參檢查和ide補全,對帶不定長參數的函數非常有用。很多庫中都會用這種方法來兼容不同版本的API。
  3. 生成器,對於只需遍歷的數據可以節省大量內存。
  4. *和**參數展開。典型的例子是zip(*list_x)和chain(*list_x),分別相當於轉置和concatenate。
  5. if __name__ == "__main__": 檢查是否作為主程序調用,用multiprocessing並行時主程序得用這個框起來。
  6. enumerate,例如將一個list變成list2index可以用dict([(x, i) for i, x in enumerate(list_x)])
  7. namedtuple,生成類似於C語言的結構體,同時支持tuple的所有語法。
  8. defaultdict,做統計時不用初始化的dict,可以用lambda實現嵌套構造defaultdict(lambda : defaultdict(int)),甚至遞歸字典tree = lambda : defaultdict(tree)。

是不是乾貨滿滿 = ̄ω ̄=


ctypes ,當年有個人靠這個省了好幾個月的加班

------------------------勞動節補充-----------------------------

回答 @於酥酥

1. ipython + ctypes: 調試/測試Linux API的互動式運行環境

ipython是最好的REPL!(我喜歡Python,至少有30%的好感來自ipython)

REPL的好處不言自明,在開發和調試時能大大的提高效率。尤其是需要對API進行快速驗證時。

我最早是用gdb來做一些REPL的事情,但畢竟操作複雜,互動式功能有限。而用ctypes,就爽快多了,ctypes可以直接人so中提取出函數,在Python層面稍加包裝,就能直接使用,不用編譯/連接,保持運行狀態,結果出來直接用Python分析……簡直是畫面太美

真實場景:

我們的運營環境有數十萬host,host上去除了編譯環境,某一天,我們對某個系統調用的返回產生懷疑。於是,按照通常的作法,在開發機上寫一個示常式序,編譯,拷貝到運營機,運行,反覆執行這一個過程。

那麼有了ctypes,直接在python/ipython的REPL里調試就好了。還不容易留下可疑的可執行程序。

2. ctypes作為膠水

ctypes增強了python作為膠水語言的能力,從進程調用/統一協議級別的脫水直接深入到二進位級別的脫水。這樣看來,C++對C的兼容就顯得沒那麼重要了

真實場景:

某個執行框架,插件以so的形式提供,so提供固定的函數入口。重構時打算去除語言耦合,改用進程調用的方式調用插件(類似於cgi server,這樣可以減少對插件編寫的限制,插件本身也更容易測試,防止so崩潰造成框架整體崩潰)。但是很多插件的作者已離職,於是只需要框架額外增加從so里調用函數出來即可,做到平滑升級。

3. ctypes與系統編程

ctypes作為一種輕量並且內置的c語言「代理」,使得python極大地增強了系統編程的能力。

從此,系統編程的代碼也可以變得更加優雅。

真實場景:

sdn/vpc方案需要對內核協議棧做較多的調整,從管理的層面上,網路配置由中央控制並下發。因此,host上存在一個daemon,一方面要接受zookeeper的配置變更通知,另一方面要把配置解析後通過netlink與內核通信。

這個daemon大概幾乎沒有人會用python去做。但是我看到iotop里用到ctypes對netlink介面的封裝,驚為天人,並且python更加適合對配置解析與處理。我斗膽用python實現了這個daemon,調試起來如絲般順滑,然後就減少了好幾個月的加班。

回答我成電師兄 @韋易笑 大神

沒有有生產環境用過cffi,以前在自己電腦上簡單用過,感覺不如ctypes簡單粗暴。當然我沒去用的主要原因還是不想在部署的時候附帶太多東西。


使用contextmanager來限制一個block的執行超時:

with timeout(seconds=10):
balabala()

如果balabala()執行時間超過10秒會中斷這個block,並且拋出RuntimeError("timeout")異常.

timeout源碼:ycyc/contextutils.py at master · MrLYC/ycyc · GitHub

配合這個decorator就能控制一個函數調用的timeout了:ycyc/decoratorutils.py at master · MrLYC/ycyc · GitHub

不過限制在主線程中使用.

等看電影有點無聊,來更新下:

利用閉包的封閉性,構造一個對象的代理對象,對代理對象的屬性訪問能夠映射到原對象的屬性,但是修改不生效:

# 創建代理對象
def proxy(origin_object):
class ProxyObject(object):
def __getattribute__(self, name):
return getattr(origin_object, name)

return ProxyObject()

# 源對象類
class OriginOject(object):
name = "origin"

def __init__(self, val):
self.val = val

origin_object = OriginOject(1) # 源對象
proxy_object = proxy(origin_object) # 代理對象

assert proxy_object.val is origin_object.val # 代理對象屬性能夠映射到源對象

proxy_object.val = 2 # 對代理對象修改不生效
assert proxy_object.val == 1
assert origin_object.val == 1

origin_object.val = 2 # 源對象屬性改變能實時反映到代理對象
assert proxy_object.val == 2
assert origin_object.val == 2


pip一下啥都有呀,比如微信介面 itchat

pip install itchat


在Quora上看到的, 不算黑科技吧, 但感覺挺有意思的. turtle是內置庫

======================================================================
補充一個最近才看到的:

要對字典裡面的鍵嵌套賦值, 對鍵不存在時候的解決方案:

import collections
tree = lambda: collections.defaultdict(tree)
some_dict = tree()
some_dict["colors"]["favourite"] = "yellow"

======================================================================再更一個剛才在stackoveflow上的黑魔法

要把一個list分成n份的時候可以這麼做.

list_of_groups = zip(*(iter(the_list),) * group_size)

&>&>&>print zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]

謝謝@靈劍 說的這是一個基於zip實現的hack, zip並沒有規定說這些迭代器一定是從前往後執行的


周末寫了個東半球最好用的Python版行為樹:fuchen/behave · GitHub

可以這麼定義行為樹:

tree = (
is_greater_than_10 &>&> wow_large_number
| is_between_0_and_10 &>&> count_from_1
| failer * repeat(3) * doomed
)

看起來挺像個DSL,但其實100%是Python代碼。

可以這麼運行:

bb = tree.blackboard(arg1, arg2, arg3)
while bb.tick() == RUNNING:
pass

還可以在樹里加註釋,方便debug:

tree = (
(is_greater_than_10 &>&> wow_large_number) // "if x &> 10, wow"
| (is_between_0_and_10 &>&> count_from_1) // "if 0 &< x &< 10, count from 1" | failer * repeat(3) * doomed // "if x &<= 0, doomed X 3, and then fail" )

寫個兩行的debugger函數:

def my_debugger(node, state):
if node.desc:
print "[%s] -&> %s" % (node.desc, state)

調試運行:

bb = tree.debug(my_debugger, 3)
while bb.tick() == RUNNING:
pass

就可以得到下面的輸出:

[ if x &> 10, wow ] -&> Failure
count 1
[ if 0 &< x &< 10, count from 1 ] -&> Running

count 2
[ if 0 &< x &< 10, count from 1 ] -&> Running

count 3
[ if 0 &< x &< 10, count from 1 ] -&> Success

嗯,我承認其實最適合實現行為樹的,還是函數式語言,比如Haskell。


其實我覺得熟練寫出generator就已經很不錯了

[ (x,y) for x in range(1,20) if x % 3 != 0 for y in range(1, x) if x % y != 0 ]

[ x for x in range(2, 100) if all(x % y != 0 for y in range(2, x)) ]

operations = [(lambda x: lambda: x.send(data))(c) for c in connections]

execute_in_parallel(operations)

提問:最後一個例子為什麼那麼複雜,為什麼不寫成

operations = [lambda: c.send(data) for c in connections]

呢?

這是作業


希爾伯特曲線:

import sys, math, time

import turtle

count = 0

def moveto(x,y):

turtle.penup()

turtle.goto(x,y)

turtle.pendown()

def hilbert(x0, y0, xi, xk, yi, yk, n):

if n X = x0 + (xi + yi)/2

Y = y0 + (xk + yk)/2

X2 = X * 600 -300

Y2 = Y * 600 -300

global count

if count count = count + 1

moveto(X2,Y2)

turtle.color(X,Y,X*Y)

turtle.goto(X2,Y2)

else:

hilbert(x0, y0, yi/2, yk/2, xi/2, xk/2, n - 1)

hilbert(x0 + xi/2, y0 + xk/2, xi/2, xk/2, yi/2, yk/2, n - 1)

hilbert(x0 + xi/2 + yi/2, y0 + xk/2 + yk/2, xi/2, xk/2, yi/2, yk/2, n - 1)

hilbert(x0 + xi/2 + yi, y0 + xk/2 + yk, -yi/2,-yk/2,-xi/2,-xk/2, n - 1)

def main():

turtle.colormode(1.)

turtle.speed(0)

for depth in range(9):

if 7 &> depth &> 4: # for faster rendering.

turtle.getscreen().tracer(0)

global count

count = 0

hilbert(0.0, 0.0, 1.0, 0.0, 0.0, 1.0, depth)

turtle.getscreen().tracer(1)

time.sleep(2)

turtle.Screen().exitonclick()

if __name__ == "__main__":

main()


pandas..


Python中類的比較操作與抽象類的結合使用

我們知道Python的基類之間可以進行比較運算,是因為基類中有實現 __lt__,__le__,__eq__,__gt__,__ge__等方法,那麼類之間要想進行比較運算,同樣也要重寫這些方法。

如下。我們定義兩個類Circle與Rectangle,其中都定義了各自的面積(area())的成員函數,在Circle定義實現了__lt__與__le__方法:

下面就可以進行測試:

但是我們只能對Circle類中已重寫的方法(__lt__與__le__)進行類之間的比較操作,假如我們去進行c&>r的操作,會報錯:TypeError: unorderable types: Circle() &> Rectangle()。因此為了實現兩個類之間的所有比較操作,我們必須重寫全部的比較函數。但是這樣很麻煩。我們就可以思考抽象出一個Circle類與Rectangle類的父類(Shape),然後在父類中重寫這些方法,然後子類繼承就可以獲得父類全部的方法,而不必在子類中逐個地實現這些方法。

與此同時,我們介紹Pyhon庫functools中的total_ordering裝飾器,當用這個裝飾器裝飾一個類時,它允許我們在類中只用定義__lt__、__eq__、__gt__、這些方法中的任意兩個。使用該裝飾器後,自動的把其他幾個比較函數也實現在該類中 。

代碼優化如下:

此時我們並沒有在子類實現比較函數,在父類中也沒有逐個地定義所有的比較函數。我們就可以在類之間進行大於等於的操作。

進一步地,假如一個類繼承了Shape基類,我們並沒有強制地要求子類定義area函數,此時調用父類的比較函數,就會出錯。我們知道在Java中是有抽象類的,當一個類被abstract關鍵字修飾後,繼承它的子類必須實現其中所有的抽象函數,否則該子類也會是一個抽象類,只能被繼承,不能被實例化對象。在Python中沒有相應的抽象機制,我們可以用庫abc中的abstractmethod在父類中修飾一個函數,被這個裝飾器裝飾的函數將會變成一個抽象函數,繼承該父類的子類中若不實現該方法,將不能實例化對象,因此我們可以將父類Shape優化為:


輸入

from __future__ import braces

就可以像寫C/java/C++一樣用大括弧而不用縮進了!


居然沒有人提metaclass……

用著SQLAlchemy的時候都沒人覺得Model的定義方法很奇特嗎?


  1. PEP 0302 -- New Import Hooks
  • Flask 中的插件是怎麼做的? 為何能用

    from flask.ext.sqlalchemy import SQLAlchemy

    代替

    from flask_sqlalchemy import SQLALchemy

  • 為和能有Py2exe, PyInstaller之類的打包工具?
  • lihaoyi/macropy · GitHub 這個神奇的庫在import 的時候發生了什麼?

2. Libencodings

文件編碼經常有

#coding: utf-8

#coding: gbk

還有些好玩的編碼

# -*- coding: rot_13 -*-
cevag uryyb jbeyq!.rapbqr(rot_13)

這個來自於我有特別的 Python 加密技巧



說到python黑魔法,必然要提到python的第三方協程庫gevent的底層實現——greenlet。

greenlet直接在內存層面,通過保存和替換Python進程的運行棧來實現不同協程的切換。

這個切換對於python解釋器是透明的,如果python解釋器對環境有感知的話,則每當協程切換的時候,它的感覺可能類似一個人前一秒還在在路上走路,下一秒突然自己又出現在了地鐵上。

對於普通python用戶而言,直接操作python的運行時棧,這就是在刀尖上跳舞有木有,這要求對內存的操作100%精確,任何錯誤都可能導致python進程崩潰!

那作者又是如何又是如何來保證正確性呢?除了要熟悉python、操作系統、編譯器等等的底層機制,明確設計方案,還需要對不同的系統以及硬體環境做對應的適配工作。我們在使用python的時候,在不同的系統或者硬體下感覺都一樣,那是因為python本身為我們屏蔽了底層細節,在做這種python底層hack的事情的時候,顯然就沒那麼輕鬆了。

舉個例子,由於CPU有很多種,例如i386、x86_64、arm等等,每種CPU的設計不盡相同,於是作者為每種CPU寫了對應的彙編操作指令來完成棧的保存和替換,這些操作都是與操作系統和硬體高度綁定的。

雖然greenlet的實現這麼bt,但就是有人做到了,加上gevent的封裝,用起來比python自帶協程好用太多。

我想任何對python比較熟悉的童鞋,在初次接觸gevent的時候,都會好奇它是如何做到的,在進一步了解其底層greenlet實現機理之後,無不驚嘆其鬼斧神工。

這種事情就是那種,別人不說,你可能永遠不會想到的事情。


sh


我真的沒注意到zip的解包是二階循環群的生成元…


Stackless Python


__slots__ 當年有個網站靠這個省了幾個GB的內存。


其實我想說

chardet



可以看看這個,其實也不算黑魔法。

difflib,它是個official的module哦,用來比較串的相似度。

(difflib)[difflib – Compare sequences],

另外常用的functools和collections也都是Python吸引人的地方。

很多第三方的庫竊以為不能算了。

另外doctest在進行單元測試的時候也是棒呆

25.2. doctest


正確使用python的注釋可以在程序的不同的地方改變一個縮進到底要多少空格多少tab的測試,可酸爽了


我干過用ast和inspect模塊以及exec/eval族函數在運行時修改程序算不算黑魔法摳鼻。。。


利用微信的web介面,過年過節給好友群發微信消息,帶上姓名,誠意十足。

GitHub - vonnyfly/wechat-sendall: 給好友群發有誠意的消息喔


@Kaiser

K神在之前很長一段時間內,總是神秘地給一些python相關的答案點贊。

偶爾的還會看一看編程相關的文章

有的時候還會親自提槍上陣回答問題

終於,在K神不斷地努力練習寫Python後,我們看到了K神最終的成果:

他像輪子哥一樣給自己寫了個女朋友AI出來!以上


python -m SimpleHTTPServer

這是python 2的寫法,python 3待補充

——————————————————————

補充:

python3 -m http.server


大家知道Python沒有C-Like語言的三元表達式

condition ? value1 : value2

可是有時候真的很實用/方便啊,怎麼辦?

(value2, value1)[condition]

--------------------------------------------

exec

exec語句~~~最近寫一堆Python程序而且以後還會不斷有新的加進來,只需要在一個配置文件中加一個名字,然後一個腳本,通過exec執行就好了,很方便~~~

--------------------------------------------

你能想到,下面的代碼幹嘛么~~~

(lambda _________, _, __, ___, ____, _____, ______, _______, ________:
getattr(getattr(
__import__(None.__class__.__name__.__class__.__name__[_____-_____]
+ False.__class__.__class__.__name__[_ &<&< (_____-_____)] + [].__class__.__name__[_ &<&< _]), [].__class__.__name__[________ &>&> __:] + {}.__class__.__name__[______-______] + _________(__&<&<_______, (_______ &<&< (_____ &<&< __)) + (____ &<&< (____ &<&< __)) + (__ ** (_______ &<&< _)) + __ ** (_____ + ________) + (_ &<&< (____ * ___)) + __ ** (________ + __) + (_ &<&< ________) + __**_______ - _ - (__ &<&< ___))), _________(__**________, (_ &<&< (_____ * ________ - __)) + (_ &<&< (_____ * ________ - ___)) + (__ ** ((_ &<&< _____) + __)) + (__ ** (_ &<&< _____)) + (__ ** ((_ &<&< _____) - __)) + (__ ** ((_ &<&< _____) - ___)) + (__ ** ((_ &<&< _____) - ____)) + (_ &<&< (_____ ** __ + _)) + (__ ** (____ * _____ + __)) + (_ &<&< (__ ** ____ + _____)) + (__ ** (_____ * ____ - _)) + (__ ** (_ &<&< ____)) + ( 1 &<&< (__ ** ____ - __)) + (_ &<&< (______ * __ + _)) + (_ &<&< (______ * __)) + (__ ** (___ ** __)) + __ ** ______ + __ ** _____ + __ ** ____ + ____ + __ + _) )(_________(____ &<&< ______, (((_____ &<&< ____) + _) &<&< ((___ &<&< _____) - ___)) + (((((___ &<&< __) - _) &<&< ___) + _) &<&< ((_____ &<&< ____) + (_ &<&< _))) + (((_______ &<&< __) - _) &<&< (((((_ &<&< ___) + _)) &<&< ___) + (_ &<&< _))) + (((_______ &<&< ___) + _) &<&< ((_ &<&< ______) + _)) + (((_______ &<&< ____) - _) &<&< ((_______ &<&< ___))) + (((_ &<&< ____) - _) &<&< ((((___ &<&< __) + _) &<&< __) - _)) - (((((___ &<&< __) + _) &<&< __) + _) &<&< ((_____ &<&< ___) + (_ &<&< _))) + (_______ &<&< (((((_ &<&< ___) + _)) &<&< __))) - ((((((_ &<&< ___) + _)) &<&< __) + _) &<&< ((((___ &<&< __) + _) &<&< _))) + (((_______ &<&< __) - _) &<&< (((((_ &<&< ___) + _)) &<&< _))) + (((___ &<&< ___) + _) &<&< ((_____ &<&< _))) + ((((___ &<&< __) - _)) &<&< _____) + (_ &<&< ___)))) (lambda _______, ___ : (lambda _, __ : _(_, __))(lambda _, __ : chr(__ % _______) + _(_, __ // _______) if __ else (lambda: _).func_code.co_lnotab, ___), *(lambda _, __, ___: _(_, __, ___))( (lambda _, __, ___: [__(___[(lambda: _).func_code.co_nlocals])] + _(_, __, ___[(lambda _: _).func_code.co_nlocals:]) if ___ else [] ), lambda _: _.func_code.co_argcount, ( lambda _: _, lambda _, __: _, lambda _, __, ___: _, lambda _, __, ___, ____: _, lambda _, __, ___, ____, _____: _, lambda _, __, ___, ____, _____, ______: _, lambda _, __, ___, ____, _____, ______, _______: _, lambda _, __, ___, ____, _____, ______, _______, ________: _ ) ))

試試吧.


大一第一次接觸lambda的時候,驚嘆:媽呀真是黑科技。

後來接觸了C++,接觸了很多別的類似於Python里lambda的東西,但是依舊覺得那行Python lambda排序,是我這輩子到目前為止寫過的最漂亮的一行代碼。


看了《代碼之美》上面關於 Python 中詞典的底層設計,震了一驚。


不知道啥叫黑魔法,這個算不算? Hidden features of Python


比如閉包和內嵌作用域,這個值得一提

我用專欄《給妹子講python》里的一篇文章來答題。

專欄鏈接:給妹子講python,歡迎大家關注,提意見!

python有一個很有意思的地方,就是def函數可以嵌套在另一個def函數之中。調用外層函數時,運行到的內層def語句僅僅是完成對內層函數的定義,而不會去調用內層函數,除非在嵌套函數之後又顯式的對其進行調用。

x = 99

def f1():
x = 88
def f2():
print(x)
f2()

f1()

88

可以看出,f1中的嵌套變數x覆蓋了全局變數x=99,然後f2中的本地變數按照引用規則,就引用了x=88。

下面我們來說說嵌套作用域的一個特殊之處:

本地作用域在函數結束後就立即失效,而嵌套作用域在嵌套的函數返回後卻仍然有效。

def f1():
x = 88
def f2():
print(x)
return f2

action = f1()
action()

88

這個例子非常重要,也很有意思,函數f1中定義了函數f2,f2引用了f1嵌套作用域內的變數x,並且f1將函數f2作為返回對象進行返回。最值得注意的是我們通過變數action獲取了返回的f2,雖然此時f1函數已經退出結束了,但是f2仍然記住了f1嵌套作用域內的變數名x。

上面這種語言現象稱之為閉包:一個能記住嵌套作用域變數值的函數,儘管作用域已經不存在。

這裡有一個應用就是工廠函數,工廠函數定義了一個外部的函數,這個函數簡單的生成並返回一個內嵌的函數,僅僅是返回卻不調用,因此通過調用這個工廠函數,可以得到內嵌函數的一個引用,內嵌函數就是通過調用工廠函數時,運行內部的def語句而創建的。

def maker(n):
k = 8
def action(x):
return x ** n + k
return action

f = maker(2)
print(f)

&.action at 0x00000000021C51E0&>

再看一個例子:

def maker(n):
k = 8
def action(x):
return x ** n + k
return action

f = maker(2)
print(f(4))

24

這裡我們可以看出,內嵌的函數action記住了嵌套作用域內得兩個嵌套變數,一個是變數k,一個是參數n,即使後面maker返回並退出。我們通過調用外部的函數maker,得到內嵌的函數action的引用。這種函數嵌套的方法在後面要介紹的裝飾器中會經常用到。這種嵌套作用域引用,就是python的函數能夠保留狀態信息的主要方法了。

這裡接著說說另一個關鍵字nonlocal

本地函數通過global聲明對全局變數進行引用修改,那麼對應的,內嵌函數內部想對嵌套作用域中的變數進行修改,就要使用nonlocal進行聲明。

def test(num):
in_num = num
def nested(label):
nonlocal in_num
in_num += 1
print(label, in_num)
return nested

F = test(0)
F(a)
F(b)
F(c)

a 1
b 2
c 3

這裡我們可以看到幾個點,我們在nested函數中通過nonlocal關鍵字引用了內嵌作用域中的變數in_num,那麼我們就可以在nested函數中修改他,即使test函數已經退出調用,這個「記憶」依然有效。

再最後一個例子:

def test(num):
in_num = num
def nested(label):
nonlocal in_num
in_num += 1
print(label, in_num)
return nested

F = test(0)
F(a)
F(b)
F(c)

G = test(100)
G(mm)

a 1
b 2
c 3
mm 101

多次調用工廠函數返回的不同內嵌函數副本F和G,彼此間的內嵌變數in_num是彼此獨立隔離的。

@李力 @LittleCoder @靈劍 @Hyde Yuan @粥粥 @劉鑫 @何宇豪 @tefx @Patchouli Exarch @楊高峰 @xlzd @zakufish @劉騰達 @facert @初識微夏 @pythonwood


lazy-loading (懶載入),利用了 Python 的描述符機制,它能在程序真正需要的時候執行,載入進內存。

  • django orm query_set 查詢資料庫的時候,會在真正需要數據的時候才執行資料庫查詢
  • werkzeug 的 cached_property

cached_property 源碼

class cached_property(property):

"""A decorator that converts a function into a lazy property. The
function wrapped is called the first time to retrieve the result
and then that calculated result is used the next time you access
the value::
class Foo(object):
@cached_property
def foo(self):
# calculate something important here
return 42
The class has to have a `__dict__` in order for this property to
work.
"""

# implementation detail: A subclass of pythons builtin property
# decorator, we override __get__ to check for a cached value. If one
# choses to invoke __get__ by hand the property will still work as
# expected because the lookup logic is replicated in __get__ for
# manual invocation.

def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func

def __set__(self, obj, value):
obj.__dict__[self.__name__] = value

def __get__(self, obj, type=None):
if obj is None:
return self
value = obj.__dict__.get(self.__name__, _missing)
if value is _missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value

view 函數的懶載入應用

from werkzeug import import_string, cached_property

class LazyView(object):

def __init__(self, import_name):
self.__module__, self.__name__ = import_name.rsplit(., 1)
self.import_name = import_name

@cached_property
def view(self):
return import_string(self.import_name)

def __call__(self, *args, **kwargs):
return self.view(*args, **kwargs)


我不知道算不算黑魔法,只是這個給我提供很大便利。

最近在學強化學習,程序一運行就是幾十分鐘,每次等的焦急,又沒法去做其他事情,所以就使用了這個。整體學習過程不超過10分鐘。

可以在點擊程序運行之後,就去做其他事情,等到訓練運行結束之後,程序就會自動向微信發一條信息,這樣不論你在哪都能及時知道你關心的結果哦~~~


在python 3.5 之後,dict(hash table) 是有序的.


$ 2to3 example.py

將python2.x的代碼自動轉換為python3.x下的代碼,代碼遷移變得省時省力。


tree = lambda : defaultdict(tree)


自曝兩篇分享:

http://hsfzxjy.github.io/python-generator-coroutine/

http://hsfzxjy.github.io/meta-class-in-python/


Python 開發者節省時間的 10 個方法

Python 是一個美麗的語言,可以激發用戶對它的愛。所以如果你試圖加入程序員行列,或者你有點厭倦C++,Perl,Java 和其他語言,我推薦你嘗試Python. Python有很多吸引程序員的功能 ,它易學,面向對象,位元組碼編譯,免費且開源。還有運行時檢查。完整快速的支持,可以執行各種任務的擴展。

高效的Python

在這篇文章,我想強調一些 Python 可以節約時間並最大限度地提高生產力的方面。在做準備時,我諮詢了幾個 Pythonists,他們最節省時間的技巧是什麼?答案在這裡...

1. 不使用分號

使用分號在 Python 中是可選的,對比其他面向對象語言,你不需要在每一條語句後面使用分號。這看起來很簡單,似乎也節省不了多少時間;但一旦你的代碼量擴展到數千號,這些分號就變得分心且沒有必要鍵入。

2. 找一個稱手的代碼編輯器

選擇一個稱手的代碼編輯器可以節省大量的時間。面對這麼多代碼編輯器,很多新手都會感覺很迷茫。習慣一個編輯器再使用其他編輯器會覺得很混亂,所以選一個稱手的是一個好的起點。不管你選擇哪一個,要實時支持 flake8 和 PEP8。關於編輯器選擇指南,請參考文章 Which Code Editors Do Pythonists Use?

3. 遵循 Python 代碼規範

遵循 Python 代碼規範可以提升代碼的可讀性,從而節省評審代碼的時間。(Python 的設計哲學 強調代碼的可讀性。)

4. 使用 help() 函數

Python 的 help() 是隨手可用的內置函數可以節約很多時間,比如查找其他函數的解釋。你可以在解釋器終端直接運行該函數。Python 文檔有更多該函數的用法。

5. 使用庫

Python 有大量的庫可以讓你不必每次都重複造輪子。比如,你可以從 PyPI(Python包索引)選擇大量可用的包,這是一個軟體倉庫。Scikit-image 是一個很好例子,它使圖像處理任務如模糊,增強對比度,縮放只需要一些函數調用就可以完成。

6. 使用Cookiecutter

Cookiecutter 是一命令行工具,可以幫助你從工程模板創建 Python 工程,這可以節省大量的時間。

7. 嚴格的注釋

養成寫注釋的好習慣,可以節省你和其他人的時間,尤其是作調試跟蹤。(是的我們聽到很多,但似乎很多程序員還是需要提醒)。注釋在團隊合作是非常關鍵,尤其是一個做了大量變更。

8. 經常測試

嘗試測試你程序中的每一個組件。聽起來好像有點費時,但長時間運行可以節省大量時間,幫助你發現隱藏的 bug,讓你對代碼更放心,還強制你理解自己的每一片代碼在真實情況下是如何地運行。REPL 是一個 read-eval-print loop,一個常用的代碼測試工具,很多 Pythonists 使用它。

9. 專註和專業

Pythonists 都建議要有一個專註領域和專長。你可以使用 Python 做很多事情,從編寫網頁攝像到處理計算和演算法。 已經有大量的庫可以幫助完成這些任務,如 SimpleCV,計算機視覺處理;Biopython,是一個生物計算庫; SymPy,是一個數學符號計算庫。深入類似這些的領域,並掌握一個特定的框架,幫助你在更深的層次學習 Python,掌握一個特定的代碼風格(在第三部分已經提到),處理特定類型的問題。

10. 每天編碼

當你養成了每天寫 python 代碼,使用 python 解決問題的習慣,你就會開始用 python 思考,可以這麼說,這將最終會幫助你更快的解決問題。

總結

在這邊簡短的文章中,我已經列舉了主要的幾個我從和 python 開發者的對話收集來的 tips,下面的是我能夠添加進去的其他的 tips。

參與 python 的活動和集會

保證參加你能夠參加到的每場活動和集會。他們有益於分享經驗,最佳實踐,工具和其他有趣的話題。這可能表面上不是一種省事的策略,但是通過建議,提示,破解來學習其他人的經驗是另一種避免重複造輪子的方法。每年一次的 PyConf 是一個知曉活動的好地方。

紙上思考

紙上思考 —— 在直接深入代碼之前這樣做 —— 將給你進行修改的靈活性。直接就去寫代碼會強迫你從一開始就得去考慮實現的細節, 這在啟動項目時常常不是對你的時間的最佳利用方式。紙上思考這種解放注意力的方式對於頭腦風暴和問題的解決都很有好處!

掌握基礎知識

最後這一條看似很明顯,但一定要投入時間來學習 Python 的基礎知識. 這樣最終會為你節省很多的時間,因為對更加複雜的主題,你會有更好的準備. 一些好書可以對此有所幫助,它們包括:

  • 《學習 Python》
  • 《Python 編程入門》: 介紹Python計算機語言和計算機編程.

通過閱讀博客和文章,保持有關信息的新鮮度也很重要。可以跟進的一個很棒博客是 The Mouse Vs. The Python. 接:http://www.oschina.net/translate/10-time-saving-tips-pythonistshttps://www.sitepoint.com/10-time-saving-tips-pythonists/來源:開源中國推薦實驗


可以輸入表達式,比如"100+200"會直接被理解成300!

比如張三問python程序員李四:今天晚上幹嘛?

李四:去看張三

結果李四去看了&<你的名字&>


pickle,持久化數據簡直不能更爽(不要用cpickle)


Just type in "import antigravity" in the terminal.


2行代碼實現多進程或者多線程方式

def foo(x):

return x*x

多進程

from multiprocessing import Pool

p = pool(processes=5)

p.map(foo,range(10))

多線程

from multiprocessing.dummy import Pool

p = pool(processes=5)

p.map(foo,range(10))


語法糖特別多,特別簡潔。

1. assert斷言

有時候不需要拋出特定異常並處理的時候,可以放在try except block里而且很方便。Java中是默認關閉的。

2. List comprehensions

lst = [item for item in Obj.func()]

3. yield, map, filter, sort, lambda

很多東西你在其他語言里做起來會複雜很多。


True, False = False, True


PHP是最好的語言!


多繼承是一個非常好用,且是面向對象語言的一個很重要的特性。曾經,寫過一個項目,用到了多繼承。N個對象被一個對象繼承的時候,對象間又保持了自己代碼的純粹性,又可以以self來調用其它類里的方法和屬性,還能把本來一個要寫10M的文件,分成一個一個的小文件。結果產品經理來了句這不會影響性能吧,說啥也要改,誰勸都不行,當時真是日他仙人板板的心都有了。我不改,被同事拿去「翻新」,還要保持現有的多個文件的模塊化的功能。他的做法是:

class objmeta(type):

def __new__(cls, name, bases, attrs):
attrs.update(dict(inspect.getmembers(obj1, inspect.isfunction)))
attrs.update(dict(inspect.getmembers(obj2, inspect.isfunction)))
return type.__new__(cls, name, bases, attrs)

def __init__(cls, name, bases, attrs):
super(objmeta, cls).__init__(name, bases, attrs)

class obj():
__metaclass__ = objmeta

def method():
return 1

然後其它的庫obj1 obj2都是寫成method去掉類的定義,然後成了。把產品經理樂得屁顛屁顛的。不知道這個算不算黑魔法,反正打那以後,我要寫多繼承,如果有反對意見自己去寫去,大爺不伺候。


$ echo {"python":"magic"}|python -m json.tool
{
"python": "magic"
}


comprehension 好用的不要不要的,又簡潔,又優雅,真希望c#也支持啊。


元類

控制類的創建行為

type是默認創建類的類,所以自定義元類是繼承自type。

可以用元類來實現單例模式:

class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance is None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance

class MysqlDatabase(metaclass=Singleton):
pass

如果多個地方需要實例化MysqlDatabase,這樣保證只會被創建一次。


【python系列】dict、list的中文顯示 - SNS程飛的專欄 - 博客頻道 - CSDN.NET


交易.py


github上GitHub - brennerm/PyTricks: Collection of less popular features and tricks for the Python programming language可以看下


Python 3 -- 人生苦短請用python


自省 【反射】 , ~~佔位再答~~

上代碼片段

@classmethod

def _revert_method(cls, name):

""" Revert the original method called `name` in the given class.

See :meth:`~._patch_method`.

"""

method = getattr(cls, name)

setattr(cls, name, method.origin)

居然不支持 markdown


Monkey Patch


黑是吧:

In python 2 and 3:

&>&>&> type(abc)
&
&>&>&> t1 = (abc, qwert)
&>&>&> type(t1)
& # t1 is a tuple
&>&>&> t2 = (abc)
&>&>&> type(t2)
& # t2 is a string
&>&>&> t3 = abc,
&>&>&> type(t3)
& # t3 is a tuple

One of the interesting pythonic things.....

And Python Humor

e.g.

&>&>&> 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 arent 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 youre Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, its a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- lets do more of those!


__call__函數!!!openstack里到處是這東西!!!


mro, descriptor...


Pythnoic 當年有個網站靠這個省了幾個人的工作量。


推薦閱讀:

MongoDB及可視化工具的安裝
中科大 Freeshell 上面有哪些好玩或者實用的應用?

TAG:Python |