Python 装饰器、生成器、迭代器、内置函数
1. 装饰器
定义: 本质上是个函数,功能是装饰其他函数—就是为其他函数添加附加功能
装饰器原则:
- 不能修改被装饰函数的源代码;
- 不能修改被装饰函数的调用方式;
实现装饰器知识储备:
- 函数即“变量”
- 定义一个函数相当于把函数体赋值给了函数名
1.1 函数调用顺序
其他高级语言类似,python不允许在函数未声明之前,对其进行引用或者调用
def foo():
print('in the foo')
bar() # 未声明bar()函数,会报错
foo()
# 报错:
in the foo
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
foo()
File "<pyshell#12>", line 3, in foo
bar()
NameError: global name 'bar' is not defined
# 例子2
def foo():
print('foo')
bar() # 先调用bar()函数也会抱错
foo()
def bar()
print('bar')
# 报错:NameError: global name 'bar' is not defined
正确示范:(注意,python为解释执行,函数foo在调用前已经声明了bar和foo,所以bar和foo无顺序之分)
def foo()
print('in the foo')
bar()
def bar():
print('in the bar')
foo()
def bar():
print('in the bar')
def foo():
print('in the foo')
bar()
foo()
1.2 高阶函数
满足下列条件之一就可成函数为高阶函数
- 某一函数当做参数传入另一个函数中
- 函数的返回值包含n个函数,n>0
高阶函数示范:
def bar():
print('in the bar')
def foo(func): # func = bar
res=func() # func(),执行bar()函数
return res
foo(bar) # 把bar()函数内存地址传进去
def bar():
print('in the bar')
def foo(func):
return func
print('function body is %s' %(foo(bar)))
foo(bar)()
#foo(bar)() 等同于bar=foo(bar)然后bar()
bar = foo(bar)
bar()
1.3 内嵌函数和变量作用域
定义:在一个函数体内创建另一个函数,这种函数就叫内嵌函数
嵌套函数:
def foo():
def bar():
print('in the bar')
bar()
foo() # 结果:in the bar
局部作用域和全局做用域的访问顺序
x = 0
def grandpa():
def dad():
x = 2
def son():
x=3
print(x) # 就近取值,最近的是 x = 3
son()
dad()
grandpa() # 结果:3
1.4 高阶函数+内嵌函数=》装饰器
函数参数固定
def decorartor(func):
def wrapper(n):
print('starting')
func(n)
print('stopping')
return wrapper
def test(n):
print('in the test arg is %s' %n)
decorartor(test)('alex')
函数参数不固定
def decorartor(func):
def wrapper(*args,**kwargs):
print('starting')
func(*args,**kwargs)
print('stopping')
return wrapper
def test(n,x=1):
print('in the test arg is %s' %n)
decorartor(test)('alex',x=2222)
无参装饰器
import time
def decorator(func):
def wrapper(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print("%s" %(stop_time-start_time))
return wrapper
@decorator # @decorator 等于 test = decorator(test)
def test(list_test):
for i in list_test:
time.sleep(0.1)
print('-'*20,i)
#decorator(test)(range(10))
test(range(10))
有参装饰器
import time
def timer(timeout=0):
def decorator(func):
def wrapper(*args,**kwargs):
start=time.time()
func(*args,**kwargs)
stop=time.time()
print('run time is %s ' %(stop-start))
print(timeout)
return wrapper
return decorator
@timer(2)
def test(list_test):
for i in list_test:
time.sleep(0.1)
print ('-'*20,i)
#timer(timeout=10)(test)(range(10))
test(range(10))
终极装饰器
user,passwd = 'alex','abc123'
def auth(auth_type):
print('auth func:',auth_type)
def outer_wrapper(func):
def wrapper(*args,**kwargs):
print("wrapper func args:",*args,**kwargs)
if auth_type=="local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
res = func(*args,**kwargs)
print("---after authentication")
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print('搞毛线ldap,不会......')
return wrapper
return outer_wrapper
def index():
print("welcome to index page")
@auth(auth_type="local") # home = auth(auth_type="local")(home)
def home():
print("welcome to home page")
return "from home"
@auth(auth_type="ldap")
def bbs():
print("welcome to bbs page")
index()
print(home()) # home 就等于 wrapper()
bbs()
小知识: functools 模块
def auth(func):
def inner(*args, **kwargs):
"""------- inner"""
return func(*args, **kwargs)
return inner
@auth
def handler():
"""------- handler"""
pass
handler()
print(handler.__name__) # 打印函数名称:inner
print(handler.__doc__) # 打印注释内容:------- inner
import functools
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
"""------- inner"""
return func(*args, **kwargs)
return inner
@auth
def handler():
"""------- handler"""
pass
handler()
# 添加 @functools.wraps(func) 后虽然执行inner函数,但是会把原函数赋值给__name__和__doc__
print(handler.__name__) # 打印函数名称:handler
print(handler.__doc__) # 打印注释内容:------- handler
2. 生成器
2.1 yield
- 通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
- 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
- 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
>>>l = [x*x for x in range(10)]
>>>l
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>>g = (x*x for x in range(10))
>>>g
<generator object <genexpr> at 0x1022ef630>
创建L和g的区别仅在于最外层的[]和(),L是一个list,而g是一个generator。
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。
def func():
print(111)
yield 1
print(222)
yield 2
print(333)
yield 3
print(444)
data = func()
for item in data:
print(item)
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, ...
def fib(max):
n,a,b=0,0,1
while n < max:
print(b)
a,b=b,a+b
n = n+1
return 'done'
上面的函数可以输出斐波那契数列的前N个数:
>>>fib(10)
1
1
2
3
5
8
13
21
34
55
done
生成器的特点:
- 生成器只有在调用时才会生成相应的数据;
- 只记录当前位置;
- 只有一个__next__()方法;
send()和next()区别:
- next()不能接收返回值,如:yield
- send()可以接收返回值,如:v = yield 1
# next()
def func():
print(111)
k1 = yield 1
print("k1值:",k1) # 不能接收返回值,k1值是空值
print(222)
yield 2
print(333)
yield 3
print(444)
data = func()
v1 = next(data)
print("v1值:",v1)
v2 = next(data)
print("v2值:",v2)
"""
# 结果:
111
v1值: 1
k1值: None
222
v2值: 2
"""
# send()
def func():
print(111)
k1 = yield 1
print("k1值:",k1) # 可以接收返回值,1212
print(222)
yield 2
print(333)
yield 3
print(444)
data = func()
v1 = data.send(None) # 第一个send传值必须是None
print("v1值:",v1)
v2 = data.send(1212) # 先把值1212 ,赋值给 yield 1在继续往下执行
print("v2值:",v2)
"""
# 结果:
111
v1值: 1
k1值: 1212
222
v2值: 2
"""
还可通过yield实现在单线程的情况下实现并发运算的效果:
import time
def consumer(name):
print("%s 准备吃包子!" %name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
c = consumer("ChenRonghua")
c.__next__()
def produceer(name):
c = consumer("a")
c2 = consumer("b")
c.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了1个包子,分两半!")
c.send(i)
c2.send(i)
produceer("alex")
2.2 yield from
yield 和 yield from 区别:
- yield ,当yield 后面跟一个函数时,会返回函数的对象
- yield from ,当yield from 后面跟一个函数时,会执行该函数
def func():
yield 3
yield 3
def func2():
yield 1
yield func() # 返回对象
yield 2
yield from func() # 执行func函数
yield 4
for i in func2():
print(i)
3. 迭代器
我们已经知道,可以直接作用于for循环的数据类型有以下几种:
- 一类是集合数据类型,如list、tuple、dict、set、str等;
- 一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:
>>>from collections import Iterable
>>>isinstance([],Iterable)
True
>>>isinstance({},Iterable)
True
>>>isinstance('abc',Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
Python的for循环本质上就是通过不断调用next()函数实现的,例如:
for x in [1,2,3,4,5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值: next(it) 或者 it.__next__()
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
例子:
from collections import Iterable
# 创建一个迭代器
setvar = {"aa","bb","cc","dd"}
it = iter(setvar)
print(it)
# 判断是否是迭代器
print(dir(it))
res = "__iter__" in dir(it) and "__next__" in dir(it)
print(res)
# 方法二:导入 Iterable 包。
isinst = isinstance(it,Iterable)
print(isinst)
# 调用一个迭代器
print(next(it))
print(it.__next__())
print(next(it))
4. 内置函数

内置参数详解:https://docs.python.org/3/library/functions.html?highlight=built#ascii
# abs,绝对值
print(abs(-10)) # 结果:10
# pow,指数
print(pow(2,5)) # 2的5次方 2**5,结果:32
# sum,求和
v1 = sum([-11, 22, 33, 44, 55]) # 可以被迭代-for循环
print(v1) # 结果:143
# divmod,求商和余数
v1, v2 = divmod(9, 2)
print(v1, v2) # 结果:4 1
# round,小数点后n位(四舍五入)
v1 = round(4.11786, 2)
print(v1) # 结果:4.12
# min,最小值
v1 = min(11, 2, 3, 4, 5, 56)
print(v1) # 2
v2 = min([11, 22, 33, 44, 55]) # 迭代的类型(for循环)
print(v2)
v3 = min([-11, 2, 33, 44, 55], key=lambda x: abs(x))
print(v3) # 2
# max,最大值
v1 = max(11, 2, 3, 4, 5, 56)
print(v1)
v2 = max([11, 22, 33, 44, 55])
print(v2)
v3 = max([-11, 22, 33, 44, 55], key=lambda x: x * 10)
print(v3) # 55
# all,是否全部为True
v1 = all( [11,22,44,""] ) # False
# any,是否存在True
v2 = any([11,22,44,""]) # True
# 进制
bin(10) # 十进制转二进制
oct(10) # 十进制转八进制
hex(10) # 十进制转十六进制
# ord,获取字符对应的unicode码点(十进制)
v1 = ord("a")
print(v1, hex(v1)) # 97 0x61
# chr,根据码点(十进制)获取对应字符
v1 = chr(97)
print(v1) # 97
# 数据类型
# int()整型、float()浮点、str()字符串、unicode()编码、bytes()字节,utf-8、gbk编码
# bool()布尔、list()列表、dict()字典、tuple()元组、set()集合
# len()长度
v = [1,2,3]
print(len(v)) # 3
# range(start,end,step) 生成start到end的数字,步长为step,供for循环使用
num = [1,2,3,4]
for i in range(len(num)):
print(num[i]," ",end="")
print()
# type(),获取数据类型
print(type("a"),type(1),type([]))
# enumerate() 将可遍历的数据对象(如列表,元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在for循环中。
str1 = "abcd"
for k,i in enumerate(str1):
print(k,i)
# del 删除
delNum = [11,22,33,44]
del delNum
# id(),内存地址
print(id(11))
# hash
print(hash("武沛齐"))
# help,帮助信息
# pycharm,不用
# 终端,使用
# zip
v1 = [11, 22, 33, 44, 55, 66]
v2 = [55, 66, 77, 88]
v3 = [10, 20, 30, 40, 50]
result = zip(v1, v2, v3)
for item in result:
print(item)
# callable,是否可执行,后面是否可以加括号。
v1 = "jack"
v2 = lambda x: x
def v3():
pass
print( callable(v1) ) # False
print(callable(v2))
print(callable(v3))
# sorted,排序
v1 = sorted([11,22,33,44,55])
# 例子
info = {
"wupeiqi": {
'id': 10,
'age': 119
},
"root": {
'id': 20,
'age': 29
},
"seven": {
'id': 9,
'age': 9
},
"admin": {
'id': 11,
'age': 139
},
}
result = sorted(info.items(), key=lambda x: x[1]['id'])
print(result)
# 例子
data_list = [
'1-5 编译器和解释器.mp4',
'1-17 今日作业.mp4',
'1-9 Python解释器种类.mp4',
'1-16 今日总结.mp4',
'1-2 课堂笔记的创建.mp4',
'1-15 Pycharm使用和破解(win系统).mp4',
'1-12 python解释器的安装(mac系统).mp4',
'1-13 python解释器的安装(win系统).mp4',
'1-8 Python介绍.mp4', '1-7 编程语言的分类.mp4',
'1-3 常见计算机基本概念.mp4',
'1-14 Pycharm使用和破解(mac系统).mp4',
'1-10 CPython解释器版本.mp4',
'1-1 今日概要.mp4',
'1-6 学习编程本质上的三件事.mp4',
'1-18 作业答案和讲解.mp4',
'1-4 编程语言.mp4',
'1-11 环境搭建说明.mp4'
]
result = sorted(data_list, key=lambda x: int(x.split(' ')[0].split("-")[-1]) )
print(result)
# map
# map(func,lst) 将传入的函数变量func作用到st变量的每个元素中,并将结果组成新的列表(Python2)/返回迭代器(Python3)
list1 = [1, 2, 3, 4]
def func(x):
return x ** 2
result = map(func, list1)
print(result)
print(list(result))
# 二
lst = ["a", "b", "c"]
def func(n):
dic = {91: "a", 98: "b", 99: "c"}
new_dic = {}
print(n)
for k, v in dic.items():
new_dic[v] = k
print(new_dic)
return new_dic[n]
it = map(func, lst)
print(list(it)) # 强转列表并打印
# reduce()
# reduce(func,lst) 其中func必须有两个参数,每次func计算的结果继续和序列的下一个元素做累积计算。
# reduce() 传入的参数func必须接收2个参数。
# 实例:计算list1序列中各个数字的累加和。
import functools
list1 = [1, 2, 3, 4]
def func(a, b):
return a + b
result = functools.reduce(func, list1)
print(result)
# filter()
# fiter(func,lst) 函数用于过滤序列,过滤不符合条件的元素,返回一个 filter 对象。如果要转换为列表,可以使用 list() 来转换。
list1 = [1,2,3,4]
def func(x):
return x % 2 == 0
result = filter(func,list1)
print(result)
print(list(result))

浙公网安备 33010602011771号