1. pass占位符 當(dāng)函數(shù)或者代碼塊中不需要寫任何代碼時(shí)需要顯式的寫pass, pass表示占位符, 如果不寫會(huì)報(bào)語法錯(cuò)誤。 在其它編程語言中如果一個(gè)方法是空的給出一對大括號{}就表示方法體是空的,,但是Python的方法體不是用大括號表示的而是用冒號+縮進(jìn)方式表示的,,Python為了表示一個(gè)空的方法體發(fā)明了這個(gè)pass占位符。 個(gè)人認(rèn)為使用縮進(jìn)方式而不是使用大括號{}形式表示方法體不好: - 使用縮進(jìn)方式當(dāng)粘貼復(fù)制代碼時(shí)就會(huì)丟失縮進(jìn),,這是一件很尷尬的事情,,粘貼好代碼還要非常認(rèn)真的看一下代碼縮進(jìn)是不是對的 - 使用縮進(jìn)方式是一個(gè)很隨意的決定。具體典故好像是隨便問了幾個(gè)不懂開發(fā)的人問哪種方式他們認(rèn)為最好,,結(jié)果選縮進(jìn)方式的比較多,,后來就這么草率的決定使用縮進(jìn)方式了。 public void test() { // java空方法體} 2. _ _ init_ _.pyPython中的init.py作用: 1. 標(biāo)識一個(gè)普通的文件夾為包Package,。在編程語言中一般都使用package來管理源文件,,用于對源文件分類,,防止命名沖突,其實(shí)包就是一個(gè)普通的文件夾,。像Java直接在src目錄下New Package就創(chuàng)建了一個(gè)包,,src表示源文件位置。但是Python創(chuàng)建Package時(shí)不但會(huì)創(chuàng)建一個(gè)文件夾,,還會(huì)創(chuàng)建一個(gè)init.py的文件,,這個(gè)文件的作用用于標(biāo)識此目錄是一個(gè)package而不是一個(gè)普通的文件夾。像Java直接就約定好源文件src目錄了,,maven約定好了測試類test和資源文件resource的位置,。但Python沒有任何約定,沒有約定的后果就是你創(chuàng)建一個(gè)文件夾不知道這個(gè)文件夾究竟是一個(gè)包還是一個(gè)普通的文件夾呢,,Python的解決辦法是如果你要?jiǎng)?chuàng)建包那么這個(gè)包下必須有init.py文件這個(gè)文件才被認(rèn)為是package,如果沒有這個(gè)固定的文件就認(rèn)為這是一個(gè)普通的文件夾,。 在開發(fā)中大家普遍認(rèn)為約定大于配置,,如果不約定就會(huì)非常的靈活,太靈活也不是什么好事,。如果一個(gè)項(xiàng)目有很多個(gè)包,,每個(gè)包下都要有個(gè)init.py文件,個(gè)人感覺顯得臃腫,,不優(yōu)雅,,個(gè)人不太理解這種設(shè)計(jì)。 initall# 在Python3.8中如果models包下__init__.py文件沒有定義__all__,,那么通過*號不會(huì)導(dǎo)入任何模塊test.pyfrom models import *print(dir())
views.test.py 定義了all所以只有model, model2被導(dǎo)入進(jìn)來了from models import *print(dir()) init.py文件也是一個(gè)普通的py源碼文件,,也可以寫任何python代碼??梢詫憘€(gè)print測試一下,。 models.init.py py print('package models imported...') views.test.py ```py 執(zhí)行該語句就會(huì)執(zhí)行models.init.py中的print代碼from models import * ``` 注意:每次執(zhí)行from models import *都會(huì)執(zhí)行models.init.py中的print代碼,這樣設(shè)計(jì)不知道什么場景下會(huì)被使用到,。很多資料都說不建議在.init.py文件中寫業(yè)務(wù)代碼,。 3. 正索引和負(fù)索引python中支持兩種索引,正索引和負(fù)索引(負(fù)索引語法還是第一次接觸到,,雖然第一次接觸但是并不反感這個(gè)語法,,反而感覺這種語法很好很贊成),可以用于字符串,、列表,、元組: - 頭索引(正索引):從左邊開始,最左邊第一個(gè)下標(biāo)為0,,最左邊第二個(gè)下標(biāo)為1 - 尾索引(負(fù)索引):從右邊開始,,最右邊第一個(gè)下標(biāo)為-1,,最右邊第二個(gè)下標(biāo)為-2 索引:一個(gè)下標(biāo)稱之為索引 [1] 切片:兩個(gè)下標(biāo)稱之為切片 [1:3] ```py
4. while else 和 for else循環(huán)代碼塊中可以有else代碼塊(我也是第一次見到這種語法),當(dāng)循環(huán)條件為False時(shí)執(zhí)行else,,如果循環(huán)體中遇到break時(shí)不執(zhí)行else代碼塊的,。 i = 0while i < 5: print(f'{i} 小于5') i += 1else: print(f'{i} 大于等于5')# 執(zhí)行結(jié)果0 小于51 小于52 小于53 小于54 小于55 大于等于5 5. 三位運(yùn)算符Java中的三位運(yùn)算符是用 ? : 來實(shí)現(xiàn)的。Python中是用if else來實(shí)現(xiàn)的(突然換種語法感覺不太適應(yīng)) ```java int a = 2; int b = 1; int result = a >= b ? a : b; System.out.println(result); ``` a = 2b = 1# if條件成立返回if前面的表達(dá)式,,if條件不成立返回else對應(yīng)的表達(dá)式result = a if a >= b else b# 2print(result) Python中的文檔注釋doctest是寫在模塊的第一行或者函數(shù)的第一行使用三個(gè)引號寫的一段字符串注釋,,注釋中一般包括函數(shù)作用的解釋以及給出如何使用函數(shù)的示例程序,示例程序用的是Python Shell格式,。 文檔測試的作用: - 給出一些示例代碼,,便于快速了解使用 - 通過工具來生成文檔,類似于Java中的Javadoc 7. del 關(guān)鍵字del 關(guān)鍵字用于刪除列表,、字典中的元素,,或者刪除變量。 del這種用法即不像面向過程的語法(面向過程一般都是調(diào)用函數(shù)),,也不是面向?qū)ο蟮恼Z法(面向?qū)ο笠话愣际峭ㄟ^對象來調(diào)用方法),,del這種語法比較像Shell命令行語法。 list = [1, 2, 3]del list[0]print(list)del list 在其它編程語言中一般都是try catch finally, Python用的不是catch而是except,,并且python還支持else代碼塊,。else子句將在try子句沒有發(fā)生任何異常的時(shí)候執(zhí)行 Python中的大部分異常類都是以Error作為后綴的,如IndentationError,、AssertionError,、ValueError、TypeError,、ZeroDivisionError, 也有些異常沒有啥后綴如StopIteration,,感覺命名不統(tǒng)一。
else的作用是try代碼塊正常執(zhí)行完畢(沒有報(bào)錯(cuò))會(huì)執(zhí)行else代碼塊,。那么把else代碼塊中的代碼單獨(dú)放在else代碼塊中和放在try代碼塊的最后部分有什么區(qū)別?,?,? 示例1:要else代碼塊,try代碼塊執(zhí)行完再畢執(zhí)行else代碼塊,,如果else代碼塊報(bào)錯(cuò)那么except是不會(huì)捕獲異常的,,那就繼續(xù)往外拋出異常。py try: print('try') except: print('Exception') else: assert 1 == 2 finally: print('finally') 示例2:不要else代碼塊,,直接將else代碼塊中代碼放在try代碼塊的最后面,。如果else代碼塊中的代碼發(fā)生異常,會(huì)被except捕獲,執(zhí)行except代碼塊,。 try: print('try') # else 代碼塊 assert 1 == 2except: print('Exception')finally: print('finally') Python在代碼塊中聲明的變量是'全局變量'這和Java語言完全相反,,Java在代碼塊中聲明的變量都是局部變量當(dāng)代碼塊塊執(zhí)行完畢了就釋放了局部變量,Python則完全不同,。 ```py if True: a = 'if' print(a) try: b = 'try' except ValueError: # 局部變量,,外面不能訪問 c = 'except' else: d = 'else' finally: e = 'finally' print(b) print(c)print(d) print(e) while True: f = 'while' break print(f) for i in range(10): g = 'for' print(g) ``` 10. 函數(shù)參數(shù)默認(rèn)值類型只能為不可變類型如果函數(shù)參數(shù)默認(rèn)值為可變類型如list列表,那么函數(shù)執(zhí)行的結(jié)果可能會(huì)和你預(yù)期的結(jié)果不一致,。list傳參屬于引用傳遞而不是值傳遞,。
def append_end(l=[]): print('id(l)=', id(l)) l.append('END') return l# ['END']print(append_end())'''第二次調(diào)用參數(shù)l的內(nèi)存地址竟然和第一次的完全一樣。這說明一個(gè)問題:參數(shù)默認(rèn)值只會(huì)分配一次內(nèi)存,,如果不傳參數(shù)所有函數(shù)調(diào)用都使用同一塊內(nèi)存,,這就解釋了第二次函數(shù)調(diào)用為啥是['END', 'END']而不是['END']'''# ['END', 'END']print(append_end()) 解決函數(shù)參數(shù)默認(rèn)是可變類型帶來的異常結(jié)果就是將函數(shù)參數(shù)默認(rèn)值改為不可變類型None ```py def append_end2(l=None): print('id(l)=', id(l)) if l is None: # 如果沒有傳值,將l顯式清空 l = [] l.append('END') return l ['END']print(append_end2()) ['END']print(append_end2()) ``` 11. global和nonlocal當(dāng)內(nèi)部作用域想修改外部作用域的變量時(shí),,就要顯式使用global和nonlocal關(guān)鍵字,。原因是如果在局部作用于聲明一個(gè)和全局作用于相同的名字的變量,Python會(huì)認(rèn)為局部變量和全局變量是兩個(gè)不同的變量(雖然名字相同),,要告訴Python這是同一個(gè)變量就要使用關(guān)鍵字來修飾。
foobar = 1def func(): global foobar foobar = 10 print(f'func foobar={foobar}')# func foobar=10func()# 10print(foobar) 12. 嵌套函數(shù)函數(shù)定義可以嵌套函數(shù),,這在其它語言中也經(jīng)常見到但是可以嵌套多層函數(shù)還是第一次見。 def function(): print('function') def func(): print('func') def fun(): print('fun') return fun return funcfunction()()() lambda表達(dá)式在很多編程語言中都有類似語法,,但是Python中的lambda表達(dá)式卻和其它語言相差很大,。
14. 函數(shù)注釋(Function Annotations)Python中的函數(shù)定義參數(shù)不需要指定參數(shù)類型,函數(shù)簽名中不需要寫返回值類型,。如果不了解這個(gè)方法的作用,,只看函數(shù)簽名是看不出來啥,也不知道傳參數(shù)傳什么類型,,也不知道這個(gè)函數(shù)的返回值是什么類型,,就算知道函數(shù)的作用也不能完全確定函數(shù)的返回值是什么類型,是int類型還是float類型呢?不指定參數(shù)類型不指定返回值類型,,這使得函數(shù)的簽名非常模糊,,非常的不直觀,也不利于快速了解函數(shù)的作用,。py def foobar(a, b): pass 像很多服務(wù)端語言都會(huì)指定參數(shù)的類型和返回值類型,,這些都作為函數(shù)簽名的一部分。如下Java方法的定義:java /** * 除法 * @param a 除數(shù) * @param b 被除數(shù) * @return 除法結(jié)果 * @throws ArithmeticException 算術(shù)異常 */ public static long div(long a, long b) throws ArithmeticException { return a / b; } 為了解決函數(shù)簽名不明確的問題,,Python3中引入了函數(shù)注釋,。注意此函數(shù)注釋并不太像我們通常所說的注釋:解釋函數(shù)的作用。Java語言中的注釋一般分為幾個(gè)部分:一部分用于描述函數(shù)的作用,;一部分描述參數(shù),;一部分描述返回值;一部分描述異常信息,。Python中的函數(shù)注釋只能用來描述參數(shù)的數(shù)據(jù)類型和作用以及字段的返回值類型,,不能描述整個(gè)函數(shù)的作用。 函數(shù)注釋作用是提高代碼可讀性,,暗示傳入?yún)?shù)及返回?cái)?shù)據(jù)的類型, 注意這只是提示參數(shù)和返回值的類型,,并不校驗(yàn)參數(shù)的值是不是這種類型,因?yàn)镻ython中參數(shù)名是沒有數(shù)據(jù)類型的,,實(shí)際上還是可以任意傳任意類型的值,,這里只是提示要傳入的數(shù)據(jù)類型,作為一種注釋來提示你,。 函數(shù)注釋包括:
參數(shù)類型和返回值類型注釋py def div(a: int, b: int) -> float: return a / b 幫助字符串注釋py def div(a: '除數(shù)', b: '被除數(shù)') -> float: return a / b 參數(shù)類型注釋和幫助字符串注釋混用py def div(a: int, b: '被除數(shù)') -> float: return a / b 同時(shí)支持參數(shù)數(shù)據(jù)類型和幫助字符串注釋 ```py def div(a: dict(type= int, help='除數(shù)'), b: dict(type= int, help='被除數(shù)')) -> float: return a / b 注釋是一種幫助開發(fā)人員了解代碼的,,并不是可執(zhí)行代碼的一部分,函數(shù)注釋只是一種提示,,并不是強(qiáng)制,。雖然我們指定了參數(shù)的數(shù)據(jù)類型,但是我們讓然雖然傳值,,我們可以傳float類型的值,。print(div(4.4, 2.0)) 可以通過annotations屬性來獲取函數(shù)注釋,返回有一個(gè)dict,,返回值的key為”return“,。通過函數(shù)名.annotations來獲取{'a': {'type': , 'help': '除數(shù)'}, 'b': {'type': , 'help': '被除數(shù)'}, 'return': }print(div.annotations)``` 個(gè)人覺得把字符串幫助描述寫在函數(shù)簽名里使得函數(shù)簽名過于冗余不夠簡潔,還不如在函數(shù)體內(nèi)些文檔注釋看著清晰,。 ```py def div(a: int, b: int) -> float: r'''除法運(yùn)算 :param a: 除數(shù):param b: 被除數(shù):return: 除法結(jié)果'''return a / b 15. 動(dòng)態(tài)添加屬性和方法Python支持動(dòng)態(tài)的添加屬性和方法(實(shí)例方法、靜態(tài)方法),,Javascript腳本語言也支持此語法,。 class Foobar: passfoobar = Foobar();foobar.attr1 = '動(dòng)態(tài)添加屬性'print(foobar.attr1)from types import MethodTypedef test(self, value): print(f'動(dòng)態(tài)添加{value}方法')# 給某一個(gè)對象動(dòng)態(tài)添加一個(gè)實(shí)力方法,其它對象是沒有該方法的foobar.func = MethodType(test, foobar)foobar.func('實(shí)例')# 給類添加靜態(tài)方法Foobar.test = MethodType(test, Foobar)Foobar.test('類') type() 函數(shù)即可用來獲取變量的類型,,也可用用于動(dòng)態(tài)創(chuàng)建類型,。 ```py def foo(self): print('Hello World') 參數(shù)1:類名參數(shù)2:父類參數(shù)3:方法Hello = type('Hello', (object,), dict(foobar=foo)) obj = Hello() obj.foobar() ``` 16. with關(guān)鍵字with關(guān)鍵字的作用自動(dòng)關(guān)閉資源或者自動(dòng)獲取鎖和自動(dòng)釋放鎖,不需要程序員顯式的關(guān)閉資源和釋放鎖了,,再也不會(huì)由于忘記關(guān)閉資源或者忘記釋放鎖造成bug了,,系統(tǒng)會(huì)自動(dòng)做這件事。 關(guān)閉資源 ```py try: f = open('test.txt') print(f.readline()) except BaseException as e: print(e) finally: f.close() with方式with open('test.txt') as f: print(f.readline())``` with自動(dòng)獲取鎖和釋放鎖 ```py import threading lock = threading.Lock() lock.acquire() print('線程安全代碼...') lock.release() ``` 17. import 模塊 和 from 模塊 import 成員 的區(qū)別
import sysprint(sys.argv) 18. id() 和 ctypes
import ctypesvalue = 'Hello World'memory_address = id(value)print(memory_address)raw_value = ctypes.cast(memory_address, ctypes.py_object).value # 根據(jù)內(nèi)存地址獲取對應(yīng)的值print(raw_value) 在其它編程語言中一般是不允許數(shù)學(xué)方式比較的,,原因是一些符號是特殊語法而不是數(shù)學(xué)用的比較符號,,在Python中這些數(shù)學(xué)比較符號沒有被用來作為語法使用,所以支持?jǐn)?shù)學(xué)方式的比較py x = 10 result = 1 < x < 10 result = 20>= x >= 10 20.偏函數(shù)使用原函數(shù)指定一些默認(rèn)值來封裝出一個(gè)新函數(shù),。如果某個(gè)函數(shù)經(jīng)常使用某個(gè)固定的默認(rèn)參數(shù),可以通過偏函數(shù)語法來簡化調(diào)用,,減少傳參個(gè)數(shù),。 ```py def int16(value): return int(value, base=16) int16('123456') import functools int16 = functools.partial(int, base=16) int16('12345') 21. 交換兩個(gè)變量的值一般情況下交換兩個(gè)變量的值需要借助一個(gè)臨時(shí)變量temp來實(shí)現(xiàn),,Python元組語法可以一次同時(shí)聲明多個(gè)變量,,也可以同時(shí)修改多個(gè)元組中的值,使用此語法可以簡單的完成兩個(gè)變量值交換,。py x, y = 1, 2 x, y = y, x |
|