一、高质量python
参考资料: 《EffectivePython:编写高质量python代码的90个有效方法》
1.使用f-string替代format
- ------------ format
name = '张三'
age = 12
n = 5
content = '{}今年{}岁'.format(name,age)
print(content)
content = '{0}今年{1}岁'.format(name,age)
print(content)
content = '{name}今年{age}岁'.format(name=name,age=age)
print(content)
content = '{0}年后,{1}将是{2}岁'.format(n,name,age+n)
print(content)
name = '张三'
age = 12
n = 5
content = f'{name}今年{age}岁'
print(content)
num = 1.2455
int_num = 3
n = 5
print(f'{num}保留2位小数:{num:.2f}')
print(f'{num}保留{n}位小数:{num:.{n}f}')
print(f'{num}转为int:{int(num)}')
print(f'整数{int_num},以0填充至{n}位:{int_num:0{n}d}')
2.尽量用enumerate取代range
示例:需要将a非str的值打印出来,并记录下标
a = ['s',3,'sf','6',0]
tmp = []
for i in range(len(a)):
if not isinstance(a[i],str):
print(a[i])
tmp.append(i)
a = ['s',3,'sf','6',0]
tmp = []
for idx,item in enumerate(a):
if not isinstance(item,str):
print(item)
tmp.append(idx)
a = [('张三',22),('李四',18)]
for idx,(name,age) in enumerate(a,start=1):
print(f'第{idx}个人信息:\n姓名:{name}\n年龄:{age}')
3.使用zip函数同时遍历两个迭代器
names = ['张三','李四']
ages = [22,12]
info = {}
for i in range(len(names)):
info[names[i]] = {'name': names[i], 'age': ages[i]}
info = {}
for name,age in zip(names,ages):
info[name] = {'name':name,'age':age}
4.不要在for与while循环后写else块
a = [1,2,3]
for i in a:
print(i)
else:
print('else')
for i in a:
if i >3:
print(i)
break
else:
print('else')
print(a)
5.使用赋值表达式(海象表达式:=)减少重复代码
python 3.8及之后才支持
info = [{'name': '张三', 'age': 22}, {'name': '李四', 'age': 12}]
for person in info:
name = person.get('name')
age = person.get('age')
if name == '张三':
if age >= 18:
print(name,'成年了,年龄:',age)
for person in info:
if (name := person.get('name')) == '张三':
if (age := person.get('age')) >= 18:
print(name,'成年了,年龄:',age)
6.用sort的key来表示复杂排序逻辑
info = [{'name': '张三', 'age': 22,'weight':55},
{'name': '李四', 'age': 12,'weight':55},
{'name': '王五', 'age': 22,'weight':70},
{'name': '赵六', 'age': 7}]
info.sort(key = lambda item:(item.get('weight',999
),item.get('age',999)),reverse=True)
print(info)
info.sort(key = lambda item:item.get('age',999),reverse=False)
info.sort(key = lambda item:item.get('weight',0),reverse=True)
print(info)
7.尽量使用参数名进行可选参数传递
def func(a,b,c=1):
pass
func(1,2,3)
def func(a,b,d=0,c=1):
pass
func(1,2,3)
func(1,2,c=3)
func(1,2,c=3,d=4)
8.使用/和*指定参数传递方式
def func(a,b,/,c,d):
print(a,b,c,d)
func(a=1,b=2,c=3,d=4)
func(1,2,3,4)
func(1,2,c=3,d=4)
def func1(a,b,*,x,y):
print(a,b,x,y)
func1(1,2,x=3,y=4)
9.用None和docstring来描述默认值会变的参数
import time
def func(t=time.time()):
print(t)
func()
time.sleep(5)
func()
def func(t=None):
"""t传入None,将通过time.time()获取当前时间"""
if t:
print(t)
else:
t=time.time()
print(t)
func()
10.使用cProfile和pstats进行性能分析
from cProfile import Profile
from pstats import Stats
def a():
for i in range(3):
time.sleep(1)
def b():
for i in range(2):
time.sleep(1)
def t():
a()
b()
print(1,2,3)
profiler = Profile()
profiler.runcall(t)
stats = Stats(profiler)
stats.strip_dirs()
stats.sort_stats('cumulative')
stats.print_stats()
ncalls tottime percall cumtime percall filename:lineno(function) 1 0.000 0.000 5.031 5.031 Python_练习.py:1643(t) 5 5.031 1.006 5.031 1.006 {built-in method time.sleep} 1 0.000 0.000 3.007 3.007 Python_练习.py:1635(a) 1 0.000 0.000 2.024 2.024 Python_练习.py:1639(b) 1 0.000 0.000 0.000 0.000 {built-in method builtins.print} 1 0.000 0.000 0.000 0.000 {method ‘disable’ of ‘_lsprof.Profiler’ objects}
ncalls:函数在测评期间的调用次数 tottime:执行函数本身所花时间(不含调用其他函数时间) tottimepercall:执行这个函数平均所花时间(不含调用其他函数时间) cumtime:执行这个函数及它调用其他函数所花时间 cumtimepercal:执行这个函数它调用其他函数平均所花时间
二、python性能优化
参考资料: 微信公众号 - 菜鸟学python
1和2都是相同的原理:局部变量存放在栈区,全局变量和对象存放在堆区,从栈区查找数据的速度会更快
1.避免全局变量
a = 1
t1 = time.time()
for i in range(10000000):
a + 1
t2 = time.time()
print(t2-t1)
def func():
a = 1
t1 = time.time()
for i in range(10000000):
a + 1
t2 = time.time()
print(t2 - t1)
func()
2.避免使用.访问属性
对于频繁访问的属性,可通过赋值给一个局部变量以提高性能
class A():
pass
def func1():
a = []
t1 = time.time()
for i in range(1000000):
a.append(A.__name__)
t2 = time.time()
print(t2-t1)
def func2():
a = []
b = A.__name__
t1 = time.time()
for i in range(1000000):
a.append(b)
t2 = time.time()
print(t2-t1)
func1()
func2()
3.使用join替代+进行字符串拼接
因为str不可变,每次相加都会申请内存空间,n-1次 使用join会一次性的申请所需的内存空间
lis = [str(i) for i in range(100000)]
def func1():
res = ''
t1 = time.time()
for s in lis:
res += s
t2 = time.time()
print(t2-t1)
def func2():
t1 = time.time()
res = ''.join(lis)
t2 = time.time()
print(t2-t1)
func1()
func2()
4.利用好if的短路特性
当多个or条件时:遇到为True的就不会再进行后面的判断,将为True概率大的表达式排在前面,能够有效提升性能 当多个and条件时:遇到为False的就不会再进行后面的判断,将为False概率大的表达式排在前面,能够有效提升性能
def s():
time.sleep(3)
return False
def func1():
if 1==1 or s():
return 0
def func2():
if s() or 1==1:
return 0
t1 = time.time()
func1()
t2 = time.time()
print(t2-t1)
t1 = time.time()
func2()
t2 = time.time()
print(t2-t1)
5.减少内层for循环的计算
def func1(x,y):
for i in range(x):
for j in range(y):
res = (x**2) +(y**2)
def func1(x,y):
for i in range(x):
tmp = x**2
for j in range(y):
res = tmp +(y**2)
6.数据复制
copy:浅复制,只复制第一层,下层还是指向原对象的下层,对下层的数据操作是在原对象上进行的,所以会对被复制的对象造成影响 deepcopy:深复制,完整的复制一个新对象
1)对于list和dict,优先使用list和dict类实现的copy/deepcopy方法,会比copy模块的快很多 2)copy比deepcopy耗时更少(性能相差几十倍),对于不是必须使用deepcopy的场景,优先使用copy
三、一些模块介绍
1.collections
这个模块实现了特定目标的容器,以提供Python标准内建容器 dict、list、set、tuple 的替代选择。
Counter:字典的子类,提供了可哈希对象的计数功能 defaultdict:字典的子类,提供了一个工厂函数,为字典查询提供了默认值 OrderedDict:字典的子类,保留了他们被添加的顺序(python 3.6之后dict是有序的 了) namedtuple:创建命名元组子类的工厂函数 deque:类似列表容器,实现了在两端快速添加(append)和弹出(pop) ChainMap:类似字典的容器类,将多个映射集合到一个视图里面
1)Counter
_list = [1,2,3,4,1,12,3,4,23,4,123,12,'a','b','a']
counter = collections.Counter(_list)
print(counter)
print(counter[1])
print(counter.values())
print(counter.get(1))
print(counter.copy())
print(counter.keys())
print(counter.items())
counter.update({'a':2})
print(counter)
counter.subtract({'a':1})
print(counter)
print(counter.elements())
print(next(counter.elements()))
print(list(counter.elements()))
print(counter.most_common())
print(counter.most_common(2))
counter.pop('a')
print(counter)
counter.popitem()
print(counter)
counter.popitem()
print(counter)
print(counter.setdefault('a',0))
print(counter)
print(counter.setdefault('d',0))
print(counter)
_tuple = (1,2,3,4,1,12,3,4,23,4,123,12,'a','b','a')
counter = collections.Counter(_tuple)
print(counter)
print(counter[1])
_dict = {'a':1,'b':2,'c':3}
counter = collections.Counter(_dict)
print(counter)
2.typing-类型提示支持
python3.6加入的特性 不符合类型要求时,会有个提示,但不按类型提示输入也不会报错
from typing import List,Dict,Tuple,Callable,NewType,TypeVar,Generic
def greeting(name: str) -> str:
return 'Hello ' + name
greeting(123)
greeting('123')
ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]
def func1(a:ConnectionOptions) -> str:
pass
a = {'a':1}
func1(a)
b = {'b':'1'}
func1(b)
def func2(a:Address) -> str:
pass
a = {'a':1}
func2(a)
b = [1,2,3]
func2(b)
c = (1,2,3)
func2(c)
d = (1,'2')
func2(d)
e = ('1',2)
func2(e)
Conn = List[str or int]
def func3(a:Conn) -> str:
pass
a = [1,2,3]
func3(a)
b = ['s','d',1]
func3(b)
def xx(a:str)->int:
return 0
def yy(a:str)->str:
return '1'
def zz(a:str,b:str)->int:
return 0
def func(x:Callable[[str],int]) -> list:
return []
func(xx)
func(yy)
func(zz)
T = TypeVar('T')
S = TypeVar('S', bound=str)
A = TypeVar('A', str, bytes)
from typing import TypeVar, Generic
T = TypeVar('T')
class LoggedVar(Generic[T]):
def __init__(self, value: T, name: str) -> None:
self.name = name
self.value = value
def set(self, new: T) -> None:
self.value = new
def get(self) -> T:
return self.value
from typing import Any
def func(a:Any) -> str:
try:
return str(a)
except:
return 'error'
from typing import Optional
def foo(arg: int = 0) -> None:
...
def foo(arg: Optional[int] = None) -> None:
...
from typing import Type
a = 3
b = Type(a)
def func(a:str) -> b:
return 0
from typing import AnyStr
def func(a:str) -> int:
return 0
func('s')
func(u's')
func(b's')
def func1(a:AnyStr) -> int:
return 0
func1('s')
func1(u's')
func1(b's')
3.pydantic-数据类型验证
pydantic 可以在代码运行时强制执行类型提示
from pydantic import BaseModel
from enum import Enum
from typing import List
class SEX(Enum):
male = '男'
female = '女'
class User(BaseModel):
userNm : str
age : int
sex : SEX
hobbies : List[str] = []
def getUserNm(self):
return self.userNm
def getAge(self):
return self.age
def getSex(self):
return self.sex
def getHobbies(self):
return self.hobbies
zhangSan = {'userNm':'zhangSan',
'age':18,
'sex':'男',
'hobbies':['游泳']}
user = User(**zhangSan)
print(user.dict())
print(user.json())
print(user.schema())
print(user.schema_json())
4.traceback-异常和栈轨迹
使用try except捕获到异常后,Excepption.args只能拿到顶层的错误信息,如果相要拿到完整的异常信息,可以使用此模块
记住traceback.print_exc()和traceback.format_exc()就够了
traceback.print_exc():异常信息重定向,file参数默认None 输出到控制台,也可传入一个打开的文件 将信息写入文件 traceback.format_exc():返回str,和print_exc()看到的信息一样
mport traceback
import sys
def add():
return 1+None
def sub():
return 1-None
try:
add()
sub()
except:
with open('./trace.txt','w',encoding='utf8') as file:
traceback.print_exc(limit=1,file=sys.stdout,chain=True)
errInfo = traceback.format_exc(limit=1,chain=True)
print(errInfo)
5.chardet-编码格式检查
import chardet
with open(file=filePath,mode='rb') as file:
data = file.read()
encodingInfo = chardet.detect(data)
encoding = encodingInfo['encoding'] if encodingInfo['encoding'] == 'utf-8' else 'gbk'
data = str(data,encoding=encoding)
|