Python 是 一種用著很爽的語(yǔ)言。Python也有著很多特性和技巧,可以幫助我們編寫(xiě)更高效、更優(yōu)雅、更易維護(hù)的代碼。下面勇哥將介紹一些我常用的Python代碼優(yōu)化的簡(jiǎn)單小技巧,少寫(xiě)很多代碼。
咱們都知道,短路運(yùn)算也就是 ”與 ,或 ,非“ 這幾個(gè)組成。但是有些時(shí)候,我們很多 if-else語(yǔ)句其實(shí)可以使用這種短路運(yùn)算來(lái)簡(jiǎn)寫(xiě)代碼的。
例, 獲取用戶(hù)信息,不存在的用戶(hù)則返回匿名用戶(hù), 代碼:
def get_user(user): # 常規(guī)代碼 if user: return user return "匿名用戶(hù)" # 短路來(lái)簡(jiǎn)寫(xiě)代碼def get_user(user): return user or "匿名用戶(hù)"
使用短路運(yùn)算處理,那么就只需要一行代碼即可,這樣寫(xiě)也挺易讀的。
使用切片代替循環(huán)或遞歸來(lái)操作序列。切片是一種用于從一個(gè)序列(如字符串、列表、元組等)中獲取一部分或全部元素的語(yǔ)法。
例,反轉(zhuǎn)數(shù)據(jù),代碼:
# 使用循環(huán)def reverse(lst): new_lst = [] for i in range(len(lst) - 1, -1, -1): new_lst.append(lst[i]) return new_lstlst = [1, 2, 3, 4, 5]print(reverse(lst)) # [5, 4, 3, 2, 1]# 使用切片def reverse(lst): return lst[::-1]lst = [1, 2, 3, 4, 5]print(reverse(lst)) # [5, 4, 3, 2, 1]
切片的操作比循環(huán)或遞歸更簡(jiǎn)單并且高效,因?yàn)榍衅腔趦?nèi)置函數(shù)實(shí)現(xiàn)的,而循環(huán)或遞歸是基于自定義函數(shù)實(shí)現(xiàn)。
列表推導(dǎo)式是一種用于從一個(gè)可迭代對(duì)象(如列表、元組、字典、集合等)創(chuàng)建一個(gè)新的列表的簡(jiǎn)潔的語(yǔ)法。
例,從一個(gè)列表中篩選出所有的偶數(shù),并將它們乘以2,代碼:
# 使用普通的循環(huán)lst = [1, 2, 3, 4, 5, 6]new_lst = []for x in lst: if x % 2 == 0: new_lst.append(x * 2)print(new_lst) # [4, 8, 12]
# 使用列表推導(dǎo)式lst = [1, 2, 3, 4, 5, 6]new_lst = [x * 2 for x in lst if x % 2 == 0]print(new_lst) # [4, 8, 12]
一行代碼實(shí)現(xiàn)循環(huán)、條件判斷和賦值等操作,提高了代碼的可讀性和效率,而且運(yùn)行速度也更快(可以思考一下為什么更快)。
生成器表達(dá)式是一種類(lèi)似于列表推導(dǎo)式的語(yǔ)法,但是它不會(huì)一次性生成一個(gè)完整的列表,而是返回一個(gè)生成器對(duì)象,可以按需逐個(gè)產(chǎn)生元素。
例,計(jì)算一個(gè)列表中所有偶數(shù)的平方和,代碼:
# 使用普通的循環(huán)lst = [1, 2, 3, 4, 5, 6]sum = 0for x in lst: if x % 2 == 0: sum += x ** 2print(sum) # 56
# 使用生成器表達(dá)式lst = [1, 2, 3, 4, 5, 6]sum = sum(x ** 2 for x in lst if x % 2 == 0)print(sum) # 56
這個(gè)生成器表達(dá)式可以節(jié)省內(nèi)存空間,提高性能,適合處理大量或無(wú)限的數(shù)據(jù),而且不會(huì)占用額外的內(nèi)存空間,特別適用于讀取大批量的數(shù)據(jù)。當(dāng)然我們也可以用yeild也能做一個(gè)生成器,這個(gè)太東西很牛逼。
枚舉是一種用于同時(shí)獲取可迭代對(duì)象中的元素和索引的函數(shù)。枚舉可以避免使用額外的變量來(lái)記錄索引,提高了代碼的可讀性和效率。
例,打印一個(gè)列表中每個(gè)元素及其對(duì)應(yīng)的索引,代碼:
# 使用普通的循環(huán)lst = ["a", "b", "c", "d", "e"]index = 0for x in lst: print(index, x) index += 1# 輸出:# 0 a# 1 b# 2 c# 3 d# 4 e
# 使用枚舉lst = ["a", "b", "c", "d", "e"]for index, x in enumerate(lst): print(index, x)# 輸出:# 0 a# 1 b# 2 c# 3 d# 4 e
使用枚舉的代碼更加簡(jiǎn)潔和清晰,而且不需要手動(dòng)更新索引。
三元運(yùn)算符是一種用于根據(jù)一個(gè)條件表達(dá)式來(lái)選擇兩個(gè)不同的值的簡(jiǎn)潔的語(yǔ)法。
例,根據(jù)一個(gè)數(shù)字的正負(fù)來(lái)賦值一個(gè)字符串,代碼:
# 使用普通的if-else語(yǔ)句num = -5if num > 0: sign = "positive"else: sign = "negative"print(sign) # negative
# 使用三元運(yùn)算符num = -5sign = "positive" if num > 0 else "negative"print(sign) # negative
三元運(yùn)算符可以用一行代碼實(shí)現(xiàn)簡(jiǎn)單的條件判斷和賦值,提高了代碼的可讀性和效率,而且不需要多余的變量和語(yǔ)句。
遇到if循環(huán)語(yǔ)句很長(zhǎng)的時(shí)候,其實(shí)可以使用字典來(lái)替代,兩者的執(zhí)行效率沒(méi)有試驗(yàn)過(guò),感覺(jué)差不了多少。
例, 使用字典來(lái)判斷返回值, 代碼:
# 使用多個(gè)if-elif-else語(yǔ)句def foo(x): if x == "a": return 1 elif x == "b": return 2 elif x == "c": return 3 else: return -1print(foo("a")) # 1print(foo("d")) # -1# 使用字典def foo(x): dic = {"a": 1, "b": 2, "c": 3} return dic.get(x, -1)print(foo("a")) # 1print(foo("d")) # -1
合理利用字典的get方法,可以減少很多代碼的使用。
裝飾器是一種用于在不修改原函數(shù)定義和調(diào)用的情況下,給函數(shù)添加額外的功能或修改其行為的語(yǔ)法。
例,給一個(gè)函數(shù)添加一個(gè)計(jì)時(shí)的功能,記錄其運(yùn)行時(shí)間,代碼:
# 使用普通的函數(shù)調(diào)用import timedef foo(): # do something time.sleep(1)start = time.time()foo()end = time.time()print(f"foo() took {end - start} seconds to run.")
# 使用裝飾器import timedef timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__}() took {end - start} seconds to run.") return result return wrapper@timer # 相當(dāng)于 foo = timer(foo)def foo(): # do something time.sleep(1)foo()
裝飾器能做的事太多了,比如flask 框架,真尼瑪裝飾器用到飛天。勇哥也就一般用于實(shí)現(xiàn)一些通用的功能,如日志、緩存、計(jì)時(shí)、權(quán)限檢查等,讓代碼可復(fù)用更強(qiáng)一寫(xiě)。
上下文管理器是一種用于在執(zhí)行某些操作之前和之后自動(dòng)執(zhí)行一些預(yù)設(shè)的操作的語(yǔ)法。上下文管理器可以用于實(shí)現(xiàn)一些資源管理的功能,
例如,打開(kāi)一個(gè)文件,讀取其內(nèi)容,并在完成后自動(dòng)關(guān)閉文件,代碼:
# 使用普通的try-finally語(yǔ)句file = open("test.txt", "r")try: content = file.read() print(content)finally: file.close()
# 使用上下文管理器with open("test.txt", "r") as file: content = file.read() print(content)
使用上下文管理器我們一般用于 打開(kāi)和關(guān)閉文件、獲取和釋放鎖、連接和斷開(kāi)數(shù)據(jù)庫(kù)等。代碼的安全性問(wèn)題和可讀性也很好處理。
lambda表達(dá)式(代替簡(jiǎn)單的函數(shù)定義來(lái)創(chuàng)建匿名函數(shù)。lambda表達(dá)式是一種用于定義一個(gè)只有一行代碼的函數(shù)的簡(jiǎn)潔的語(yǔ)法。
例,處理排序sorted,指定字段,代碼:
lst = [9,2,3,4,5,5,1,2,3]# 不使用lambda表達(dá)式def add(item): return itemprint(sorted(add, item))# 使用lambda表達(dá)式print(sorted(lst, key=lambda x:x))
lambda表達(dá)式我們一般用來(lái)實(shí)現(xiàn)一些簡(jiǎn)單的功能,如排序、過(guò)濾、映射等。使用匿名函數(shù),代碼有時(shí)候少寫(xiě)一些外,還有一點(diǎn)就是 lambda 表達(dá)式用完就會(huì)在內(nèi)存中舍棄,也挺好。
map函數(shù)代替循環(huán)來(lái)對(duì)可迭代對(duì)象中的每個(gè)元素應(yīng)用一個(gè)函數(shù)。map函數(shù)是一種用于將一個(gè)函數(shù)作用于一個(gè)可迭代對(duì)象中的每個(gè)元素,并返回一個(gè)新的可迭代對(duì)象的函數(shù)。
例,將一批字符列表字符串轉(zhuǎn)數(shù)字列表,代碼:
# 不使用map函數(shù)lst = ["1", "2", "3", "4", "5"]new_lst = []for x in lst: new_lst.append(int(x))print(new_lst) # [1, 2, 3, 4, 5]# 使用map函數(shù)lst = ["1", "2", "3", "4", "5"]new_lst = list(map(int, lst))print(new_lst) # [1, 2, 3, 4, 5]
是不是看起來(lái)代碼又少了很多,而且這種內(nèi)置函數(shù),一般速度都比咱們自己寫(xiě)的代碼運(yùn)行效率高,基于這個(gè)強(qiáng)大的高階函數(shù),我們可以用來(lái)實(shí)現(xiàn)一些批量處理的功能,如類(lèi)型轉(zhuǎn)換、格式化、計(jì)算,數(shù)據(jù)合并等。
filter函數(shù)代替循環(huán)來(lái)從可迭代對(duì)象中篩選出滿(mǎn)足一個(gè)條件的元素。filter函數(shù)是一種用于將一個(gè)條件函數(shù)作用于一個(gè)可迭代對(duì)象中的每個(gè)元素,并返回一個(gè)只包含滿(mǎn)足條件元素的新的可迭代對(duì)象的函數(shù)。
例,從一個(gè)列表中挑選符合要求的數(shù)據(jù)出來(lái)組成另一個(gè)列表,代碼:
# 不使用filter函數(shù)lst = [1, 2, 3, 4, 5]new_lst = []for x in lst: if x % 2 == 0: new_lst.append(x)print(new_lst) # [2, 4]# 使用filter函數(shù)lst = [1, 2, 3, 4, 5]new_lst = list(filter(lambda x: x % 2 == 0, lst))print(new_lst) # [2, 4]
filter函數(shù)可以用于實(shí)現(xiàn)一些篩選和過(guò)濾的功能,如刪除空值、去重、選擇子集等,也是一個(gè)很牛的高階函數(shù)。
@property 裝飾器有些時(shí)候可以用來(lái)替代 geter和setter 方法來(lái)管理類(lèi)書(shū)信。這個(gè)裝飾器裝飾的函數(shù)會(huì)轉(zhuǎn)為一個(gè)屬性的語(yǔ)法,可以在訪(fǎng)問(wèn)和修改屬性的時(shí)候,執(zhí)行一些額外的操作,不用顯式調(diào)用。
例,對(duì)類(lèi)屬性的修改與獲取, 代碼:
# 不使用@property裝飾器class Person: def __init__(self, name, age): self.name = name self.age = age def get_name(self): return self.name def set_name(self, name): if not isinstance(name, str): raise TypeError("name must be a string") self.name = name def get_age(self): return self.age def set_age(self, age): if not isinstance(age, int): raise TypeError("age must be an integer") if age < 0 or age > 150: raise ValueError("age must be between 0 and 150") self.age = agep = Person("kira", 40) # 實(shí)例化print(p.get_name()) # kiraprint(p.get_age()) # 40p.set_name("勇哥")p.set_age(35)print(p.get_name()) # 勇哥print(p.get_age()) # 35# 使用@property裝飾器class Person: def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @name.setter def name(self, name): if not isinstance(name, str): raise TypeError("name must be a string") self._name = name @property def age(self): return self._age @age.setter def age(self, age): if not isinstance(age, int): raise TypeError("age must be an integer") if age < 0 or age > 150: raise ValueError("age must be between 0 and 150") self._age = agep = Person("kira", 30)print(p.name) # kiraprint(p.age) # 30p.name = "勇哥"p.age = 35print(p.name) # 勇哥print(p.age) # 35
從上面的代碼,我們就可以了解到@property 可以做的事就很多了,比如實(shí)現(xiàn)屬性管理,數(shù)據(jù)驗(yàn)證,類(lèi)型轉(zhuǎn)換,緩存... 可讀性和安全性也不錯(cuò).
來(lái)到本文的最后一個(gè)要分享的 __slots__ 屬性,這個(gè)事用來(lái)指定一個(gè)類(lèi)可以有那些屬性的語(yǔ)法,可以用來(lái)替代一下__dict__ 來(lái)節(jié)省類(lèi)的內(nèi)存空間,因?yàn)樗苊饬私o每個(gè)實(shí)例創(chuàng)建一個(gè) __dict__ 屬性來(lái)存儲(chǔ)所有屬性和值。一般用于內(nèi)存優(yōu)化。
例,指定一個(gè)類(lèi)實(shí)例時(shí)只有指定的屬性,代碼:
# 不使用__slots__屬性class Person: def __init__(self, name, age): self.name = name self.age = agep = Person("勇哥", 90)print(p.__dict__) print(p.__sizeof__())
輸出:
{'name': '測(cè)試玩家勇哥', 'age': 90}32
# 使用__slots__屬性class Person: # 指定該類(lèi)只能擁有name和age兩個(gè)屬性 __slots__ = ("name", "age") def __init__(self, name, age): self.name = name self.age = agep = Person("Alice", 20)print(p.__sizeof__()) print(p.__dict__)
輸出:
Traceback (most recent call last): File "D:/app/apitest/debug/ts.py", line 82, in <module> print(p.__dict__) # AttributeError: 'Person' object has no attribute '__dict__'AttributeError: 'Person' object has no attribute '__dict__'32
很明顯,這個(gè)類(lèi)已經(jīng)沒(méi)有了__dict__屬性了,也就是可以減少內(nèi)存占用,提高訪(fǎng)問(wèn)速度這種玩意,但是也有不好的地方,比如不能多重繼承了,也不能添加新屬性。要打印出來(lái)你限制的屬性也就只能dir 或者 getter來(lái)獲取了。總之小伙伴們看著使用吧。
本文鏈接:http://www.www897cc.com/showinfo-26-10472-0.htmlPython應(yīng)知小技巧:如何用更少的代碼做更多的事情
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com