python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python反模式

Python编程中需要避免的21个代码反模式实战详解

作者:Python资讯站

这篇文章主要为大家详细介绍了Python编程中需要避免的21个代码反模式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

1.print 语句里手动四舍五入

错误示范:

value = 3.1415926   
print(round(value, 2))  # 这样写并不会报错,但有更好的方式

为什么不推荐?

这种做法没错,但 Python 其实提供了更优雅的方法:

更好的写法:

print(f"{value:.2f}")  # 格式化字符串,简洁又直观

{:.2f} 代表保留两位小数,既美观又避免不必要的 round() 调用。

2.频繁在 NumPy 和普通列表之间转换

错误示范:

import numpy as np   
data = [1, 2, 3, 4]   
data_np = np.array(data)   
max_value = max(data_np)  # 这里用了 Python 内置的 max,而不是 NumPy 的

为什么是坑?

NumPy 主要用于高效的数值计算,它的"numpy.max()“速度远快于 Python 内置的"max()”,但上面的代码却使用了后者,导致性能下降。

更好的写法:

max_value = np.max(data_np)  # 充分利用 NumPy 的优化

在数据分析中,最好一开始就决定用 NumPy 或 Pandas,并始终保持一致,而不是来回转换。

3.用字符串操作文件路径

错误示范:

filename = "C:\\Users\\Admin\\Documents\\file.txt"   
folder = "C:\\Users\\Admin\\Documents"   
path = folder + "\\" + "file.txt"  # 这样拼接路径很容易出错

为什么不推荐?

直接用字符串拼接路径,不仅代码不优雅,而且不同系统的路径分隔符不同(Windows 用 \,Linux 和 macOS 用 /),这可能导致跨平台问题。

更好的写法:

from pathlib 
import Path   
folder = Path("C:/Users/Admin/Documents")   
path = folder / "file.txt"  # 使用 Pathlib 更安全、可读性更高

"Pathlib"让路径操作更直观,能自动适配不同系统的路径格式。

4.写 IO 函数时只支持文件路径

错误示范:

def save_data(path, data):       
with open(path, "w") as f:           
f.write(data)

为什么是坑?

上面的函数只支持文件路径,用户无法传入其他类型的 IO 对象,比如"StringIO"(内存中的文件),或者网络流。

更好的写法:

def save_data(file_obj, data):       
file_obj.write(data)

这样,用户既可以传"open(“file.txt”, “w”)“,也可以传"io.StringIO()”,更加灵活。

5.用"+"号拼接字符串

错误示范:

result = ""   
for i in range(100):       
result += str(i)  # 每次拼接都会创建新字符串,效率极低

为什么是坑?

Python 的字符串是不可变对象,每次"+"拼接都会生成新字符串,导致性能问题。

更好的写法:

from io 
import StringIO   
buffer = StringIO()   
for i in range(100):       
buffer.write(str(i))   
result = buffer.getvalue()  # 这样不会频繁创建新字符串

或者:

result = "".join(str(i) for i in range(100))  # 使用 join 拼接更高效

6.用"eval()"解析字符串

错误示范:

data = "{'name': 'Alice', 'age': 25}"   
parsed = eval(data)  # 有安全风险!

为什么是坑?

"eval()"可能执行恶意代码,比如:

evil_data = "__import__('os').system('rm -rf /')"   
eval(evil_data)  # 可能导致灾难性后果!

更好的写法:

import json   
parsed = json.loads(data.replace("'", '"'))  # 更安全的解析方式

"json.loads()"只能解析 JSON,不会执行恶意代码。

7.依赖全局变量存储函数输入/输出

错误示范:

result = None  # 全局变量   
def compute(x, y):       
global result      
result = x + y  # 改变全局变量,增加了函数的副作用

为什么不推荐?

全局变量会导致代码难以维护,函数变得不纯(即依赖外部状态),可能出现意想不到的 Bug。

更好的写法:

def compute(x, y):       
return x + y  # 让函数返回值,而不是改写全局变量

这样,"compute()"不会影响外部变量,调用时更安全:

result = compute(3, 5)

8.误以为"and"和"or"只返回布尔值

错误示范:

result = 5 or 10  # 你以为 result 是 True?错,它是 5

为什么是坑?

在 Python 中,“or"和"and"不一定返回"True"或"False”,而是返回"第一个确定结果的值":

print(5 or 10)  # 输出 5,因为 5 是真值,or 不再继续判断   
print(0 or 10)  # 输出 10,因为 0 是假值,or 继续检查 10   
print(5 and 10)  # 输出 10,因为 and 需要所有条件都为真   
print(0 and 10)  # 输出 0,因为 and 发现 0 是假值,直接返回

如何避免?

如果你只是想得到"True"或"False",请用"bool()":

is_valid = bool(5 or 10)  # 这样才是标准布尔值

9.变量名全是单个字母

错误示范:

def calc(a, b, c):       
d = a * b + c       
return d

为什么是坑?

这种写法让代码难以阅读,别人(包括你自己)以后再看时,完全不知道 abc 代表什么。

更好的写法:

def calc(price, quantity, discount):       
total = price * quantity + discount       
return total

变量名有意义,代码可读性就会大大提升!

10.“div"和"mod"分开计算,而不是用"divmod()”

错误示范:

quotient = 17 // 5   
remainder = 17 % 5

为什么是坑?

Python 早就提供了"divmod()",可以一次性得到商和余数:

quotient, remainder = divmod(17, 5)  # 代码更简洁

这不仅减少了计算次数,也让代码更 Pythonic。

11.不知道"@property",还在写 Getter/Setter

错误示范:

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 里,属性访问应该尽量像直接访问变量那样自然。

更好的写法:

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

现在,我们可以这样用:

p = Person("Alice")   
print(p.name)  # 直接访问,像变量一样   
p.name = "Bob"  # 直接赋值

这样写既符合 Python 风格,又方便后续扩展(比如添加数据验证)。

12.误把“属性”当“变量”,导致性能问题

错误示范:

class Circle:       
def __init__(self, radius):           
self.radius = radius          
@property       
def area(self):           
print("Calculating area...")  # 这行会频繁执行           
return 3.14 * self.radius ** 2
c = Circle(10)  
 print(c.area)  # 每次访问都计算一次面积   
 print(c.area)  # 计算了两次,浪费性能

为什么是坑?

有些计算量较大的属性(如"area"),不应该每次访问都重新计算。

更好的写法:

from functools 
import cached_property      
class Circle:       
def __init__(self, radius):           
self.radius = radius          
@cached_property       
def area(self):           
print("Calculating area...")  # 只计算一次           
return 3.14 * self.radius ** 2

这样"area"只会在第一次访问时计算,之后直接返回缓存值,提升性能。

13.在遍历列表时修改它

错误示范:

numbers = [1, 2, 3, 4, 5]   
for num in numbers:       
if num % 2 == 0:           
numbers.remove(num)

为什么是坑?

遍历时修改列表,可能会跳过一些元素。例如:

numbers = [1, 2, 4, 5]   
for num in numbers:       
if num % 2 == 0:           
numbers.remove(num)   
print(numbers)  # 结果为 [1, 4,5](漏删 4)

更好的写法:

numbers = [1, 2, 3, 4, 5]   
numbers = [num for num in numbers if num % 2 != 0]  # 直接用列表推导式   
print(numbers)  # [1, 3, 5]

或者,先复制一份列表:

for num in numbers[:]:       
if num % 2 == 0:           
numbers.remove(num)

14.滥用"map()“和"filter()”

错误示范:

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()"没错,但 Python 里有更好的方式——列表推导式。

更好的写法:

squared = [x ** 2 for x in numbers]  # 代码更简洁   
evens = [x for x in numbers if x % 2 == 0]  # 也更易读

这样写既直观又符合 Pythonic 风格。

15.乱用 dunder 方法(魔法方法)

错误示范:

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 方法(即双下划线方法)应该遵循 Python 语言的预期行为,比如 __add__() 代表加法,而 __iadd__()+=)本该用于数值运算。但这里却用它来实现“成为朋友”的逻辑,让 += 变成了一个不符合直觉的操作。

更好的写法:

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)  # 这样更直观

魔法方法要谨慎使用,否则会让代码变得奇怪且难以理解!

16.用正则解析 HTML / XML

错误示范:

import re   html = "<div><p>Hello, world!</p></div>"   
match = re.search(r"<p>(.*?)</p>", html)   
print(match.group(1))  # 这样做并不靠谱

为什么是坑?

HTML 是上下文敏感的,不能用正则完美解析,除非页面结构极其简单,否则你迟早会翻车。

更好的写法:

from bs4 import BeautifulSoup   
html = "<div><p>Hello, world!</p></div>"   
soup = BeautifulSoup(html, "html.parser")   
print(soup.p.text)  # 这样解析才靠谱

如果你需要处理 HTML / XML,请用"BeautifulSoup"或"lxml"这类专业的解析库,而不是正则表达式

17.不知道"r"“”(原始字符串)

错误示范:

pattern = "\\d+\\.\\d+"  # 正则表达式匹配浮点数   
print(re.findall(pattern, "The price is 3.14"))

为什么是坑?

\ 在字符串里是转义字符,如果写"\d+",Python 会误以为 \d 是转义字符,这会导致正则解析出错。

更好的写法:

pattern = r"\d+\.\d+"  # 使用原始字符串

加个 r"",就能避免转义问题,写正则时 必须养成加 r 的习惯

18.误解"super()"的行为

错误示范:

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()  # 输出顺序为 C.f -> B.f -> A.f    

为什么是坑?

Python 采用"C3 线性化"(MRO 规则),"super()"并不只是简单地调用父类,而是根据 MRO 确定顺序。上面的代码会输出:

C.f   B.f   A.f  # 你以为 B 之后是 A?其实是 B -> A

更好的写法:

print(C.mro())  # 用 .mro() 确认方法解析顺序

使用"super()"前,建议先查看 MRO,以免调用顺序与你想象的不同!

19.传递原始字典或元组,而不是用数据

错误示范:

def process_data(data):       
return data["name"].upper(), data["age"] + 1     
person = {"name": "Alice", "age": 25}   
print(process_data(person))

为什么是坑?

如果字典键名变了,你的代码就会崩溃,而且代码可读性很差。

更好的写法:

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"代替字典,代码会更清晰,IDE 还能自动补全属性!

20.还在用"namedtuple()“,而不是"NamedTuple”

错误示范:

from collections 
import namedtuple   
Person = namedtuple("Person", ["name", "age"])

为什么是坑?

"namedtuple"需要用字符串定义字段,而且没有类型注解支持。

更好的写法:

from typing 
import NamedTuple      
class Person(NamedTuple):       
name: str       
age: int

使用"NamedTuple",不仅更易读,而且支持类型注解,适合现代 Python 代码。

21.在导入时执行代码(import-time side effects)

错误示范:

# utils.py   
print("Utils module loaded!")  # 只要 import 这个模块,就会执行这行代码
import utils  # 这里会自动输出 "Utils module loaded!"

为什么是坑?

模块导入时不应该有副作用!这样会影响性能,并导致意想不到的行为。

更好的写法:

def main():       
print("Utils module loaded!")      
if __name__ == "__main__":       
main()  # 只有直接运行这个文件时才执行

用"if name == “main”"保护代码,确保它不会在 import 时执行!

以上就是Python编程中需要避免的21个代码反模式实战详解的详细内容,更多关于Python反模式的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文