- 闭包
- 装饰器
- 推导式
- 列表推导式
- 字典推导式
- 集合推导式
- 元组推导式
- 迭代器
- 迭代器的使用
- 创建迭代器
- 生成器
当满足以下三个条件时就形成了闭包:
- 在一个外函数中定义了一个内函数
- 内函数里运用了外函数的临时变量
- 外函数的返回值是内函数的引用
# 闭包
def out(i): # 一个外层函数
def inner(j): # 内层函数
return i*j
return inner
使用时候先调用外层函数,返回结果就是内层函数。
func = out(3) # 此时,func 相当于是函数 inner ,只是实参 3 传入函数形参 i res = func(4) # 相当于 inner(4),并且形参已经赋值 3 print(res) # 结果 12
闭包较普通函数有以下优点:
- 间接实现在外部使用局部变量,保留下了局部变量的信息
一个函数结束的时候,会把自己的临时变量都释放给内存。通过闭包可以实现保留这些信息并继续使用。 - 构造一些结构清晰、语义简单的函数,提升部分代码和变量的复用性
# 没用闭包 def mypow(x, y): return x**y print(mypow(5, 2)) print(mypow(2, 3))
# 使用闭包 def anypow(y): def inner(x): return x**y return inner square = anypow(2) cube = anypow(3) print(square(5)) print(cube(3))装饰器
装饰器 (@) 是一种语法糖,主要用于在函数或类的基础上添加一些可重用的额外功能。从应用开发的角度来开,使用装饰器来对我们的应用代码进行功能性扩展和包装,以提高代码的可重用性和可维护性。
装饰器可以在不改变函数源代码的情况下,为函数添加新的功能。常用于日志记录、在Web应用中进行登录验证或路由注册、调用跟踪管理及参数验证、性能优化中记录任务执行时间等。
# 正常的将函数作为参数传递式写法
def print_hw(f):
print('hello world')
return f
def test():
sum_li = sum((12, 3, 4))
print(sum_li)
return sum_li
test2 = print_hw(test) # 函数引用作为实参,即 test2 = f = test
print(test2()) # 相当于 test2() = f() = test()
使用装饰器,可以改成
# 直接改装饰器
def print_hw(f):
print('hello world')
return f
@print_hw # 装饰器
def test():
sum_li = sum((12, 3, 4))
print(sum_li)
return sum_li
print(test()) # 直接调用函数,就可以先调用 print_hw
装饰器函数也可以这样写:
# 通用装饰器
def print_hw(f):
def wrapper(*args, **kwargs):
print('hello world')
return f()
return wrapper
@print_hw # 装饰器
def test():
sum_li = sum((12, 3, 4))
print(sum_li)
return sum_li
print(test()) # 直接调用
由此,可以写成:
def print_hw(func):
def print_in():
print('hello world')
return func()
return print_in
def test():
sum_li = sum((12, 3, 4))
print(sum_li)
return sum_li
test1 = print_hw(test)
print(test1())
这四个例子是等价的。由最后一个列子可以看到,形成了一个闭包。所以装饰器是传递的参数不是变量而是函数的特殊形式的闭包。装饰器的两种写法(第2/3种)的区别与优劣:
- 上一种(示例中第2种写法)结构明晰,传参明确,但是扩展的功能只能在原函数之前(因为原函数定义在 return 里)。
- 下一种(示例中第3种写法)可以在原函数执行完成后增加扩展功能,但是如果是写通用函数时,需注意传参的问题(因为实参传给了 *args 和 **kwargs )
另外装饰器可以叠加使用。叠加使用时,会将被叠加的装饰器及原函数作为统一的一个函数,被外层的装饰器调用
# 装饰器的嵌套使用
def decorator_outer(func): # 外部装饰器
def wrapper(*args, **kwargs): # 通用不定长参数
print('--外部装饰器开始--')
res = func(*args, **kwargs)
print('--外部装饰器结束--')
return res
return wrapper
def decorator_inner(func): # 内部装饰器
def wrapper(*args, **kwargs):
print('--内部装饰器开始--')
res = func(*args, **kwargs)
print('--内部装饰器结束__')
return res
return wrapper
@decorator_outer
@decorator_inner
def orig_func(a):
print(f'这是原函数,参数是{a}')
return a * 3
print(f'返回结果为:{orig_func(5)}')
# 执行结果
# --外部装饰器开始--
# --内部装饰器开始--
# 这是原函数,参数是5
# --内部装饰器结束__
# --外部装饰器结束--
# 返回结果为:15
装饰器常用于将现有的函数增加功能,而不改变函数使用方式和代码。例如增加时间记录、日志记录等。如果在原有函数内部更改,可能会有混乱,写新的函数在新函数内部调用旧函数,则需要在所有调用的地方更改调用函数名。使用装饰器则可以避免这些问题。
推导式推导式(又称为解析式)是 python 的一种特有的语法,可以从一个数据序列构建另一个新的数据序列的结构体。其主结构是一个简单的循环判断语句。Python 支持各种数据结构的推导式:
- 列表(list)推导式
- 字典(dict)推导式
- 集合(set)推导式
- 元组(tuple)推导式
列表推导式的格式为:
- [表达式 for 变量 in 列表]
- [表达式 for 变量 in 列表 if 条件]
- [表达式 for 可迭代对象 in 矩阵 for 变量 in 可迭代对象]
- [表达式 for 变量1 in 列表1 for 变量2 in 列表2]
>>> names = ['Bob','Tom','alice','Jerry','Wendy','Smith'] >>> new_names = [name.upper() for name in names if len(name)>3] >>> print(new_names) ['ALICE', 'JERRY', 'WENDY', 'SMITH']
>>> multiples = [i for i in range(30) if i % 3 == 0] >>> print(multiples) [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
li = [i+j for i in '123' for j in 'abc'] table1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] li = [i*i for row in table1 for i in row]
列表推导式的嵌套没有数量限制,但是嵌套越多复杂性越高,使用时需注意。
字典推导式字典推导式的格式为
- { 键表达式 : 值表达式 for 变量 in 集合 }
- { 键表达式 : 值表达式 for 变量 in 集合 if 条件 }
listdemo = ['Google','Runoob', 'Taobao']
# 将列表中各字符串值为键,各字符串的长度为值,组成键值对
>>> newdict = {key:len(key) for key in listdemo}
>>> newdict
{'Google': 6, 'Runoob': 6, 'Taobao': 6}
>>> dic = {x: x**2 for x in (2, 4, 6)}
>>> dic
{2: 4, 4: 16, 6: 36}
>>> type(dic)
集合推导式
集合推导式的格式为:
- { 表达式 for 元素 in 序列 }
- { 表达式 for 元素 in 序列 if 条件 }
>>> setnew = {i**2 for i in (1,2,3)}
>>> setnew
{1, 4, 9}
>>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'d', 'r'}
>>> type(a)
元组推导式
元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的元组。其格式为:
- ( 表达式 for 元素 in 序列 )
- ( 表达式 for 元素 in 序列 if 条件 )
>>> a = (x for x in range(1,10)) >>> aat 0x7faf6ee20a50> # 返回的是生成器对象 >>> tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组 (1, 2, 3, 4, 5, 6, 7, 8, 9)
需要注意的是元组推导式返回的结果是一个生成器对象,需要转换类型使用。
迭代器迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。字符串,列表或元组对象都可用于创建迭代器。
需注意的是,使用 for in 遍历对象必须是可迭代对象,也就是定义了迭代器的对象。
迭代器的使用迭代器有两个基本的方法:iter() 和 next(),分别用于创建迭代器对象和调用迭代器的下一个元素:
>>> list=[1,2,3,4] >>> it = iter(list) # 创建迭代器对象 it >>> print (next(it)) # 输出迭代器的下一个元素 1 >>> print (next(it)) 2
迭代器对象可以使用常规for语句进行遍历:
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
# 执行结果:
# 1 2 3 4
迭代器使用 next() 函数遍历,并通过 StopIteration 结束遍历。
import sys # 引入 sys 模块
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
while True:
try:
print (next(it))
except StopIteration: # 完成遍历
sys.exit()
创建迭代器
把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next() 。iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。next() 方法会返回下一个迭代器对象。
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
# 执行结果:
# 1
# 2
# 3
# 4
# 5
StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
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,end=' ')
# 执行结果:
# 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。调用一个生成器函数,返回的是一个迭代器对象。
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a # 暂停,并返回 yield 的值 a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()
# 执行结果:
# 0 1 1 2 3 5 8 13 21 34 55



