Python編程中需要避免的21個(gè)代碼反模式實(shí)戰(zhàn)詳解
1.print 語句里手動(dòng)四舍五入
錯(cuò)誤示范:
value = 3.1415926 print(round(value, 2)) # 這樣寫并不會(huì)報(bào)錯(cuò),但有更好的方式
為什么不推薦?
這種做法沒錯(cuò),但 Python 其實(shí)提供了更優(yōu)雅的方法:
更好的寫法:
print(f"{value:.2f}") # 格式化字符串,簡潔又直觀
{:.2f} 代表保留兩位小數(shù),既美觀又避免不必要的 round() 調(diào)用。
2.頻繁在 NumPy 和普通列表之間轉(zhuǎn)換
錯(cuò)誤示范:
import numpy as np data = [1, 2, 3, 4] data_np = np.array(data) max_value = max(data_np) # 這里用了 Python 內(nèi)置的 max,而不是 NumPy 的
為什么是坑?
NumPy 主要用于高效的數(shù)值計(jì)算,它的"numpy.max()“速度遠(yuǎn)快于 Python 內(nèi)置的"max()”,但上面的代碼卻使用了后者,導(dǎo)致性能下降。
更好的寫法:
max_value = np.max(data_np) # 充分利用 NumPy 的優(yōu)化
在數(shù)據(jù)分析中,最好一開始就決定用 NumPy 或 Pandas,并始終保持一致,而不是來回轉(zhuǎn)換。
3.用字符串操作文件路徑
錯(cuò)誤示范:
filename = "C:\\Users\\Admin\\Documents\\file.txt" folder = "C:\\Users\\Admin\\Documents" path = folder + "\\" + "file.txt" # 這樣拼接路徑很容易出錯(cuò)
為什么不推薦?
直接用字符串拼接路徑,不僅代碼不優(yōu)雅,而且不同系統(tǒng)的路徑分隔符不同(Windows 用 \,Linux 和 macOS 用 /),這可能導(dǎo)致跨平臺(tái)問題。
更好的寫法:
from pathlib
import Path
folder = Path("C:/Users/Admin/Documents")
path = folder / "file.txt" # 使用 Pathlib 更安全、可讀性更高
"Pathlib"讓路徑操作更直觀,能自動(dòng)適配不同系統(tǒng)的路徑格式。
4.寫 IO 函數(shù)時(shí)只支持文件路徑
錯(cuò)誤示范:
def save_data(path, data): with open(path, "w") as f: f.write(data)
為什么是坑?
上面的函數(shù)只支持文件路徑,用戶無法傳入其他類型的 IO 對象,比如"StringIO"(內(nèi)存中的文件),或者網(wǎng)絡(luò)流。
更好的寫法:
def save_data(file_obj, data): file_obj.write(data)
這樣,用戶既可以傳"open(“file.txt”, “w”)“,也可以傳"io.StringIO()”,更加靈活。
5.用"+"號拼接字符串
錯(cuò)誤示范:
result = "" for i in range(100): result += str(i) # 每次拼接都會(huì)創(chuàng)建新字符串,效率極低
為什么是坑?
Python 的字符串是不可變對象,每次"+"拼接都會(huì)生成新字符串,導(dǎo)致性能問題。
更好的寫法:
from io import StringIO buffer = StringIO() for i in range(100): buffer.write(str(i)) result = buffer.getvalue() # 這樣不會(huì)頻繁創(chuàng)建新字符串
或者:
result = "".join(str(i) for i in range(100)) # 使用 join 拼接更高效
6.用"eval()"解析字符串
錯(cuò)誤示范:
data = "{'name': 'Alice', 'age': 25}"
parsed = eval(data) # 有安全風(fēng)險(xiǎn)!
為什么是坑?
"eval()"可能執(zhí)行惡意代碼,比如:
evil_data = "__import__('os').system('rm -rf /')"
eval(evil_data) # 可能導(dǎo)致災(zāi)難性后果!
更好的寫法:
import json
parsed = json.loads(data.replace("'", '"')) # 更安全的解析方式
"json.loads()"只能解析 JSON,不會(huì)執(zhí)行惡意代碼。
7.依賴全局變量存儲(chǔ)函數(shù)輸入/輸出
錯(cuò)誤示范:
result = None # 全局變量 def compute(x, y): global result result = x + y # 改變?nèi)肿兞?,增加了函?shù)的副作用
為什么不推薦?
全局變量會(huì)導(dǎo)致代碼難以維護(hù),函數(shù)變得不純(即依賴外部狀態(tài)),可能出現(xiàn)意想不到的 Bug。
更好的寫法:
def compute(x, y): return x + y # 讓函數(shù)返回值,而不是改寫全局變量
這樣,"compute()"不會(huì)影響外部變量,調(diào)用時(shí)更安全:
result = compute(3, 5)
8.誤以為"and"和"or"只返回布爾值
錯(cuò)誤示范:
result = 5 or 10 # 你以為 result 是 True?錯(cuò),它是 5
為什么是坑?
在 Python 中,“or"和"and"不一定返回"True"或"False”,而是返回"第一個(gè)確定結(jié)果的值":
print(5 or 10) # 輸出 5,因?yàn)?5 是真值,or 不再繼續(xù)判斷 print(0 or 10) # 輸出 10,因?yàn)?0 是假值,or 繼續(xù)檢查 10 print(5 and 10) # 輸出 10,因?yàn)?and 需要所有條件都為真 print(0 and 10) # 輸出 0,因?yàn)?and 發(fā)現(xiàn) 0 是假值,直接返回
如何避免?
如果你只是想得到"True"或"False",請用"bool()":
is_valid = bool(5 or 10) # 這樣才是標(biāo)準(zhǔn)布爾值
9.變量名全是單個(gè)字母
錯(cuò)誤示范:
def calc(a, b, c): d = a * b + c return d
為什么是坑?
這種寫法讓代碼難以閱讀,別人(包括你自己)以后再看時(shí),完全不知道 a、b 和 c 代表什么。
更好的寫法:
def calc(price, quantity, discount): total = price * quantity + discount return total
變量名有意義,代碼可讀性就會(huì)大大提升!
10.“div"和"mod"分開計(jì)算,而不是用"divmod()”
錯(cuò)誤示范:
quotient = 17 // 5 remainder = 17 % 5
為什么是坑?
Python 早就提供了"divmod()",可以一次性得到商和余數(shù):
quotient, remainder = divmod(17, 5) # 代碼更簡潔
這不僅減少了計(jì)算次數(shù),也讓代碼更 Pythonic。
11.不知道"@property",還在寫 Getter/Setter
錯(cuò)誤示范:
class Person: def __init__(self, name): self._name = name def get_name(self): return self._name def set_name(self, value): self._name = value
為什么是坑?
在 Python 里,屬性訪問應(yīng)該盡量像直接訪問變量那樣自然。
更好的寫法:
class Person: def __init__(self, name): self._name = name @property def name(self): return self._name @name.setter def name(self, value): self._name = value
現(xiàn)在,我們可以這樣用:
p = Person("Alice")
print(p.name) # 直接訪問,像變量一樣
p.name = "Bob" # 直接賦值
這樣寫既符合 Python 風(fēng)格,又方便后續(xù)擴(kuò)展(比如添加數(shù)據(jù)驗(yàn)證)。
12.誤把“屬性”當(dāng)“變量”,導(dǎo)致性能問題
錯(cuò)誤示范:
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
print("Calculating area...") # 這行會(huì)頻繁執(zhí)行
return 3.14 * self.radius ** 2
c = Circle(10) print(c.area) # 每次訪問都計(jì)算一次面積 print(c.area) # 計(jì)算了兩次,浪費(fèi)性能
為什么是坑?
有些計(jì)算量較大的屬性(如"area"),不應(yīng)該每次訪問都重新計(jì)算。
更好的寫法:
from functools
import cached_property
class Circle:
def __init__(self, radius):
self.radius = radius
@cached_property
def area(self):
print("Calculating area...") # 只計(jì)算一次
return 3.14 * self.radius ** 2
這樣"area"只會(huì)在第一次訪問時(shí)計(jì)算,之后直接返回緩存值,提升性能。
13.在遍歷列表時(shí)修改它
錯(cuò)誤示范:
numbers = [1, 2, 3, 4, 5] for num in numbers: if num % 2 == 0: numbers.remove(num)
為什么是坑?
遍歷時(shí)修改列表,可能會(huì)跳過一些元素。例如:
numbers = [1, 2, 4, 5] for num in numbers: if num % 2 == 0: numbers.remove(num) print(numbers) # 結(jié)果為 [1, 4,5](漏刪 4)
更好的寫法:
numbers = [1, 2, 3, 4, 5] numbers = [num for num in numbers if num % 2 != 0] # 直接用列表推導(dǎo)式 print(numbers) # [1, 3, 5]
或者,先復(fù)制一份列表:
for num in numbers[:]: if num % 2 == 0: numbers.remove(num)
14.濫用"map()“和"filter()”
錯(cuò)誤示范:
numbers = [1, 2, 3, 4, 5] squared = list(map(lambda x: x ** 2, numbers)) evens = list(filter(lambda x: x % 2 == 0, numbers))
為什么是坑?
雖然"map()"和"filter()"沒錯(cuò),但 Python 里有更好的方式——列表推導(dǎo)式。
更好的寫法:
squared = [x ** 2 for x in numbers] # 代碼更簡潔 evens = [x for x in numbers if x % 2 == 0] # 也更易讀
這樣寫既直觀又符合 Pythonic 風(fēng)格。
15.亂用 dunder 方法(魔法方法)
錯(cuò)誤示范:
class Person:
def __init__(self, name):
self.name = name
def __iadd__(self, other):
print(f"{self.name} 和 {other.name} 成為了朋友!")
return self
p1 = Person("Alice")
p2 = Person("Bob")
p1 += p2 # 這真的合適嗎??
為什么是坑?
Dunder 方法(即雙下劃線方法)應(yīng)該遵循 Python 語言的預(yù)期行為,比如 __add__() 代表加法,而 __iadd__()(+=)本該用于數(shù)值運(yùn)算。但這里卻用它來實(shí)現(xiàn)“成為朋友”的邏輯,讓 += 變成了一個(gè)不符合直覺的操作。
更好的寫法:
class Person:
def __init__(self, name):
self.name = name
self.friends = []
def add_friend(self, other):
print(f"{self.name} 和 {other.name} 成為了朋友!")
self.friends.append(other)
p1.add_friend(p2) # 這樣更直觀
魔法方法要謹(jǐn)慎使用,否則會(huì)讓代碼變得奇怪且難以理解!
16.用正則解析 HTML / XML
錯(cuò)誤示范:
import re html = "<div><p>Hello, world!</p></div>" match = re.search(r"<p>(.*?)</p>", html) print(match.group(1)) # 這樣做并不靠譜
為什么是坑?
HTML 是上下文敏感的,不能用正則完美解析,除非頁面結(jié)構(gòu)極其簡單,否則你遲早會(huì)翻車。
更好的寫法:
from bs4 import BeautifulSoup html = "<div><p>Hello, world!</p></div>" soup = BeautifulSoup(html, "html.parser") print(soup.p.text) # 這樣解析才靠譜
如果你需要處理 HTML / XML,請用"BeautifulSoup"或"lxml"這類專業(yè)的解析庫,而不是正則表達(dá)式
17.不知道"r"“”(原始字符串)
錯(cuò)誤示范:
pattern = "\\d+\\.\\d+" # 正則表達(dá)式匹配浮點(diǎn)數(shù) print(re.findall(pattern, "The price is 3.14"))
為什么是坑?
\ 在字符串里是轉(zhuǎn)義字符,如果寫"\d+",Python 會(huì)誤以為 \d 是轉(zhuǎn)義字符,這會(huì)導(dǎo)致正則解析出錯(cuò)。
更好的寫法:
pattern = r"\d+\.\d+" # 使用原始字符串
加個(gè) r"",就能避免轉(zhuǎn)義問題,寫正則時(shí) 必須養(yǎng)成加 r 的習(xí)慣!
18.誤解"super()"的行為
錯(cuò)誤示范:
class A:
deff(self):
print("A.f")
classB(A):
deff(self):
print("B.f")
super().f()
classC(B): # 正確繼承順序:C -> B -> A
deff(self):
print("C.f")
super().f()
c = C()
c.f() # 輸出順序?yàn)?C.f -> B.f -> A.f
為什么是坑?
Python 采用"C3 線性化"(MRO 規(guī)則),"super()"并不只是簡單地調(diào)用父類,而是根據(jù) MRO 確定順序。上面的代碼會(huì)輸出:
C.f B.f A.f # 你以為 B 之后是 A?其實(shí)是 B -> A
更好的寫法:
print(C.mro()) # 用 .mro() 確認(rèn)方法解析順序
使用"super()"前,建議先查看 MRO,以免調(diào)用順序與你想象的不同!
19.傳遞原始字典或元組,而不是用數(shù)據(jù)
錯(cuò)誤示范:
def process_data(data):
return data["name"].upper(), data["age"] + 1
person = {"name": "Alice", "age": 25}
print(process_data(person))
為什么是坑?
如果字典鍵名變了,你的代碼就會(huì)崩潰,而且代碼可讀性很差。
更好的寫法:
from dataclasses
import dataclass
@dataclass
class Person:
name: str
age: int
def process_data(person: Person):
return person.name.upper(), person.age + 1
p = Person("Alice", 25)
print(process_data(p))
用"dataclass"代替字典,代碼會(huì)更清晰,IDE 還能自動(dòng)補(bǔ)全屬性!
20.還在用"namedtuple()“,而不是"NamedTuple”
錯(cuò)誤示范:
from collections
import namedtuple
Person = namedtuple("Person", ["name", "age"])
為什么是坑?
"namedtuple"需要用字符串定義字段,而且沒有類型注解支持。
更好的寫法:
from typing import NamedTuple class Person(NamedTuple): name: str age: int
使用"NamedTuple",不僅更易讀,而且支持類型注解,適合現(xiàn)代 Python 代碼。
21.在導(dǎo)入時(shí)執(zhí)行代碼(import-time side effects)
錯(cuò)誤示范:
# utils.py
print("Utils module loaded!") # 只要 import 這個(gè)模塊,就會(huì)執(zhí)行這行代碼
import utils # 這里會(huì)自動(dòng)輸出 "Utils module loaded!"
為什么是坑?
模塊導(dǎo)入時(shí)不應(yīng)該有副作用!這樣會(huì)影響性能,并導(dǎo)致意想不到的行為。
更好的寫法:
def main():
print("Utils module loaded!")
if __name__ == "__main__":
main() # 只有直接運(yùn)行這個(gè)文件時(shí)才執(zhí)行
用"if name == “main”"保護(hù)代碼,確保它不會(huì)在 import 時(shí)執(zhí)行!
以上就是Python編程中需要避免的21個(gè)代碼反模式實(shí)戰(zhàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Python反模式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實(shí)現(xiàn)根據(jù)指定端口探測服務(wù)器/模塊部署的方法
這篇文章主要介紹了Python根據(jù)指定端口探測服務(wù)器/模塊部署的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-08-08
安裝conda搭建python環(huán)境保姆級教程(超詳細(xì)!)
這篇文章主要給大家介紹了關(guān)于安裝conda搭建python環(huán)境保姆級教程的相關(guān)資料,conda可以理解為一個(gè)工具,也是一個(gè)可執(zhí)行命令,其核心功能是包管理和環(huán)境管理,需要的朋友可以參考下2023-11-11
Python Numpy 控制臺(tái)完全輸出ndarray的實(shí)現(xiàn)
這篇文章主要介紹了Python Numpy 控制臺(tái)完全輸出ndarray的實(shí)現(xiàn)方式,希望給大家做個(gè)參考,一起跟隨小編過來看看吧2020-02-02
VSCode Python開發(fā)環(huán)境配置的詳細(xì)步驟
這篇文章主要介紹了VSCode Python開發(fā)環(huán)境配置的詳細(xì)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02
Python實(shí)現(xiàn)學(xué)校管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)學(xué)校管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01
淺談python元素如何去重,去重后如何保持原來元素的順序不變
這篇文章主要介紹了淺談python元素如何去重,去重后如何保持原來元素的順序不變?具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-02-02

