Python-技巧
0.推薦的擴展
requests urilib的替代品
BeautifulSoup4 解析HTML代碼
rq 任務隊列
selenium 自動化測試工具,模擬瀏覽器
1.sys.modules, 根據已加載的模塊創建對象, 其中 page 為類名
if hasattr(sys.modules[__name__], page):setattr(self, page, getattr(sys.modules[__name__], page)(self))2.hasattr, setattr, locals(), globals() 動態創建變量
3.綁定、未綁定
未綁定的類方法: 沒有self
通過類來引用方法返回一個未綁定方法對象, 要調用它, 你必須顯示地提供一個實例作為第一個參數
綁定的實例方法: 有self
通過實例訪問方法返回一個綁定的方法對象, Python自動地給方法綁定一個實例, 所以我們調用它時不用再傳一個實例參數
class Test:def func(self,message):print messageobject1 = Test() x = object1.func x('綁定方法對象,實例是隱含的')t=Test.func t(object1,'未綁定的方法對象,需要傳遞一個實例') #t('未綁定的方法對象,需要傳遞一個實例') #錯誤的調用object1 = Test()生成一個實例, object1.func返回一個綁定的方法, 把實例object1和方法func綁定, 而Test.func是用類去引用方法, 我們得到一個未綁定的方法對象, 要調用它就得傳一個實例參數, 如t(object1,'未綁定的方法對象,需要傳遞一個實例')
class A(object): def f(self): print "f"def ff(): print "ff" a = A() a.f() xf = a.f #xf為綁定的方法, 故不用傳入第一個值 xf() a.f = ff a.f()4.MRO(method resolution order, 多繼承時判斷調的屬性的類)
MRO要先確定一個線性序列, 然后查找路徑由由序列中類的順序決定, 所以MRO的算法就是生成一個線性序列
Python先后有三種不同的MRO: 經典方式、Python2.2 新式算法、Python2.3 新式算法(C3), Python 3中只保留了最后一種, 即C3算法
經典方式: 非常簡單, 深度優先, 按定義從左到右
新式算法: 還是經典方式, 但出現重復的, 只保留最后一個
C3算法: 最早被提出是用于Lisp的, 應用在Python中是為了解決原來基于深度優先搜索算法不滿足本地優先級, 和單調性的問題:
本地優先級: 指聲明時父類的順序, 比如C(A, B), 如果訪問C類對象屬性時, 應該根據聲明順序, 優先查找A類, 然后再查找B類
單調性: 如果在C的解析順序中, A排在B的前面, 那么在C的所有子類里, 也必須滿足這個順序
class B(A) 這時B的mro序列為[B,A]
class B(A1,A2,A3 ...)?這時B的mro序列 mro(B) = [B] + merge(mro(A1), mro(A2), mro(A3) ..., [A1,A2,A3])
merge操作就是C3算法的核心, 遍歷執行merge操作的序列, 如果一個序列的第一個元素, 是其他序列中的第一個元素, 或不在其他序列出現, 則從所有執行merge操作序列中刪除這個元素, 合并到當前的mro中, merge操作后的序列, 繼續執行merge操作, 直到merge操作的序列為空, 如果merge操作的序列無法為空, 則說明不合法
class a:pass class b(a):pass class c(a):pass class d(b, c):pass比如此菱形繼承結構
按經典方式, d類MRO為dbaca, 缺點是如果c類重寫了a類中得方法, c類的方法將不會被調用到(即本地優先順序問題)
按新式算法, d類MRO為dbca, 缺點是單調性,?比如d繼承b、c, 且b在c的前面, 如果f繼承d, 那么f的MRO中也應該和d的一樣b在c的前面, 單調性即繼承時要保持順序, 現在e繼承c、b, 且c在b的前面, f繼承d, e時, bc的順序就沒法決定了, 無論怎樣排都違反了單調性
class A(O):pass class B(O):pass class C(O):pass class E(A,B):pass class F(B,C):pass class G(E,F):pass 按C3算法,?A、B、C都繼承至一個基類, 所以mro序列依次為[A,O]、[B,O]、[C,O]
mro(E) = [E] + merge(mro(A), mro(B), [A,B])
?????? = [E] + merge([A,O], [B,O], [A,B])
此時需要執行merge操作的序列為[A,O]、[B,O]、[A,B]
A是序列[A,O]中的第一個元素, 在序列[B,O]中不出現, 在序列[A,B]中也是第一個元素, 所以從執行merge操作的序列([A,O]、[B,O]、[A,B])中刪除A, 合并到當前mro, [E]中
mro(E) = [E,A] + merge([O], [B,O], [B])
再執行merge操作, O是序列[O]中的第一個元素, 但O在序列[B,O]中出現并且不是其中第一個元素, 繼續查看[B,O]的第一個元素B, B滿足條件, 所以從執行merge操作的序列中刪除B, 合并到[E, A]中
mro(E) = [E,A,B] + merge([O], [O])
?????? = [E,A,B,O]
實現C3算法的代碼:
#-*- encoding:GBK -*-# def mro_C3(*cls): if len(cls)==1: if not cls[0].__bases__: return cls else: return cls+ mro_C3(*cls[0].__bases__) else: seqs = [list(mro_C3(C)) for C in cls ] +[list(cls)] res = [] while True: non_empty = list(filter(None, seqs)) if not non_empty: return tuple(res) for seq in non_empty: candidate = seq[0] not_head = [s for s in non_empty if candidate in s[1:]] if not_head: candidate = None else: break if not candidate: raise TypeError("inconsistent hierarchy, no C3 MRO is possible") res.append(candidate) for seq in non_empty: if seq[0] == candidate: del seq[0]查看一個類的MRO列表, 可以使用 classname.__mro__
5.super 避免多次調用基類
# -*- coding:utf-8 -*- class D(object): def foo(self): print "class D" class B(D): pass class C(D): def foo(self): print "class C" class A(B, C): pass f = A() f.foo() #A的實例對象f在調用foo函數的時候, 根據廣度優先搜索原則, 調用的是C類里面的foo函數, 上面的代碼輸出class C; 如果定義D類的時候直接class D, 而不是class D(object), 那么上述代碼就該輸出class D了1). super并不是一個函數, 是一個類名, 形如super(B, self)事實上調用了super類的初始化函數產生了一個super對象;
2). super類的初始化函數并沒有做什么特殊的操作, 只是簡單記錄了類類型和具體實例;
3). super(B, self).func的調用并不是用于調用當前類的父類的func函數;
4). Python的多繼承類是通過mro的方式來保證各個父類的函數被逐一調用, 而且保證每個父類函數只調用一次(如果每個類都使用super);
5). 混用super類和非綁定的函數是一個危險行為, 這可能導致應該調用的父類函數沒有調用或者一個父類函數被調用多次
6).super并不是像我們想象中一樣直接找到當前類的父類, 而是沿著mro順藤摸瓜
參考:http://blog.csdn.net/johnsonguo/article/details/585193
6.模塊即模塊對象
7.利用 __new__ 實現單例
class Singleton(object):def __new__(cls, *args, **kwargs):# 關鍵在于這,每一次實例化的時候,我們都只會返回這同一個instance對象if not hasattr(cls, "instance"):cls.instance = super(Singleton, cls).__new__(cls, *args, **kwargs)return cls.instance8.__new__ 執行順序
class A(object):def __new__(cls):Object = super(A, cls).__new__(cls)print "in New"return Object #如果把此行注釋掉, 則不會執行 __init__ 方法def __init__(self):print "in init"class B(A):def __init__(self):print "in B's init"B()9.判斷一個變量是否存在
1).
'var' in locals().keys()2).
try:print var except NameError:print 'var not defined'3).
'var' in dir()10.根據類名創建實例, 主要是獲得類的 class 對象
1).類名cls為class類型, 直接 cls() 即可
2).類名cls為str類型
m = __import__(clsstr所在模塊名) cls = getattr(m, clsstr) cls()如果myclass并不在mymodule的自動導出列表中(__all__), 則必須顯式地導入, __import__('mymodule', globals(), locals(), ['myclass'])
11.獲取當前模塊名
locals()/globals().get("__name__")
12.import, reload, __import__
del sys.modules[modulename] 即可實現 unimport
import 調用的 __import__, 比如 import sys => sys = __import__("sys")
reload 對已經加載的模塊進行重新加載, 一般用于原模塊有變化等特殊情況, reload前該模塊必須已經import過, 但原來已經使用的實例還是會使用舊的模塊, 新生產的實例會使用新的模塊
import sys #引用sys模塊進來,并不是進行sys的第一次加載 reload(sys) #重新加載sys sys.setdefaultencoding('utf8') ##調用setdefaultencoding函數 #如果去掉reload(sys), 會執行失敗, 因為這里的import語句其實并不是sys的第一次導入語句, 也就是說這里其實可能是第二、三次進行sys模塊的import, 這里只是一個對sys的引用, 只能reload才能進行重新加載; 那么為什么要重新加載, 而直接引用過來則不能調用該函數呢?因為setdefaultencoding函數在被系統調用后被刪除了, 所以通過import引用進來時其實已經沒有了, 所以必須reload一次sys模塊, 這樣setdefaultencoding才會為可用, 才能在代碼里修改解釋器當前的字符編碼__import__, 返回模塊實例
__import__(module_name[, globals[, locals[, fromlist]]]) #可選參數默認為globals(),locals(),[] __import__('os') __import__('os',globals(),locals(),['path','pip']) #等價于from os import path, pip13.迭代器
基本原理:
實現了__iter__方法的對象是可迭代的, 實現了next()方法的對象是迭代器(迭代器就是一個有next()方法的對象), 所以iter(實現了__iter__方法的對象)會調用此對象的__iter__方法, 返回一個實現了next()方法的迭代器對象, 不斷調用此迭代器對象的next()方法, 實現遍歷,?直到遇到StopIteration異常, 使用迭代器一個顯而易見的好處就是每次只從對象中讀取一條數據, 不會造成內存的過大開銷
序列、字典、文件中當使用for x in y的結構時, 其實質就是迭代器, 迭代器是和實際對象綁定在一起的, 所以在使用迭代器時或者上述3者時不能修改可變對象的值, 這會產生錯誤
迭代器不要求你事先準備好整個迭代過程中所有的元素, 迭代器僅僅在迭代至某個元素時才計算該元素, 而在這之前或之后, 元素可以不存在或者被銷毀, 這個特點使得它特別適合用于遍歷一些巨大的或是無限的集合, 比如幾個G的文件, 或是斐波那契數列等等, 這個特點被稱為延遲計算或惰性求值(Lazy evaluation)
創建迭代器的方法: iter(object)和iter(func, sentinel)兩種, 一種使用的是序列, 另一種使用類來創建, 迭代器更大的功勞是提供了一個統一的訪問集合的接口, 只要是實現了__iter__()方法的對象, 就可以使用迭代器進行訪問,?返回一個對象, 這個對象擁有一個next()方法, 這個方法能在恰當的時候拋出StopIteration異常即可
for i in seq: do_something_to(i)實際為:
fetch = iter(seq) while True: try: i = fetch.next() except StopIteration: break do_something_to(i)字典中, iterkeys(), itervalues(), iteritems() 比 keys(), values(), items() 更省內存
open("test.txt").readlines() 返回的是列表,?open("test.txt") 返回的是迭代器
14.生成器
如果一個函數返回的列表非常大, 僅僅創建這個列表就會用完系統所有內存, 因為在我們的觀念中函數只有一次返回結果的機會, 因而必須一次返回所有的結果, 此類問題可以用生成器解決
生成器是特定的函數, 允許你返回一個值, 然后“暫停”代碼的執行, 稍后恢復, 生成器使用了“延遲計算”, 所以在內存上面更加有效, 生成器函數不能有返回值, 因為 yield 的值就是返回值, 生成器就是一類特殊的迭代器
1).調用生成器函數將返回一個生成器
>>> generator = get_0_1_2() >>> generator <generator object get_0_1_2 at 0x00B1C7D8>2).第一次調用生成器的next方法時, 生成器才開始執行生成器函數(而不是構建生成器時), 直到遇到yield時暫停執行(掛起), 并且yield的參數將作為此次next方法的返回值
>>> generator.next() 03).之后每次調用生成器的next方法, 生成器將從上次暫停執行的位置恢復執行生成器函數, 直到再次遇到yield時暫停, 并且同樣的, yield的參數將作為next方法的返回值
>>> generator.next() 1 >>> generator.next() 24).如果當調用next方法時生成器函數結束(遇到空的return語句或是到達函數體末尾), 則這次next方法的調用將拋出StopIteration異常(即for循環的終止條件)
>>> generator.next() Traceback (most recent call last):File "<stdin>", line 1, in <module> StopIteration5).生成器函數在每次暫停執行時, 函數體內的所有變量都將被封存(freeze)在生成器中, 并將在恢復執行時還原, 并且類似于閉包, 即使是同一個生成器函數返回的生成器, 封存的變量也是互相獨立的, 我們的小例子中并沒有用到變量, 所以這里另外定義一個生成器來展示這個特點
>>> def fibonacci(): ... a = b = 1 ... yield a ... yield b ... while True: ... a, b = b, a+b ... yield b ... >>> for num in fibonacci(): ... if num > 100: break ... print num, ... 1 1 2 3 5 8 13 21 34 55 89看到while True可別太吃驚, 因為生成器可以掛起, 所以是延遲計算的, 無限循環并沒有關系, 這個例子中我們定義了一個生成器用于獲取斐波那契數列
如果生成器函數調用了return, 或者執行到函數的末尾, 會出現一個StopIteration異常
有一篇好文章:點擊進入
15.列表解析器
也可以直接生成 dict
{x: 1 for x in ["name", "sex"]} #返回 {"name": 1, "sex" 1}列表解析一次生成一個列表, 所占內存較大
(x+1 for x in lst) #生成器表達式,返回迭代器。外部的括號可在用于參數時省略。 [x+1 for x in lst] #列表解析,返回list由于返回迭代器時, 并不是在一開始就計算所有的元素, 這樣能得到更多的靈活性并且可以避開很多不必要的計算, 所以除非你明確希望返回列表, 否則應該始終使用生成器表達式
可以在Python Shell中試一下一下兩個語句的執行時間
(for x in range(1000000)) #返回生成器 [for x in range(1000000)] #返回整個列表或者提供多條for子句進行嵌套循環, 嵌套次序就是for子句的順序:
((x, y) for x in range(3) for y in range(x))同樣外部也可以使用 if
(x for x in (y.doSomething() for y in lst) if x>0)16.在Python里, 函數的默認值實在函數定義的時候實例化的, 而不是在調用的時候, 如果在調用函數的時候重寫了默認值, 那么這個存儲的值就不會被使用, 當你不重寫默認值的時候, 那么Python就會讓默認值引用存儲的值(這個例子里的numbers)
def abc(numbers = []):numbers.append(9)print numbersabc(): [9] #第一次執行
abc(): [9, 9] #第二次執行
abc([1, 2]): [1, 2, 9] #第三次執行
abc(): [9, 9, 9] #第四次執行
def print_now(now = time.time()):print now #如果不加參數, 則每次都會返回相同的時間默認參數最好指向不變對象!
17.當def這個聲明執行的時候, Python會靜態地從函數的局部作用域里獲取信息,?當來到 xxx = yyy 這行的時候(不是執行到這行代碼, 而是當Python解釋器讀到這行代碼的時候), 它會把xxx這個變量加入到函數的局部變量列表里
18.__builtin__模塊, 在Python啟動后、且沒有執行程序員所寫的任何代碼前, Python會首先加載該內建模塊到內存, 另外, 該內建模塊中的功能可以直接使用, 不用在其前添加內建模塊前綴, 導入僅僅是讓__builitin__標識符在該作用域內可見
19.from __future__ import xxx, 必須是模塊或程序的第一個語句, 此外,'__ future__' 模塊中存在的特性最終將成為Python語言標準的一部分, 到那時, 將不再需要使用 '__future__' 模塊
20.序列解包
a, b, c = 1, 2, 3 #賦值 a, b = b, a #轉換a, b的值 a = 1, #聲明元組21.最好了解下二進制文件和文本文件的區別以及編碼問題, ASCII(固定一個字節)->Unicode(固定兩個字節)->UTF-8(變長), 在計算機內存中, 統一使用Unicode編碼, 當需要保存到硬盤或者需要傳輸的時候, 就轉換為UTF-8編碼, 用記事本編輯的時候, 從文件讀取的UTF-8字符被轉換為Unicode字符到內存里, 編輯完成后, 保存的時候再把Unicode轉換為UTF-8保存到文件, len()函數計算的是str(Unicode編碼)的字符數, 如果換成bytes(UTF-8等編碼), len()函數就計算字節數:
參考自:廖雪峰Python
22.dict的key必須是不可變對象
23.Python函數參數順序
定義: (arg, kwarg = <value>, *args, **kwargs)
調用:
第一, 按順序把"arg"這種形式的實參給對應的形參
第二, 把"arg=<value>"這種形式的實參賦值給形式kwarg
第三, 把多出來的"arg"這種形式的實參組成一個tuple給帶一個星號的形參args
第四, 把多出來的"key=value"這種形式的實參轉為一個dictionary給帶兩個星號的形參kwargs
test(1,2) ===> 1 2 () {}
test(1,2,3)? ===> 1 2 (3,) {}
test(1,2,3,4) ===> 1 2 (3,4)
test(x=1)?? ===> 1 5 () {}
test(x=1,y=1)? ===> 1 1 () {}
test(x=1,y=1,a=1)?? ===> 1 1 () {'a':1}
test(x=1,y=1,a=1,b=1)?? ===> 1 1 () {'a':1,'b':1}
test(1,y=1) ===> 1 1 () {}
test(1,2,y=1) ===> 出錯, 說y給賦了多個值
test(1, y = 2, 3, a = 4) ===> 出錯, non-keyword arg after keyword arg
test(1,2,3,4,a=1) ===> 1 2 (3,4) {'a':1}
24.限制 **kwargs 參數個數
#只接收city和job作為關鍵字參數 def person(name, age, *, city, job):print(name, age, city, job)25.functools 模塊
wraps: 用來裝飾返回func的函數, 以同步func與被裝飾的函數的屬性, 比如__name__等 @functools.wraps(func)
partial: 變化函數的參數創建偏函數, 固定住原函數的部分參數, 比如 int2 = functools.partial(x, base = 2), 此時 int2 是一個把二進制字符串轉為十進制數字的函數, int2('10010')
26.一個.py文件稱為一個模塊, 為了避免模塊名沖突, Python按目錄來組織模塊的方法, 稱為包(Package), 包內必須有__init__.py, 因為__init__.py本身就是一個模塊, 它的模塊名就是包名(目錄名)
27.第三方庫都會在 Python 官方的 pypi.python.org 網站注冊, 要安裝一個第三方庫, 必須先知道該庫的名稱, 可以在官網或者 pypi 上搜索
28.各種包管理工具區別:
distribute是setuptools的取代, pip是easy_install的取代
distribute被創建是因為setuptools包不再維護了
29.Python的實例屬性必須在__init__(self) 方法中定義, 直接跟在類名后邊定義的屬性都默認是類屬性(類似于c++的static變量), 類的屬性一改, 只要沒有覆蓋此屬性的對象的此屬性都會改
main.py
#-*- coding: utf-8 -*-class Model():model_path = "online_model"model_dict = {}def __init__(self, path = None):self.reload(path)def reload(self, path = None):if not path:returnself.model_dict.clear()fp = file(path, "r")for line in fp:cols = line.split()self.model_dict[cols[0]] = cols[1]self.model_path = pathdef main():m1 = Model()m2 = Model()m1.reload("aa_model")m2.reload("bb_model")print m1.model_pathprint m2.model_pathprint m1.model_dictprint m2.model_dictreturn 0if __name__ == "__main__":main()aa_model
1 a2 b
bb_model
x 1 y 2 z 3結果如圖:
aa_model, bb_model: 由于reload方法中self.model_path = path, 實例重寫了類的model_path屬性(第一次賦值視為變量的定義), 故輸出本實例的model_path
{‘y': 2, 'x': '1', 'z': '3'},?{‘y': 2, 'x': '1', 'z': '3'}: 由于self.model_dict.clear(), 第一次使用此變量非賦值而是直接使用(類似PHP), 類似函數中表示使用已有的值, 根據MRO, 搜索到Model.model_dict, 故兩次會覆蓋同一個變量
30.類的特殊屬性
C.__name__: 類的名字
C.__doc__: 類的文檔描述
C.__bases__: 類的基類組成的元組
C.__dict__: 類的屬性
C.__module__: 類所在模塊
C.__class__: 類的類名
__getattribute__: 不管屬性是否存在都會調用
__getattr__: 如果屬性不存在則調用, 如果__getattribute__被定義會先被調用, 然后調用此方法, 也可以返回函數
class A(object):def __getattr__(self, attr):if attr == "age":return lambda : 25a = A() a.age()?
__setattr__
__delattr__
__slots__: 限制實例可以添加到屬性
hasattr(), getattr(), setattr(), delattr()
31.實例的特殊屬性
i.__class__: 實例的類名
i.__dict__: 實例的屬性, __dict__分層存儲屬性, 每一層的__dict__只存儲該層新增的屬性, 子類不需要重復存儲父類中的屬性
32.property()可以即時生成屬性
33.類中私有屬性__xx, 實現為Python自動為__xx改變變量名
34.當我們定義一個class的時候, 我們實際上就定義了一種數據類型, 我們定義的數據類型和Python自帶的數據類型, 比如str、list、dict沒什么兩樣, type(instance), 返回instance對應的Class類型, 比如
class A(object):passa = A() b = type(a)() #此時b為A的實例print type(A) #<type 'type'> print type(a) #<class '__main__.A'> print isinstance(b, A) #Truetype()函數可以查看一個類型或變量的類型, A是一個class(類對象), 它的類型就是type, 而h是一個實例, 它的類型就是class A
class的定義是運行時動態創建的, 而創建class的方法就是使用type()函數,?type()函數既可以返回一個對象的類型, 又可以創建出新的類型, 比如, 我們可以通過type()函數創建出A類, 而無需通過class A(object)...的定義,?
def fn(self, name = "John"):self.name = nameA = type('A', (object,), dict(hello=fn)) a = A() print type(A) #<type 'type'> print type(a) #<class '__main__.A'>type() 創建類對象的參數如下:
class的名稱
繼承的父類集合, 注意Python支持多重繼承, 如果只有一個父類, 別忘了tuple的單元素寫法
class的方法名稱與函數綁定, 這里我們把函數fn綁定到方法名hello上
通過type()函數創建的類和直接寫class是完全一樣的, 因為Python解釋器遇到class定義時, 僅僅是掃描一下class定義的語法, 然后調用type()函數創建出class
除了使用type()動態創建類對象, 還可以使用元類, 即 metaclass, 流程為先定義metaclass, 就可以創建類, 最后創建實例
# metaclass是創建類,所以必須從`type`類型派生: class ListMetaclass(type):def __new__(cls, name, bases, attrs):attrs['add'] = lambda self, value: self.append(value)return type.__new__(cls, name, bases, attrs)class MyList(list):__metaclass__ = ListMetaclass # 指示使用ListMetaclass來定制類, 表示創建MyList時要通過ListMetaclass.__new__()來創建, 在此, 我們可以修改類的定義, 比如, 加上新的方法, 然后, 返回修改后的定義__new__()方法接收到的參數依次是:當前準備創建的類的對象, 類的名字, 類繼承的父類集合, 類的方法集合
35.types模塊, 可以查看所有的type, type(abs) == types.BuiltinFunctionType
36.把方法變成屬性 @property
37.Linux下用Python實現多進程(此處不是講線程), Unix/Linux操作系統提供了一個fork()系統調用, 它返回兩次, 因為操作系統自動把當前進程(稱為父進程)復制了一份(稱為子進程), 然后, 分別在父進程和子進程內返回, 子進程永遠返回0, 而父進程返回子進程的ID, 這樣做的理由是, 一個父進程可以fork出很多子進程, 所以, 父進程要記下每個子進程的ID, 而子進程只需要調用getppid()就可以拿到父進程的ID; Windows系統不支持fork()函數; 有了fork調用, 一個進程在接到新任務時就可以復制出一個子進程來處理新任務, 常見的Apache服務器就是由父進程監聽端口, 每當有新的http請求時, 就fork出子進程來處理新的http請求
import osprint('Process (%s) start...' % os.getpid()) # Only works on Unix/Linux/Mac: pid = os.fork() if pid == 0:print('I am child process (%s) and my parent is %s.' % (os.getpid(), os.getppid())) else:print('I (%s) just created a child process (%s).' % (os.getpid(), pid))38.multiprocessing模塊是跨平臺的多進程模塊
創建一個進程運行某個函數
from multiprocessing import Process import os# 子進程要執行的代碼 def run_proc(name):print('Run child process %s (%s)...' % (name, os.getpid()))if __name__=='__main__':print('Parent process %s.' % os.getpid())p = Process(target=run_proc, args=('test',))print('Child process will start.')p.start()p.join()print('Child process end.')結果為:
Parent process 67416. Child process will start. Run child process test (70388)... Child process end.join()方法可以等待子進程結束后再繼續往下運行, 通常用于進程間的同步
39.占位
40.timestamp與時區毫無關系, timestamp一旦確定, 其UTC時間就確定了, 轉換到任意時區的時間也是完全確定的, 全球各地的計算機在任意時刻的timestamp都是完全相同的, datetime是有時區的, 會在timestamp與本地時間之間做轉換
>>> from datetime import datetime >>> t = 1429417200.0 >>> print(datetime.fromtimestamp(t)) # 本地時間 2015-04-19 12:20:00 >>> print(datetime.utcfromtimestamp(t)) # UTC時間 2015-04-19 04:20:0041.struct模塊, 了解c語言的人, 一定會知道struct結構體在c語言中的作用, 它定義了一種結構, 里面包含不同類型的數據(int, char, bool等等), 方便對某一結構對象進行處理, 而在網絡通信當中, 大多傳遞的數據是以二進制流(binary data)存在的, 當傳遞字符串時, 不必擔心太多的問題, 而當傳遞諸如int、char之類的基本數據的時候, 就需要有一種機制將某些特定的結構體類型打包成二進制流的字符串然后再網絡傳輸, 而接收端也應該可以通過某種機制進行解包還原出原始的結構體數據, python中的struct模塊就提供了這樣的機制, 該模塊的主要作用就是對python基本類型值與用python字符串格式表示的C struct類型間的轉化.
>>>import struct >>>ss = struct.pack("!H4s2I", 20, "abcd", 6, 7) >>>ss "\x00\x14abcd\x00\x00\x00\x06\x00\x00\x00\x07" #ss是一個字符串 類似c結構體的字節流(二進制)的字符串表示 >>>struct.unpack("!H4s2I", ss) (20, 'abcd', 6, 7)H表示 一個unsigned short的id, 4s表示4字節長的字符串, 2I表示有兩個unsigned int類型的數據
struct中支持的格式如下表
| x | pad byte | no value | 1 |
| c | char | string of length 1 | 1 |
| b | signed char | integer | 1 |
| B | unsigned char | integer | 1 |
| ? | _Bool | bool | 1 |
| h | short | integer | 2 |
| H | unsigned short | integer | 2 |
| i | int | integer | 4 |
| I | unsigned int | integer or long | 4 |
| l | long | integer | 4 |
| L | unsigned long | long | 4 |
| q | long long | long | 8 |
| Q | unsigned long long | long | 8 |
| f | float | float | 4 |
| d | double | float | 8 |
| s | char[] | string | 1 |
| p | char[] | string | 1 |
| P | void * | long |
另一方面, 打包的后的字節順序默認上是由操作系統的決定的, 當然struct模塊也提供了自定義字節順序的功能, 可以指定大端存儲、小端存儲等特定的字節順序, 對于底層通信的字節順序是十分重要的, 不同的字節順序和存儲方式也會導致字節大小的不同; 在format字符串前面加上特定的符號即可以表示不同的字節順序存儲方式, 例如采用小端存儲 s = struct.Struct(‘<I3sf’)就可以了, 官方api library 也提供了相應的對照列表:
| @ | native | native??????????? 湊夠4個字節 |
| = | native | standard??????? 按原字節數 |
| < | little-endian | standard??????? 按原字節數 |
| > | big-endian | standard?????? 按原字節數 |
| ! | network (= big-endian) | standard?????? 按原字節數 |
利用struct解析BMP文件頭, 首先找一個bmp文件, 沒有的話用“畫圖”畫一個, 讀入前30個字節來分析:
>>> s = b'\x42\x4d\x38\x8c\x0a\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00\x00\x00\x80\x02\x00\x00\x68\x01\x00\x00\x01\x00\x18\x00'BMP格式采用小端方式存儲數據,文件頭的結構按順序如下:? 兩個字節:'BM'表示Windows位圖,'BA'表示OS/2位圖; 一個4字節整數:表示位圖大小; 一個4字節整數:保留位,始終為0; 一個4字節整數:實際圖像的偏移量; 一個4字節整數:Header的字節數; 一個4字節整數:圖像寬度; 一個4字節整數:圖像高度; 一個2字節整數:始終為1; 一個2字節整數:顏色數。? 所以,組合起來用unpack讀取:
>>> struct.unpack('<ccIIIIIIHH', s) (b'B', b'M', 691256, 0, 54, 40, 640, 360, 1, 24)結果顯示,b'B'、b'M'說明是Windows位圖,位圖大小為640x360,顏色數為24。
42.快速創建字典
{}.from_keys(list, defaultValue)
dict((key1, value1), (key2, value2), (key3, value3)...)
zip([key1, key2, key3...], [value1, value2, value3...])
dict(name = "John", "age" = 26)
43.判斷字典中鍵名是否存在的兩種方法
#第一種 d.has_key()#第二種 "" in d.keys()44.判斷對象中屬性/方法是否存在
hasattr(object, attr)attr in dir(object)
45.動態調用方法
def _execute(self, sql, params, isMany):func = "executemany" if isMany else "execute"func = getattr(self._db["cur"], func)return func(sql, params)46.False, 0, '', (), [], {}都可以視為假
47.列表內數據唯一(類似PHP中array_unique)
set([1, 2, 1]) -- set([1, 2])48.根據一個對象創建另一個對象
obj.__class__(...)49.collections模塊
collections.namedtuple 創建可命名的tuple
import collections as cs Point = cs.namedtuple("Point", ["x", "y", "z"]) p = Point(1, 2, 3)print p.x, p.y, p.zcollections.deque 為了高效實現插入和刪除操作的雙向列表,增刪效率高于 list,也可以使用 deque(maxlen = 10) 來限制列表長度,超出時添加會把頭尾的元素擠出。
import collections as cs q = cs.deque(['a', 'b', 'c']) q.append('x') q.appendleft('y') print q>>>deque(['y', 'a', 'b', 'c', 'x'])collections.defaultdict 使用dict時,如果引用的Key不存在,就會拋出KeyError。如果希望key不存在時,返回一個默認值,就可以用defaultdict,也可以直接創建多維字典。
import collections as cstree = lambda: collections.defaultdict(tree)d = tree() d["names"]["John"] = "ABC"collections.OrderedDict 創建按key值遍歷時有序的dict(普通dict無序排列) 使用
50.禁止創建對象屬性字典。每個類都有實例屬性,默認用一個字典保存一個對象的實例屬性,允許我們設置任意屬性,但是可能會浪費很多內存。使用__slots__高速Python不要使用字典,而且只給一個固定集合的屬性分配空間。
class MyClass(obj ect) : __slots__ = [ ' name' , ' identifier' ] def __init__(self, name, identifier) : self. name = name self. identifier = identifier self. set_up()51.文件遍歷
with open("foo.txt", "r") as f:for line in f:# do_something(line)for line in open("foo.txt", "r"):# do_something(line)52.打印格式化的json
import json print json.dumps({"name": "John"}, indent = 2)53.格式化輸出字典
print("I'm %(name)s. I'm %(age)d year old" % {'name':'Vamei', 'age':99})print("I'm {name}.I'm {age} year old").format(name = "John", age = 26)
54.字典和集合也有列表推導式,都使用{}包圍,分別返回字典和集合
#快速兌換字典鍵—值 mca={"a":1, "b":2, "c":3, "d":4} dicts={v:k for k,v in mca.items()} #{1: 'a', 2: 'b', 3: 'c', 4: 'd'}55.字面量,即 {}, [] 等直接表示。
56.從標準輸入獲取數據,除了 input、raw_input 也可以使用如下方式
import sys sys.stdin.readline().strip()57.數據庫 IN 查詢的正確姿勢不是如下
id_list = [1, 2, 3] arg_list = ','.join(['%s'] * len(id_list)) cursor.execute('SELECT col1, col2 FROM table1 WHERE id IN (%s)' % arg_list, id_list)而是如下(參考資料:http://ju.outofmemory.cn/entry/51481):
id_list = [1, 2, 3] cursor.execute('SELECT col1, col2 FROM table1 WHERE id IN %s', (id_list,))58.
總結
- 上一篇: 剑指offer-面试题13.在O(1)时
- 下一篇: Resin的安全性ip限制