python 33个高级用法技巧
- 使用装饰器计时函数
装饰器是一种允许在一个函数或方法调用前后运行额外代码的结构。
import timedef timer(func):"""装饰器函数,用于计算函数执行时间并打印。参数:func (function): 被装饰的函数返回:function: 包装后的函数"""def wrapper(*args, **kwargs):"""包装函数,计算并打印函数执行时间。参数:*args: 原函数的非关键字参数**kwargs: 原函数的关键字参数返回:原函数的返回值"""start_time = time.time() # 记录开始时间result = func(*args, **kwargs) # 执行原函数end_time = time.time() # 记录结束时间print(f"Function {func.__name__} took {end_time - start_time} seconds")return result # 返回原函数的结果return wrapper@timer
def example_function(x):"""示例函数,等待 x 秒后返回 x。参数:x (int/float): 等待的秒数返回:int/float: 输入的 x 值"""time.sleep(x) # 模拟耗时操作return x# 调用被装饰的函数
result = example_function(2)
print(result)
Function example_function took 2.0022225379943848 seconds
2
-
装饰器功能:
- 装饰器
timer
计算并打印被装饰函数的执行时间。 - 通过
wrapper
函数实现这一功能,使得可以在不修改原函数代码的情况下,添加额外的行为。
- 装饰器
-
装饰器语法糖:
@timer
是装饰器的简洁语法,用于将装饰器应用于函数。- 相当于手动将函数传递给装饰器并将返回值赋给原函数名。
-
包装函数:
wrapper
函数接受任意数量的参数和关键字参数,确保可以包装任何函数。- 在
wrapper
中,可以在调用原函数之前或之后添加任何额外的代码,这里是计算并打印执行时间。
- 使用生成器
生成器是一种特殊的迭代器,通过yield
关键字逐个生成值。
通过使用装饰器,可以在不修改原函数代码的情况下,添加额外的行为。装饰器函数接受一个函数作为参数,返回一个新的包装函数,在调用原函数之前或之后执行额外的代码。装饰器提供了一种简洁而强大的方式来扩展函数的功能,使代码更加模块化和可重用。
def countdown(n):"""生成从 n 到 1 的倒计时序列。参数:n (int): 倒计时的起始值生成:int: 当前倒计时的值"""while n > 0:yield nn -= 1# 使用生成器函数 countdown 进行倒计时
for i in countdown(5):print(i)
5
4
3
2
1
-
生成器函数:
- 生成器函数使用
yield
关键字逐个生成值,与常规的返回值函数不同,它在每次生成值后暂停执行,并保留其状态以便继续生成下一个值。 - 在生成器函数
countdown
中,while
循环每次生成当前的n
值,然后将n
减少 1。
- 生成器函数使用
-
迭代生成器:
- 使用
for
循环迭代生成器时,循环会自动处理生成器的状态,并在每次迭代时调用生成器函数的__next__()
方法,获取下一个值。 - 当生成器函数不再生成新值时,迭代结束。
- 使用
通过使用生成器函数,可以逐个生成序列中的值,而不需要一次性创建整个序列。这种方法在处理大数据集或需要逐步生成数据时非常有用,能够节省内存并提高效率。生成器函数使用 yield
关键字生成值,并在每次生成后暂停执行,保留其状态以便后续继续生成。
- 使用命名元组
命名元组是一种特殊的元组,允许通过名称访问元素。
from collections import namedtuplePoint = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y)
10 20
- 使用全局变量
全局变量可以在多个函数之间共享数据。
global_var = 0def increment():global global_varglobal_var += 1increment()
print(global_var)
1
- 使用局部变量
局部变量在函数内部定义,只在函数内部有效。
def example():local_var = 'Hello'print(local_var)example()
# print(local_var) # 这行会报错,因为local_var是局部变量
Hello
- 使用类方法
类方法是一种绑定到类而不是实例的方法。
class MyClass:class_var = 0@classmethoddef increment_class_var(cls):cls.class_var += 1MyClass.increment_class_var()
print(MyClass.class_var)
1
- 使用静态方法
静态方法是一种不依赖于类或实例的独立方法。
class MyClass:@staticmethoddef static_method():print('This is a static method.')MyClass.static_method()
This is a static method.
- 使用实例方法
实例方法是绑定到实例的方法,可以访问实例的属性和方法。
class MyClass:def __init__(self, value):self.value = valuedef display_value(self):print(self.value)obj = MyClass(10)
obj.display_value()
10
- 使用装饰器添加功能
装饰器可以在不修改原函数代码的情况下添加功能。
def decorator(func):"""装饰器函数,用于在调用被装饰函数之前打印一条消息。参数:func (function): 被装饰的函数返回:function: 包装后的函数"""def wrapper(*args, **kwargs):"""包装函数,打印一条消息然后调用原函数。参数:*args: 原函数的非关键字参数**kwargs: 原函数的关键字参数返回:原函数的返回值"""print('Function is called')return func(*args, **kwargs)return wrapper@decorator
def say_hello():"""打印 'Hello' 的函数"""print('Hello')# 调用被装饰的函数
say_hello()
Function is called
Hello
- 使用链式函数调用
链式函数调用允许连续调用多个方法。
class MyClass:def __init__(self, value):self.value = valuedef increment(self):self.value += 1return selfdef display_value(self):print(self.value)obj = MyClass(10)
obj.increment().increment().display_value()
12
- 使用自定义迭代器
自定义迭代器可以实现自己的迭代逻辑。
class MyIterator:def __init__(self, data):"""初始化 MyIterator 对象,并设置初始数据和索引。参数:data (list): 要迭代的数据列表"""self.data = dataself.index = 0def __iter__(self):"""返回迭代器对象本身。返回:MyIterator: 迭代器对象"""return selfdef __next__(self):"""返回下一个数据元素。返回:int/float: 当前索引的数据元素抛出:StopIteration: 当没有更多元素时停止迭代"""if self.index < len(self.data):result = self.data[self.index]self.index += 1return resultelse:raise StopIteration# 创建 MyIterator 对象并迭代打印每个元素
my_iter = MyIterator([1, 2, 3])
for value in my_iter:print(value)
1
2
3
__iter__
方法返回迭代器对象本身。__next__
方法在每次迭代中被调用,返回当前索引位置的元素,并将索引加1。
当索引超出数据列表的长度时,抛出 StopIteration 异常,迭代结束。
- 使用类方法
类方法可以在不实例化类的情况下调用。
class MyClass:class_var = 0@classmethoddef increment_class_var(cls):cls.class_var += 1MyClass.increment_class_var()
print(MyClass.class_var)
1
- 使用属性装饰器
属性装饰器用于控制属性的访问和修改。
class MyClass:def __init__(self, value):"""初始化 MyClass 对象并设置初始值。参数:value (int/float): 初始值"""self._value = value@property # 属性方法def value(self):"""获取 _value 的值。返回:int/float: 当前 _value 的值"""return self._value@value.setter # 属性的设置方法def value(self, new_value):"""设置 _value 的新值。参数:new_value (int/float): 新值"""self._value = new_value# 创建一个 MyClass 对象,初始值为 10
obj = MyClass(10)# 获取并打印 _value 的值
print(obj.value) # 输出: 10# 设置 _value 的新值为 20
obj.value = 20# 获取并打印新的 _value 的值
print(obj.value) # 输出: 20
10
20
-
@property
装饰器:- 将方法转换为属性,使得可以通过
obj.value
访问,而不需要调用方法。 - 这种方法使得属性访问看起来更自然,与直接访问实例变量类似。
- 将方法转换为属性,使得可以通过
-
@value.setter
装饰器:- 将方法转换为属性的设置方法,使得可以通过
obj.value = new_value
来设置属性的值。 - 这种方法提供了一种控制属性值设置的机制,可以在设置值之前进行验证或其他处理。
- 将方法转换为属性的设置方法,使得可以通过
- 使用字典合并
合并两个字典。
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}# 使用字典的update方法合并
dict1.update(dict2)
print(dict1)
print({**dict1, **dict2})
{'a': 1, 'b': 3, 'c': 4}
{'a': 1, 'b': 3, 'c': 4}
- 使用
Counter
计数
Counter
类用于计数可哈希对象。
from collections import Counterdata = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
counter = Counter(data)
print(counter)
Counter({'apple': 3, 'banana': 2, 'orange': 1})
- 使用
deque
进行双端队列操作
deque
是一种双端队列,可以在两端高效地添加和删除元素。
from collections import dequed = deque([1, 2, 3])
d.appendleft(0)
d.append(4)
print(d)
deque([0, 1, 2, 3, 4])
- 使用
defaultdict
defaultdict
是一个带有默认值的字典。
from collections import defaultdictdd = defaultdict(int)
dd['a'] += 1
print(dd)
defaultdict(<class 'int'>, {'a': 1})
- 使用堆排序
使用heapq
模块进行堆排序。
import heapq# 初始化一个无序列表
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]# 使用 heapq.heapify 将列表转换为堆
heapq.heapify(data)# 使用 heapq.heappop 逐个弹出最小元素,实现排序
sorted_data = [heapq.heappop(data) for _ in range(len(data))]# 打印排序后的列表
print(sorted_data)
[1, 1, 2, 3, 4, 5, 5, 6, 9]
-
堆排序过程:
heapq.heapify(data)
将列表data
转换为最小堆,最小元素在堆的根节点。heapq.heappop(data)
逐个弹出最小元素,重新调整堆结构,使得次小元素成为新的根节点。- 通过列表推导式,所有元素依次弹出并存入新的列表
sorted_data
,实现排序。
-
堆的性质:
- 最小堆是一种完全二叉树结构,满足父节点小于或等于子节点的性质。
- 这种结构使得获取最小元素的时间复杂度为
O(1)
,插入和删除元素的时间复杂度为O(log n)
。
import heapq# 初始化一个无序列表
data = [3, 1, 4, 1, 5, 9, 2, 6, 5]# 将所有元素取反,构建最大堆
max_heap = [-x for x in data]# 使用 heapq.heapify 将列表转换为堆
heapq.heapify(max_heap)# 使用 heapq.heappop 逐个弹出最大元素(原值)
sorted_data = [-heapq.heappop(max_heap) for _ in range(len(max_heap))]# 打印排序后的列表
print(sorted_data)
[9, 6, 5, 5, 4, 3, 2, 1, 1]
通过使用 heapq
模块,可以间接实现大顶堆和堆排序。虽然 heapq
主要支持最小堆,但通过取反数的方法,可以高效地实现最大堆排序。
- 使用
bisect
进行二分查找
使用bisect
模块进行二分查找。
import bisect# 初始化一个有序列表
data = [1, 2, 4, 4, 5]# 使用 bisect.insort 在合适的位置插入元素 3
bisect.insort(data, 3)# 打印插入后的列表
print(data)
[1, 2, 3, 4, 4, 5]
-
二分查找:
bisect
模块使用二分查找算法在有序列表中找到元素应该插入的位置。- 二分查找的时间复杂度为
O(log n)
,比线性查找O(n)
更高效。
-
插入元素:
insort
函数不仅找到插入位置,还会将元素插入到该位置。
- 使用
itertools
生成排列组合
import itertools# 生成 'ABC' 字符串的长度为2的排列
permutations = list(itertools.permutations('ABC', 2))
# 生成 'ABC' 字符串的长度为2的组合
combinations = list(itertools.combinations('ABC', 2))print('Permutations:', permutations)
print('Combinations:', combinations)
Permutations: [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
Combinations: [('A', 'B'), ('A', 'C'), ('B', 'C')]
- 使用
itertools
生成无限序列
import itertoolscounter = itertools.count(start=1, step=2)
print(next(counter))
print(next(counter))
print(next(counter))
1
3
5
- 使用
functools.partial
使用partial
函数创建部分参数的函数。
from functools import partialdef power(base, exponent):"""计算 base 的 exponent 次方。参数:base (int/float): 底数exponent (int/float): 指数返回:int/float: base 的 exponent 次方"""return base ** exponent# 使用 partial 函数创建一个新的函数 square,固定 exponent 参数为 2
square = partial(power, exponent=2)# 计算 3 的平方
print(square(3)) # 输出: 9
9
partial
函数:
partial
函数用于固定一个函数的部分参数,从而创建一个新的函数。
在这个例子中,partial
被用来固定power
函数的exponent
参数为 2,从而创建一个新的函数square
。
- 使用
functools.lru_cache
使用lru_cache
缓存函数结果,提高性能。
from functools import lru_cache@lru_cache(maxsize=None)
def fibonacci(n):"""计算第n个斐波那契数,使用lru_cache进行缓存以提高性能。参数:n (int): 需要计算的斐波那契数的索引返回:int: 第n个斐波那契数"""if n < 2:return nreturn fibonacci(n-1) + fibonacci(n-2)# 打印第10个斐波那契数
print(fibonacci(10))
55
-
缓存的优势:
- 在没有缓存的情况下,递归计算斐波那契数会有大量的重复计算。例如,计算
F(10)
会多次计算F(9)、F(8)
等。 - 使用
lru_cache
后,每个值只计算一次,然后存储在缓存中,以后再需要相同值时直接从缓存中读取,避免重复计算,提高了性能。
- 在没有缓存的情况下,递归计算斐波那契数会有大量的重复计算。例如,计算
-
递归过程:
- 当计算
fibonacci(10)
时,函数会递归调用fibonacci(9)
和fibonacci(8)
,依次类推,直到调用fibonacci(0)
和fibonacci(1)
。 - 由于
lru_cache
的存在,计算fibonacci(10)
的整个过程中,每个值只会计算一次,并存储在缓存中。
- 当计算
- 使用
subprocess
运行外部命令
import subprocessresult = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)
print(result.stdout)
Hello, World!
- 使用
shutil
进行文件操作
import shutil
# 创建一个文件 example.txt 并写入内容
with open('example.txt', 'w') as file:file.write('Hello, world!')
shutil.copy('example.txt', 'example_copy.txt')
print('File copied.')
File copied.
- 使用
pathlib
处理文件路径
from pathlib import Path# 创建一个文件 example.txt 并写入内容
with open('example.txt', 'w') as file:file.write('Hello, world!')# 使用 pathlib 创建一个 Path 对象
p = Path('example.txt')# 打印文件名
print(p.name) # 输出:example.txt# 打印文件名(不包括后缀)
print(p.stem) # 输出:example# 打印文件后缀
print(p.suffix) # 输出:.txt# 打印文件的父目录
print(p.parent) # 输出:.
example.txt
example
.txt
.
- 使用正则表达式匹配字符串
使用re
模块进行正则表达式匹配。
import repattern = re.compile(r'\d+')
match = pattern.search('The answer is 42')
print(match.group())
42
- 使用内存映射文件
使用mmap
模块进行内存映射文件操作。
import mmap
# 创建一个文件 example.txt 并写入内容
with open('example.txt', 'w') as file:file.write('Hello, world!')
# 打开文件 example.txt 进行读写操作 ('r+b' 表示读写二进制模式)
with open('example.txt', 'r+b') as f:# 使用 mmap 模块创建内存映射对象with mmap.mmap(f.fileno(), 0) as mm:# 从内存映射对象中读取一行,并将其解码为 UTF-8 字符串print(mm.readline().decode('utf-8'))
Hello, world!
with mmap.mmap(f.fileno(), 0) as mm:
使用 mmap
模块创建一个内存映射对象。f.fileno()
返回文件的文件描述符,0
表示将整个文件映射到内存中。with
语句确保内存映射对象在块结束时会自动关闭。
- 使用logging记录日志
import logginglogging.basicConfig(level=logging.INFO)
logging.info('This is an info message')
INFO:root:This is an info message
- 使用
argparse
解析命令行参数
import argparsedef main(name="Default Name"):print(f"Hello, {name}!")if __name__ == "__main__":parser = argparse.ArgumentParser(description='Example script')parser.add_argument('name', type=str, nargs='?', default="Default Name", help='Your name')args = parser.parse_args()main(args.name)
Hello, Alice!
- 使用
unittest
进行单元测试
import unittestdef add(x, y):return x + yclass TestAdd(unittest.TestCase):def test_add(self):self.assertEqual(add(2, 3), 5)if __name__ == '__main__':unittest.main(argv=[''], verbosity=2, exit=False)
test_add (__main__.TestAdd) ... ok----------------------------------------------------------------------
Ran 1 test in 0.002sOK
- 使用
tqdm
显示进度条
from tqdm import tqdm
import timefor i in tqdm(range(100)):time.sleep(0.01)
100%|██████████| 100/100 [00:01<00:00, 97.88it/s]
- 使用
pandas
进行数据分析
import pandas as pddata = {'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35]}
df = pd.DataFrame(data)
print(df)
Name Age
0 Alice 25
1 Bob 30
2 Charlie 35