知識來源:runoob
1、python用法
1.1 標識符
1)第一個字符必須是字母表中字母或下劃線 _ 。
2)標識符的其他的部分由字母、數字和下劃線組成。
3)標識符對大小寫敏感。
1.2 保留字
>>> import keyword >>> keyword.kwlist ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] |
1.3 標準數據類型 (序列操作包括索引,切片,加,乘,檢查成員)
不可變:Number、String、Tuple (元組);
可變:List [列表]、Dictionary {字典}、Set {集合}。
1.4 運算符
and |
布爾"與" - 如果 x 爲 False,x and y 返回 False,否則它返回 y 的計算值。 |
類似java的&& |
or |
布爾"或" - 如果 x 是 True,它返回 x 的值,否則它返回 y 的計算值。 |
類似java的|| |
not |
布爾"非" - 如果 x 爲 True,返回 False 。如果 x 爲 False,它返回 True。 |
類似java的! |
is |
is 是判斷兩個標識符是不是引用自一個對象 |
x is y, 類似 id(x) == id(y) |
is not |
is not 是判斷兩個標識符是不是引用自不同對象 |
x is not y , 類似 id(a) != id(b) |
1.5 條件控制 (在Python中沒有switch – case語句)
if condition_1: statement_block_1 elif condition_2: statement_block_2 else: statement_block_3 |
1.6 循環語句
1)指定區間的值和步長,以會生成數列,for i in range(0, 10, 3):
2)遍歷一個序列的索引,for i in range(len(<sequence>)):
3)pass是空語句,是爲了保持程序結構的完整性。
while <expr>: <statement(s)> else: <additional_statement(s)>
for <variable> in <sequence>: <statements> else: <statements> |
1.7 迭代器
1)創建一個迭代器:
__iter__() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 __next__() 方法並通過 StopIteration 異常標識迭代的完成。
__next__() 方法(Python 2 裏是 next())會返回下一個迭代器對象。
2)使用了 yield 的函數被稱爲生成器(generator),其返回的是一個迭代器對象:
在調用生成器運行的過程中,每次遇到 yield 時函數會暫停並保存當前所有的運行信息,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續運行。
1.8 函數
def functionname(formal_args [,var_arg_default = 35] [,*var_args_tuple], [**var_args_dict]): "函數_文檔字符串" function_suite return [expression] |
1)必需參數:(必須傳入一個參數,不然會出現語法錯誤)
2)關鍵字參數:調用時:functionname(formal_args = 35 )
3)默認參數:
4)不定長參數:
加了星號 * 的參數會以元組(tuple)的形式導入
加了兩個星號 ** 的參數會以字典的形式導入
匿名函數:
lambda [arg1 [,arg2,.....argn]]:expression #例子 sum = lambda arg1, arg2: arg1 + arg2 # 調用sum函數 print ("相加後的值爲 : ", sum( 10, 20 )) |
1.9 推導式 (列表、字典、集合)
[<expression with variable> for <variable> in <sequence> [condition]] #例子 >>> [[x, x**2] for x in [2, 4, 6] if x > 3] [[4, 16], [6, 36]] >>> matrix = [[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12],] >>> [[row[i] for row in matrix] for i in range(4)] [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]] |
1.10 del 語句
使用 del 語句可以從一個列表中依索引而不是值來刪除一個元素。這與使用 pop() 返回一個值不同。可以用 del 語句從列表中刪除一個切割,或清空整個列表
1.11 字典創建
>>> {'jack': 4098, 'sape': 4139} >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)]) >>> {x: x**2 for x in (2, 4, 6)} >>> dict(sape=4139, guido=4127, jack=4098) |
1.12 遍歷技巧
1)在字典中遍歷時,關鍵字和對應的值可以使用 items() 方法同時解讀出來:
>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} >>> for k, v in knights.items(): ... print(k, v) |
2)在序列中遍歷時,索引位置和對應值可以使用 enumerate() 函數同時得到:
>>> for i, v in enumerate(['tic', 'tac', 'toe']): ... print(i, v) |
3)同時遍歷兩個或更多的序列,可以使用 zip() 組合:
注意:
>>> questions = ['name', 'quest', 'favorite color'] >>> answers = ['lancelot', 'the holy grail', 'blue'] >>> for q, a in zip(questions, answers): ... print('What is your {0}? It is {1}.'.format(q, a)) |
1.13 模塊
模塊是一個包含所有你定義的函數和變量的文件,其後綴名是.py。模塊可以被別的程序引入,以使用該模塊中的函數等功能。
每個模塊都有一個__name__屬性,當其值是'__main__'時,表明該模塊自身在運行,否則是被引入。
包是一種管理 Python 模塊命名空間的形式,採用"點模塊名稱"。目錄只有包含一個叫做 __init__.py 的文件纔會被認作是一個包。
無論是隱式的還是顯式的相對導入都是從當前模塊開始的。主模塊的名字永遠是"__main__",一個Python應用程序的主模塊,應當總是使用絕對路徑引用。
1.14 輸入和輸出
str.format():格式化輸出值
str(): 函數返回一個用戶易讀的表達形式
repr(): 產生一個解釋器易讀的表達形式
1.15 錯誤和異常
1)用戶自定義異常:當創建一個模塊有可能拋出多種不同的異常時,一種通常的做法是爲這個包建立一個基礎異常類,然後基於這個基礎類爲不同的錯誤情況創建不同的子類。
>>>class MyError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) |
2)預定義的清理行爲:關鍵詞 with 語句就可以保證諸如文件之類的對象在使用完之後一定會正確的執行他的清理方法
with open("myfile.txt") as f: for line in f: print(line, end="") |
3) 異常處理
try: <expression> except IOError: <expression> raise else: <expression> finally: <expression> |
1.16 面向對象
1)類的方法:
在類的內部,使用 def 關鍵字來定義一個方法,與一般函數定義不同,類方法必須包含參數 self,且爲第一個參數。
self 代表的是類的實例,代表當前對象的地址。
self 的名字並不是規定死的,也可以使用 this,但是最好還是按照約定是用 self。
2)類的私有屬性:
兩個下劃線開頭,聲明該屬性爲私有,不能在類的外部被使用或直接訪問。在類內部的方法中使用時 self.__private_attrs。
3)類的私有方法:
兩個下劃線開頭,聲明該方法爲私有方法,只能在類的內部調用 ,不能在類的外部調用。self.__private_methods。
4)繼承 class DerivedClassName(modname.BaseClassName):
5)多繼承 class DerivedClassName(Base1, Base2, Base3):
需要注意圓括號中父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法。
6)方法重寫:
c = Child() # 子類實例 c.myMethod() # 子類調用重寫方法 super(Child,c).myMethod() #用子類對象調用父類已被覆蓋的方法 |
1.17 命名空間和作用域
1)命名空間 :(命名空間查找順序爲:局部-> 全局-> 內置,命名空間的生命週期取決於對象的作用域)
①、內置名稱, Python 語言內置的名稱,比如函數名 abs、char 和異常名稱 BaseException、Exception 等等。
②、全局名稱,模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。
③、局部名稱,函數中定義的名稱,記錄了函數的變量,包括函數的參數和局部定義的變量。(類中定義的也是)
2) 作用域:
Python 中只有模塊,類以及函數、lambda纔會引入新的作用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)則不會,因此這些代碼塊內定義的變量,外部也可以訪問。
B(Built-in): 包含了內建的變量/關鍵字等
G(Global):當前腳本的最外層,比如當前模塊的全局變量。
E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。
L(Local):最內層,包含局部變量,比如一個函數/方法內部。
①、局部變量只能在其被聲明的函數內部訪問,而全局變量可以在整個程序範圍內訪問。調用函數時,所有在函數內聲明的變量名稱都將被加入到作用域中。
total = 0 # 這是一個全局變量 def sum( arg1, arg2 ): total = arg1 + arg2 # total在這裏是局部變量. return total |
②、當內部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字
num = 1 def fun1(): global num # 需要使用 global 關鍵字聲明 num = 123
def outer(): num = 10 def inner(): nonlocal num # nonlocal關鍵字聲明 num = 100 inner() |
1.18 補充
在交互模式中,最後被輸出的表達式結果被賦值給變量 _,此處, _ 變量應被用戶視爲只讀變量。
在整數除法中,除法 / 總是返回一個浮點數,如果只想得到整數的結果,丟棄可能的分數部分,可以使用運算符 // ,但是// 得到的並不一定是整數類型的數,它與分母分子的數據類型有關係。
type()不會認爲子類是一種父類類型,isinstance()會認爲子類是一種父類類型。
創建一個空集合必須用 set() 而不是 { },因爲 { } 是用來創建一個空字典。
2、用type動態生成類的方式
#type(類名,由父類名稱組成的元組(針對繼承的情況,可以爲空),包含屬性的字典(屬性名稱: 屬性值, 方法名: 已聲明的函數名)) args=13 def fn(self, name='world'): print('Hello,%s' % name) Hello =type('Hello', (object,), dict(hello=fn,age=args)) #所有的東西——都是對象。這包括整數、字符串、函數以及類。它們全部都是對象,而且它們都是從一個類創建而來,這個類就是type。 >>> args.__class__.__class__ <type 'type'> >>> fn.__class__.__class__ <type 'type'> |
3、python gil
GIL:即全局解釋器所(global interpreter lock),每個線程在執行時候都需要先獲取GIL,保證同一時刻只有一個線程可以執行代碼,即同一時刻只有一個線程使用CPU,也就是說多線程並不是真正意義上的同時執行。
python程序 -> cpython解析器(會上gil鎖,保證同時只有一個線程佔用cpu) -> [CPU執行]
3.1 如何解決GIL鎖的問題呢?
1)更換cpython爲jpython(不建議)
2)使用多進程完成多線程的任務
3)在使用多線程可以使用c語言去實現
3.2 什麼時候會釋放Gil鎖
1)非公平競爭:遇到像 i/o操作這種會有時間空閒情況造成cpu閒置的情況,會釋放Gil鎖。(釋放鎖的進程不參加競爭)
2)公平競爭:會有一個專門ticks進行計數一旦ticks數值達到100 的時候,會釋放Gil鎖。(線程之間開始競爭Gil鎖)
3.3 互斥鎖和Gil鎖的關係
Gil鎖 : 保證同一時刻只有一個線程能使用到cpu
互斥鎖 : 多線程時,保證修改共享數據時有序的修改,不會產生數據修改混亂
首先假設只有一個進程,這個進程中有兩個線程 Thread1,Thread2, 要修改共享的數據date, 並且有互斥鎖,執行以下步驟: (1)多線程運行,假設Thread1獲得GIL可以使用cpu,這時Thread1獲得 互斥鎖lock,Thread1可以改date數據(但並沒有開始修改數據); (2)Thread1線程在修改date數據前發生了 i/o操作 或者 ticks計數滿100 (注意就是沒有運行到修改data數據),這個時候 Thread1 讓出了Gil,Gil鎖可以被競爭; (3) Thread1 和 Thread2 開始競爭 Gil (注意:如果Thread1是因爲 i/o 阻塞 讓出的Gil Thread2必定拿到Gil,如果Thread1是因爲ticks計數滿100讓出Gil 這個時候 Thread1 和 Thread2 公平競爭); (4)假設 Thread2正好獲得了GIL, 運行代碼去修改共享數據date,由於Thread1有互斥鎖lock,所以Thread2無法更改共享數據date,這時Thread2讓出Gil鎖 , GIL鎖再次發生競爭 ; (5)假設Thread1又搶到GIL,由於其有互斥鎖Lock所以其可以繼續修改共享數據data,當Thread1修改完數據釋放互斥鎖lock,Thread2在獲得GIL與lock後纔可對data進行修改。 |