一、装饰器

装饰器(decorator)是 Python 中的一种高级功能,用于在不修改原函数代码的前提下,动态扩展函数或类的功能

本质上,装饰器是一个函数:它接收一个函数作为参数,并返回一个新的函数(通常是对原函数的增强版本)。

装饰器通过 @decorator_name 语法应用在函数或方法定义之前。

Python 还提供了一些内置装饰器,例如 @staticmethod 和 @classmethod。

常见应用场景:

  • 日志记录:记录函数调用信息、参数和返回值
  • 性能统计:统计函数执行时间
  • 权限控制:限制函数访问权限
  • 缓存:缓存函数结果,提高性能

1.1 基本语法

装饰器的核心思想是:用一个函数"包装"另一个函数

def decorator_function(original_function):
    def wrapper(*args, **kwargs):
        # 调用前
        print("执行前")

        result = original_function(*args, **kwargs)

        # 调用后
        print("执行后")

        return result
    return wrapper

@decorator_function
def target_function():
    print("原函数执行")

解析:

  • decorator_function:装饰器函数(接收原函数)
  • wrapper:包装函数(真正被执行)
  • @decorator_function:等价于函数替换

等价写法:

target_function = decorator_function(target_function)

1.2  使用装饰器

装饰器通过 @ 语法糖应用在函数定义前:

@time_logger
def target_function():
    pass

等价于:

def target_function():
    pass

target_function = time_logger(target_function)

这种机制使我们可以在不修改原函数的情况下,统一添加功能(如日志、权限等)。

1.2.1 没有返回值的函数的装饰器

打印日志功能

def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

say_hello()

1.2.2 带有返回值的函数的装饰器

带有返回值的函数的装饰器,需要通过 functools 返回函数包装。


# 修改方法:
import functools

def exec_log(fun):
    @functools.wraps(fun)  # 保留原函数的元信息(如名称、文档字符串等)
    def wrapper(*args, **kwargs):
        print("开始执行函数")
        ret = fun()
        print(f"函数执行完成 ret={ret}")
        # 原函数有返回,则装饰器函数必须有返回
        # 否则报错:TypeError: 'NoneType' object is not callable
        return ret
    return wrapper  # 返回包装函数,而不是执行结果

@exec_log
def exec_me(): # 建议避免使用 exec 作为函数名,因为它是 Python 内置关键字
    print("Welcome to China!")
    return 200

# 调用原函数
r = exec_me()
print(r)

输出内容:

开始执行函数
Welcome to China!
函数执行完成 ret=200
200

1.2.3 带参数的装饰器

如果原函数有参数,需要在 wrapper 中使用 *args, **kwargs

如果原函数有参数,需要在 wrapper 中使用 *args, **kwargs

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("执行前")
        func(*args, **kwargs)
        print("执行后")
    return wrapper

@my_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

输出:

执行前
Hello, Alice!
执行后

说明:使用 *args, **kwargs 可以兼容任意参数函数。

进阶:装饰器执行重复打印3次,

def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()

# 输出:

# Hello! 悟空

# Hello! 悟空

# Hello! 悟空

说明:这是"装饰器工厂",外层函数用于接收参数。

1.3 类装饰器

单例模式

class SingletonDecorator:
    def __init__(self, cls):
        self.cls = cls
        self.instance = None

    def __call__(self, *args, **kwargs):
        if self.instance is None:
            self.instance = self.cls(*args, **kwargs)
        return self.instance

@SingletonDecorator
class Database:
    def __init__(self):
        print("初始化")

db1 = Database()
db2 = Database()
print(db1 is db2)
输出:
初始化
True

1.4 内置装饰器

常用内置装饰器:

  1. @staticmethod:定义静态方法
  2. @classmethod:定义类方法
  3. @property:将方法变为属性
class MyClass:
    @staticmethod
    def static_method():
        print("静态方法")

    @classmethod
    def class_method(cls):
        print(cls.__name__)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

1.5 多个装饰器

函数采用多个装饰器的话,从下到上依次执行。

# 1.4 多个装饰器的堆叠
# 多个装饰器从下到上依次执行:
def decorator1(func):
    def wrapper():
        print("Decorator 1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2")
        func()
    return wrapper

# 两个装饰器,先执行第一个装饰器,打印:"Decorator 1"
@decorator1
# 再执行第二个装饰器,打印:"Decorator 2";然后执行原函数,打印:"Hello!"
@decorator2
def say_hello():
    print("Hello!")

# 输出:
# Decorator 1
# Decorator 2
# Hello!
say_hello()

核心总结

装饰器 = 函数包装函数 + 不修改原代码扩展功能
  • @ 语法本质是函数替换
  • wrapper 才是真正执行的函数
  • 推荐使用 *args, **kwargs 提高通用性
  • 支持函数、类、甚至带参数的装饰器

二、迭代器

迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。

迭代器是一个可以记住遍历的位置的对象。

迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

迭代器有两个基本的方法:iter() 和 next()

字符串,列表或元组对象都可用于创建迭代器:

2.1 迭代器

语法:

for 变量 in 可迭代:
    pass

iterable: 可迭代的东西,例如:字符串、数组、元组、集合Set、列表、字典、File

for循环迭代:

# 字符串的迭代
for c in "我是中国人,我爱中国":
    print(c)

2.2 创建迭代器

迭代器的创建有两种方式。

方式一:从可迭代对象获取迭代器 __iter__() 方法

list = [1,2,3,4,5]
# 1)从可迭代对象获取迭代器 __iter__() 方法
it = list.__iter__()
print(it)
# 输出:1
print(it.__next__())
# 输出:2
print(it.__next__())
# 输出:3
print(it.__next__())

方式二:iter(可迭代)创建迭代器

采用iter函数创建迭代器。迭代器是可迭代的,可以使用for遍历。

# 2)通过 iter(可迭代) 创建迭代器
print("通过iter创建迭代器")
iter = iter(list)
for item in iter:
    print(item)

print("迭代器遍历完成")

总结:

两种获取迭代器的方法:

1.iter内置函数可以获取迭代器

2.可迭代对象的__iter__()方法可以获取迭代器。特殊方法。

for里面一定可以拿到迭代器的。所以所有不可迭代的东西不能使用for循环遍历。

迭代器统一了所有不同数据类型的遍历工作。

2.3 采用next获取迭代器元素

list = [1,2,3,4,5]
it2 = iter(list)
while True:
    print (next(it2))

next遍历迭代器后,遍历完成,会报错StopIteration。

因为迭代器没有提供是否遍历完成的函数,next怎么安全遍历呢?

可以通过 try-catch捕捉StopIteration异常

list = [1,2,3,4,5]
it2 = iter(list)
while True:
    try:
        print (next(it2))
    except StopIteration:
        print("next遍历完成")

2.3.1 StopIteration

StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 __next__() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。

在 20 次迭代后停止执行:

class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
 
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
 
myclass = MyNumbers()
myiter = iter(myclass)
 
for x in myiter:
  print(x)

迭代器本身的特性:

1.只能往前不能反复

2.特别节省内存

3.惰性机制

三、生成器

生成器本质就是迭代器。

在 Python 中,使用了 yield 的函数被称为生成器(generator)。

yield 是一个关键字,用于定义生成器函数,生成器函数是一种特殊的函数,可以在迭代过程中逐步产生值,而不是一次性返回所有结果。

调用一个生成器函数,返回的是一个迭代器对象。

def countdown(n):
    while n > 0:
        yield n
        n -= 1
 
# 创建生成器对象
generator = countdown(5)
# 生成器不会直接返回结果
# 输出:<generator object countdown at 0x10450bf40>
print(generator)
 
# 通过迭代生成器获取值
print(next(generator))  # 输出: 5
print(next(generator))  # 输出: 4
print(next(generator))  # 输出: 3
 
# 使用 for 循环迭代生成器
for value in generator:
    print(value)  # 输出: 2 1

3.1.创建生成器

创建生成器有两种方法:

1)生成器函数

2)生成器表达式

3.1.1 生成器函数

生成器函数:生成器函数中有一个关键字 yield.

# 1.1 生成器函数
def fun():
    print(1234567)
    # yield返回结果
    yield 999

# 生成器函数返回的是迭代器
ret = fun()
# 可以采用迭代器next获取结果
# 只有执行next时候,生成器的函数体才会执行。
print(ret.__next__())

3.1.2 yield可以分段返回

例如:fun2分2段返回结果,第一段返回999,第二段返回888

# yield可以分段返回
def fun2():
    print(1234567)
    # yield返回结果
    yield 999 # yield也有返回的意思。return立即返回结果,yield只有执行到next的时候才会返回。
    # 第二段返回
    print(45678)
    yield 888

ret = fun2()
print("第一段返回结果")
# 第一段返回结果:
# 输出:
# 1234567
# 999
print(ret.__next__())
print()
print("第二段返回结果")
# 第二段返回结果:
# 输出:
# 45678
# 888
print(ret.__next__())

3.2 生成器的读取

生成器的遍历和读取,和迭代器类似。

3.2.1 生成器分段返回

生成器结果分段返回,有什么使用场景呢?

例如:制作10000件衣服,分批进行制作,制作完一批,返回一批。继续制作下一批。

四、推导式

推导式目的主要是简化代码。

4.1 列表推导式

语法:

列表推导式:[数据 for循环 if判断]

# 1.列表推导式
# 1)将1~10添加到列表中
list = [i for i in range(1,10)]
# 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9]
print(list)

# 2)添加20件衣服,衣服名称命名为”衣服i“
list = [f"衣服{i}" for i in range(1,10)]
# 输出:['衣服1', '衣服2', '衣服3', '衣服4', '衣服5', '衣服6', '衣服7', '衣服8', '衣服9']
print(list)

4.2 集合推导式

语法:

集合推导式:{数据 for循环 if判断}

# 2.集合推导式
# 将1~10添加到集合中
set = {i for i in range(1,10,2)}
# 输出:{1, 3, 5, 7, 9}
print(set)

4.3 字典推导式

语法:

字典推导式:{key:value for循环 if判断}

# 3.字典推导式
# 将列表中的索引作为键,元素作为值,生成字典
list = ["白龙马","唐僧", "白晶晶", "铁扇公主"]
# 字典推导式
dict = {i:list[i] for i in range(len(list))}
# 输出:{0: '白龙马', 1: '唐僧', 2: '白晶晶', 3: '铁扇公主'}
print(dict)

五、生成器表达式

4.生成器表达式

语法:

 (数据 for循环 if判断)

# 生成1~10的平方的列表
# 创建生成器表达式
gen = (i**2 for i in range(1,10,2))
# 生成器列表的数据打印
for item in gen:
    print(item)
# 输出:
# 1
# 9
# 25
# 49
# 81
# 生成1~10的平方的列表生成的结果作为列表
lst = list(gen)
# 输出:[]
print(lst)
# 为什么是空列表呢?
# 因为生成器表达式是一次性的,在for item in gen:已经遍历完成,因此剩余的内容遍历是一个空列表

因为生成器表达式是一次性的,在for item in gen:已经遍历完成,因此剩余的内容遍历是一个空列表。

更多推荐