迭代操作是访问集合元素的一种方式,是 Python最强大的功能之一。
迭代器是用来迭代取值的工具,是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
通过索引的方式进行迭代取值,实现简单,但仅适用于序列类型:字符串,列表,元组。
对于没有索引的字典、集合等非序列类型,必须找到一种不依赖索引来进行迭代取值的方式。
迭代器提供了一种通用的且不依赖于索引的迭代取值方式的功能。
字符串、列表、元组、字典、集合、打开的文件都是可迭代对象。
迭代器对象是内置有 iter和 next方法:
list = [1, 2, 3, 4]
# 创建迭代器对象
# it = iter(list)
it = list.__iter__()
print(type(it)) #
# 输出迭代器的下一个元素
print(next(it)) # 1
print(next(it)) # 2
print(it.__next__()) # 3
了解了迭代器,就可以不依赖索引迭代取值了。
使用while循环的实现遍历:
import sys # 引入 sys 模块
list = [1, 2, 3, 4]
it = iter(list) # 创建迭代器对象
while True:
try:
print(next(it))
except StopIteration:
# sys.exit()
break
上述 while循环可以使用 for语句遍历简写:in后可以跟任意可迭代对象
list = [1, 2, 3, 4]
it = iter(list) # 创建迭代器对象
for x in it:
print(x, end=" ")
把一个类作为一个迭代器使用时,需要在类中实现两个方法:
__iter__()
与__next__()
。
__iter__()
方法:返回一个特殊的迭代器对象, 这个迭代器对象实现了__next__()
方法并通过 StopIteration 异常标识迭代的完成。__next__()
方法:返回下一个迭代器对象。 __init__()
,它会在对象初始化的时候执行。**示例代码如下:**创建一个返回数字的迭代器,初始值为 1,逐步递增 1。
class MyClass:
def __iter__(self):
self.a = 1 # 初始化一个变量
return self
def __next__(self):
x = self.a
self.a += 1 # 每次迭代加1
return x
obj = MyClass()
myIter = iter(obj)
print(next(myIter))
print(next(myIter))
print(next(myIter))
print(next(myIter))
**StopIteration:**
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况。
在__next__()
方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
示例代码如下:在第 4次迭代后停止执行:
class MyClass2:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 4:
x = self.a
self.a += 1
return x
else:
raise StopIteration # 结束迭代
obj = MyClass2()
myIter = iter(obj)
for x in myIter:
print(x)
在 Python 中,使用了
yield 关键字
的函数被称为生成器(generator)。
生成器是一个返回迭代器的函数,只能用于迭代操作,可理解为生成器就是一个自定义迭代器。
yield 能够临时挂起当前函数,记下其上下文(包括局部变量、待决的 try catch 等),将控制权返回给函数调用者。当下一次再调用其所在生成器时,会恢复保存的上下文,继续执行剩下的语句,直到再遇到 yield 或者退出为止。
生成器有一个基本的方法:
生成器调用过程:
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值,并在下一次执行 next() 方法时从当前位置继续运行。
示例代码如下:使用 yield 实现斐波那契数列。
# 创建生成器函数实现 - 斐波那契
def fibonacci(n):
# n 代表数列的个数
a, b, counter = 0, 1, 0
while counter <= n:
yield a # 返回
a, b = b, a + b
counter += 1 # 个数加1
# f 是一个迭代器,由生成器返回生成
f = fibonacci(10)
print()
print(type(f)) #
print('=====for遍历====')
for value in f:
print(value, end=" ")
print()
f = fibonacci(10)
print('=====while遍历====')
while True:
try:
# 获取迭代器中的下一个元素值
value = next(f)
print(value, end=" ")
except StopIteration:
break
装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。即装饰器,就是可以让我们拓展一些原有函数没有的功能。
装饰器的使用符合了面向对象编程的开放封闭原则
。
假设原函数(被装饰的函数)是无参数的,对其创建一个不带参数的无参装饰器函数。
示例代码如下:
import time
def say1():
print("我是say1, Hello Python")
time.sleep(2)
def say2():
print("我是say2, Hello Python")
time.sleep(2)
# 计时装饰器
def count_time(func):
# 装饰函数名:可以自定义
def wrapper():
t1 = time.time()
func()
print("执行时间为:", time.time() - t1)
return wrapper
if __name__ == '__main__':
say1 = count_time(say1) # 因为装饰器 count_time(say1) 返回的是装饰函数对象 wrapper,
say1() # 执行 say1()就相当于执行wrapper()
print("---------")
say2 = count_time(say2)
say2()
注意:
这里的wrapper装饰函数名是可以自定义的,只要你定义的函数名,跟return的函数名是相同即可。
假设原函数(被装饰的函数)是有参数的,对其创建一个带参数的有参装饰器函数。
示例代码如下:
import time
def say1(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
def say2(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
# 计时装饰器
def count_time(func):
# 装饰的函数名:可以自定义
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
return wrapper
if __name__ == '__main__':
say1 = count_time(say1) # 因为装饰器 count_time(say1) 返回的是装饰函数对象 wrapper,
say1("say111") # 执行 say1()就相当于执行wrapper()
print("---------")
say2 = count_time(say2)
say2("say222")
注意:
上面装饰器函数wrapper的参数为 *args和 **kwargs,表示可以接受任意参数。
在 Python项目中,难免会看到@符号的代码,这个
@符号
就是装饰器的语法糖。
如果装饰器的语法糖@来实现定义装饰器,我们就可以直接调用这个原函数名。其实底层实现也是创建装饰器函数。本质上没有变化。方便了我们的使用。
(1)原函数是无参数,使用语法糖创建装饰器
import time
# 计时装饰器
def count_time(func):
# 装饰的函数名:可以自定义
def wrapper():
t1 = time.time()
func()
print("执行时间为:", time.time() - t1)
return wrapper
# count_time必须定义在前面
@count_time
def say1():
print("我是say1, Hello Python")
time.sleep(2)
@count_time
def say2():
print("我是say2, Hello Python")
time.sleep(2)
if __name__ == '__main__':
say1() # 使用用语法糖之后,就可以直接调用该函数来执行扩展的装饰功能。
print("---------")
say2()
(2)原函数是有参数,使用语法糖创建装饰器
import time
# 计时装饰器
def count_time(func):
# 装饰的函数名:可以自定义
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
return wrapper
# count_time必须定义在前面
@count_time
def say1(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
@count_time
def say2(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
if __name__ == '__main__':
say1("say111") # 使用用语法糖之后,就可以直接调用该函数来执行扩展的装饰功能。
print("---------")
say2("say222")
装饰器也是函数,既然是函数,那么就可以进行参数传递。
示例代码如下:假设我们在使用装饰器的时候,传入一些备注的msg信息。
import time
# 计时装饰器
# 参数自行定义,这里使用的是默认值参数。
def count_time_args(msg=None):
def count_time(func):
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args, **kwargs)
print(f"[{msg}]执行时间为:", time.time() - t1)
return wrapper
return count_time
@count_time_args("say1")
def say1(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
@count_time_args("say2")
def say2(name):
print("我是%s, Hello Python" % name)
time.sleep(2)
if __name__ == '__main__':
say1("say111")
print("---------")
say2("say222")
基于原来的 count_time函数外部再包一层用于接收参数的 count_time_args函数,接收回来的参数就可以直接在内部的函数里面调用了。
执行结果如下:
在 Python中,其实也可以用类来实现装饰器的功能,称之为类装饰器。
类装饰器的实现是调用了类里面的 __call__
函数。
当我们将类作为一个装饰器,工作流程:
__init__()
方法初始化类__call__()
方法调用真正的装饰方法类装饰器的写法比我们写装饰器函数更加简单。
示例代码如下:
import time
# 定义无参数的类装饰器
class MyClassCountTimeDecorator:
def __init__(self, func):
self.func = func
print("执行类的__init__方法")
def __call__(self, *args, **kwargs):
print('进入__call__函数')
t1 = time.time()
self.func(*args, **kwargs)
print("执行时间为:", time.time() - t1)
@MyClassCountTimeDecorator
def say1():
print("我是say1, Hello Python")
time.sleep(2)
def say2():
print("我是say2, Hello Python")
time.sleep(2)
if __name__ == '__main__':
say1()
print('--------------')
say2()
当装饰器有参数的时候,__init__()
函数就不能传入func(func代表要装饰的函数)了,而 func是在__call__
函数调用的时候传入的。
import time
# 定义带参数的类装饰器
class MyClassCountTimeDecorator:
def __init__(self, arg1, arg2): # init()方法里面的参数都是装饰器的参数
print('执行类Decorator的__init__()方法')
self.arg1 = arg1
self.arg2 = arg2
def __call__(self, func): # 因为装饰器带了参数,所以接收传入函数变量的位置是这里
print('执行类Decorator的__call__()方法')
def wrapper(*args): # 装饰器的函数名字可以自定义,只要跟return的函数名相同即可
print('执行wrap()')
print('装饰器参数:', self.arg1, self.arg2)
print('执行' + func.__name__ + '()')
func(*args)
print(func.__name__ + '()执行完毕')
return wrapper
@MyClassCountTimeDecorator('say', '1')
def say1(a1, a2, a3):
print("我是say1, 传入的参数:", a1, a2, a3)
time.sleep(2)
@MyClassCountTimeDecorator('say', '2')
def say2(a1, a2):
print("我是say2, 传入的参数:", a1, a2)
time.sleep(2)
if __name__ == '__main__':
say1("鲁班", "后裔", "妲己")
print('--------------')
say2("赵云", "赵子龙")
注意:
多理解无参与有参数的装饰器/类装饰器区别,加深理解代码。
一个函数可以被多个装饰器进行装饰,那么装饰器的执行顺序是怎么调用的?
答案是:在装饰器修饰完的函数,在执行的时候
先执行原函数的功能,然后再由里到外依次执行装饰器的内容。
示例代码如下:
def Decorator_1(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('我是装饰器1')
return wrapper
def Decorator_2(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('我是装饰器2')
return wrapper
def Decorator_3(func):
def wrapper(*args, **kwargs):
func(*args, **kwargs)
print('我是装饰器3')
return wrapper
@Decorator_1
@Decorator_2
@Decorator_3
def say1():
print("我是say1, Hello Python")
if __name__ == '__main__':
say1()
– 求知若饥,虚心若愚。