生成器
生成器是 Python 中一种特殊的迭代器,它不需要一次性将所有值都生成出来,而是可以在需要时逐个生成值,从而节省内存空间,生成器在处理大量数据或需要延迟生成的数据时非常有用。
生成器有两种实现方式,一种是利用生成器表达式,另一种是利用关键字 yield
。
生成器表达式
x = (i for i in range(5)) # 生成器表达式;x 是生成器对象;[i for i in range(5)] 是列表表达式或列表推导式
print(x)
for i in range(3):
print(next(x)) # 利用 next 获取生成器对象中的下一个元素;调用一次 next,生成一个值
print(list(x)) # 获取生成器对象中还未生成的值;生成器中所有的值都已生成,这时若再调用 next,会报错
---------
<generator object <genexpr> at 0x0000021F7EC067C8>
0
1
2
[3, 4]
# 生成器还可用于 for 循环中
x = (i for i in range(5))
for i in x:
print(i)
---------
0
1
2
3
4
yield
生成器可以使用函数和关键字 yield
来定义,yield
语句的作用类似于 return
,但不同之处在于它会将值返回给调用者,并暂停生成器的执行,等待下一次调用。
当使用 next()
作用于生成器对象时,函数开始执行,在遇到 yield
时暂停执行,并返回 yield
后面的值;当再次使用 next()
时,函数会从原来暂停的地方继续执行,直到遇到 yield
语句,如果没有 yield
语句,则抛出异常。
def my_generator():
yield 1
yield 2
yield 3
gen = my_generator() # 创建生成器对象
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
---------
<generator object my_generator at 0x0000019D384AE7C8>
1
2
3
# 生成器还可用于 for 循环中,类似于 range(),使得代码更加简洁
for value in gen:
print(value)
---------
1
2
3
生成器方法
生成器比迭代器多 3 个方法,send()、throw()、close()。
send() 可以和 next() 一样用来生成值(即调用 send() 时会从生成器对象中生成值),还可以往生成器内部传递数据,会把数据传递给 yield 前面的变量;使用 send() 之前,生成器对象至少已经通过 next() 生成过一次值。
def generator():
for i in range(10):
yield i
gen = generator()
print(gen.send(6)) # 会报错,因为使用 send() 之前还未调用过 next()
def generator():
for i in range(10):
x = yield i
print('send() 传入的数据为:', x)
gen = generator()
next(gen) # 先调用一次 next(),再调用 send() 时便不会报错
print(gen.send(11)) # 传入的 11 会赋值给 yield 前面的变量 x;同时会从生成器对象中生成一个值
print(gen.send(12))
print(gen.send(13))
---------
send() 传入的数据为: 11
1
send() 传入的数据为: 12
2
send() 传入的数据为: 13
3
def generator():
host = yield
for i in range(10):
host = yield f'https://{host}/user/login'
gen = generator()
next(gen)
print(gen.send('www.baidu.com'))
print(gen.send('www.csdn.net'))
print(gen.send('www.github.com'))
---------
https://www.baidu.com/user/login
https://www.csdn.net/user/login
https://www.github.com/user/login
throw() 方法表示在生成器中抛出异常,效果等同于 raise。
close() 方法关闭生成器,关闭后再使用 next() 会报错。
迭代器
在 Python 中,迭代器(Iterator)是一个可以逐个访问元素并在需要时生成元素的对象。它具有两个基本方法:__iter__()
和 __next__()
。
__iter__()
方法返回迭代器对象本身。这使得迭代器可以在循环中使用,并且可以在需要时通过iter()
函数进行迭代。__next__()
方法返回迭代器的下一个元素。如果没有更多的元素可供返回,则引发StopIteration
异常。
迭代器通常与可迭代对象一起使用。可迭代对象是一类具有 __iter__()
方法的对象,该方法返回一个迭代器。例如,列表、元组、集合和字典都是可迭代对象,因为它们都具有 __iter__()
方法,可以返回一个迭代器。生成器也是一种特殊类型的迭代器。
迭代器的工作原理是通过维护一个内部状态来记录当前位置,并且在每次调用 __next__()
方法时更新状态以返回下一个元素。这使得迭代器能够按需生成元素,而不必一次性将所有元素加载到内存中。
__iter__()
和 iter()
都可用来创建迭代器。
__iter__()
方法:__iter__()
是一个特殊方法(或称为魔术方法),用于定义一个对象是可迭代的。- 当对象需要支持迭代操作时,可以在其类中实现
__iter__()
方法,该方法应返回一个迭代器对象。 - 这个方法被内置的
iter()
函数隐式调用,用于获取对象的迭代器。
iter()
函数:iter()
是一个内置函数,用于获取可迭代对象的迭代器。- 当你需要迭代访问一个可迭代对象(例如列表、元组、集合、字典等)时,可以使用
iter()
函数获取其迭代器。 iter()
函数接受一个可迭代对象作为参数,并返回该对象对应的迭代器。
区别总结:
__iter__()
方法是在类中实现的,用于将对象定义为可迭代的,并且应该返回一个迭代器对象。iter()
函数是一个内置函数,用于获取可迭代对象的迭代器。
在实际应用中,通常不直接调用对象的 __iter__()
方法,而是使用 iter()
函数来获取迭代器。例如,可以使用 iter()
函数来迭代访问列表、元组、集合、字典等数据类型。
__next__()
方法和 next()
函数都是用于获取迭代器的下一个元素,但它们有以下区别:
__next__()
方法:__next__()
方法是迭代器对象内部的一个特殊方法(或称为魔术方法),用于返回迭代器的下一个元素。- 在自定义迭代器类中,需要实现
__next__()
方法以定义迭代器的行为。 - 如果迭代器没有更多的元素可供返回,则
__next__()
方法应该引发StopIteration
异常。
next()
函数:next()
是一个内置函数,用于从迭代器中获取下一个元素。- 它接受一个迭代器作为参数,并返回该迭代器的下一个元素。
- 如果迭代器没有更多的元素可供返回,则
next()
函数会引发StopIteration
异常。
区别总结:
__next__()
方法是迭代器对象内部的方法,用于返回迭代器的下一个元素。next()
函数是一个内置函数,用于从迭代器中获取下一个元素。
在实际应用中,通常使用 next()
函数来从迭代器中获取下一个元素,而不是直接调用迭代器的 __next__()
方法。这是因为 next()
函数更加方便,并且能够处理 StopIteration
异常,使代码更加健壮。
a = [i for i in range(1, 6)] # 列表,可迭代对象
b = (10, 20, 30) # 元组,可迭代对象
a_iter = iter(a) # 参数为可迭代对象(列表、元组、字典、集合、字符串、文件对象等);使用 iter() 函数时会调用 __iter__() 方法;返回迭代器
b_iter = b.__iter__() # 直接调用 __iter__() 方法,返回迭代器
print(a_iter)
print(b_iter)
print(next(a_iter)) # 参数为迭代器(包括生成器);使用 next() 函数时会调用 __next__() 方法;生成一个值
print(b_iter.__next__()) # 直接调用 __next__() 方法,生成一个值
---------
<list_iterator object at 0x000001E4A9FEAAC8>
<tuple_iterator object at 0x000001E4AA0F6748>
1
10
装饰器
基本装饰器
Python 装饰器是一种高级语法,它可以在不修改原函数代码的情况下,增加或改变原函数的功能。装饰器本质上是一个函数,它可以接收一个函数作为参数,并返回一个新的函数。装饰器通常用于添加功能,例如日志记录、性能测试、权限检查等。
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
---------
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
在这个示例中,my_decorator
是一个装饰器函数,它接受一个函数 func
作为参数,并返回一个内部函数 wrapper
。wrapper
函数在调用原始函数之前和之后执行一些操作。通过在 say_hello
函数上面加上 @my_decorator
,我们告诉 Python 在调用 say_hello
函数之前先将其传递给 my_decorator
函数。
除了上述基本的装饰器外,还有一些高级用法,例如带参数的装饰器、类装饰器等。使用装饰器可以使代码更加简洁、可读,并且使功能模块化,提高了代码的可维护性和可重用性。
带参数装饰器
def my_decorator_with_args(arg1, arg2):
def decorator(func):
def wrapper(*args, **kwargs):
print(f"Decorator arguments: {arg1}, {arg2}")
print("Something is happening before the function is called.")
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
return decorator
@my_decorator_with_args("arg1_value", "arg2_value")
def say_hello(name):
print(f"Hello, {name}!")
say_hello("Alice")
---------
Decorator arguments: arg1_value, arg2_value
Something is happening before the function is called.
Hello, Alice!
Something is happening after the function is called.
在这个示例中,my_decorator_with_args
是一个带参数的装饰器工厂函数,它接受两个参数 arg1
和 arg2
。返回的实际装饰器函数 decorator
接受被装饰的函数 func
,并返回内部函数 wrapper
。在 wrapper
函数内部,我们可以访问装饰器的参数 arg1
和 arg2
,并在调用原始函数之前和之后执行一些操作。
当我们使用 @my_decorator_with_args("arg1_value", "arg2_value")
来装饰 say_hello
函数时,实际上是先调用 my_decorator_with_args("arg1_value", "arg2_value")
,返回一个装饰器函数,然后将 say_hello
函数传递给该装饰器函数进行装饰。
最后,当我们调用 say_hello("Alice")
时,装饰器内部的 wrapper
函数将会被执行,同时将参数传递给原始函数。
类装饰器
类装饰器是指实现了 __call__
方法的类,它可以像函数一样被调用,并且可以接受一个函数作为参数,返回一个新的函数。
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Something is happening before the function is called.")
result = self.func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
@MyDecorator
def say_hello():
print("Hello!")
say_hello()
---------
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
在这个示例中,MyDecorator
类实现了 __call__
方法,这意味着它可以像函数一样被调用。在 __init__
方法中,它接受一个函数 func
作为参数,并将其保存为实例属性。在 __call__
方法中,它执行装饰器的功能,即在调用原始函数之前和之后执行一些操作。
通过将 @MyDecorator
应用到 say_hello
函数上,我们实际上创建了一个 MyDecorator
的实例,并将 say_hello
函数传递给它的 __init__
方法。然后,当我们调用 say_hello()
时,MyDecorator
实例的 __call__
方法将被调用,从而实现了装饰器的功能。
类装饰器的优点之一是可以更方便地维护状态信息,因为可以将状态信息存储在类的实例属性中。此外,类装饰器还可以更容易地实现一些高级功能,例如接受装饰器参数、实现装饰器链等。