一,装饰器
定义:本质是函数,装饰其他的函数,就是为了给其他的函数添加一些附加的功能
原则:
1,不能改变被装饰函数的源代码
2,不能改变被装饰函数的调用方式
这两句话或许你会看的云山雾绕,但不要紧,咱们在代码中去进一步理解
1,函数调用
首先咱有两个函数,那么该如何去调用呢?有哪些可行的调用方式呢?
①
def bar():
print("in the bar!")
# foo() # 写在这里就是错误的调用方式
def foo():
print("in the foo!")
bar()
foo()
②
def bar():
print("in the bar!")
foo()
def foo():
print("in the foo!")
# foo() # 写在这里只能调用bar
bar()
③
def foo():
print("in the foo")
def bar():
print("in the bar")
bar()
# foo() # 写在这里都不会被调用
foo()
上面①②③展示了一些正确和不规范的调用方式,但小编还是建议一般这样写
def bar():
print("in the bar!")
# bar() ①
def foo():
print("in the foo!")
# foo() ①
bar() ②
foo() ②
是不是感觉这两种写法清晰明了很多
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
in the bar!
in the foo!
Process finished with exit code 0
2,匿名函数
calc = lambda x: x ** 2 # 其实calc和calc1就是函数
calc1 = lambda x, y: x + y
print(calc(3))
print(calc1(3, 4))
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
9
7
Process finished with exit code 0
我们阔以说 函数即“变量”
3,高阶函数
①基于装饰器的需要,前面也已提到过,咱们再看一下
def bar():
print("in the bar!")
def test(func):
print(func)
func()
test(bar)
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
<function bar at 0x0000021ADA091E50> # 这里是bar的内存地址
in the bar!
Process finished with exit code 0
总结:
①把一个函数名当做实参传给另一个函数(在不修改被装饰函数源代码的情况下为其添加功能)
②返回值中包含函数名(不修改函数的调用方式)
②咱们把①中代码略微改一二
import time
def bar(): # 被装饰函数
time.sleep(1)
print("in the bar!")
def test(func): # 装饰函数
start_time = time.time()
func()
stop_time = time.time()
print("the func run time is %s" % (stop_time - start_time))
test(bar)
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
in the bar!
the func run time is 1.0111019611358643
Process finished with exit code 0
有没有感觉”貌似“像装饰器了,但当你再看开始的定义和原则时,发现又有一点点的不足,怎么改变了被装饰函数的调用方式(即不能再bar()这样调用了)
③咱们再把②修改一下
import time
def bar():
time.sleep(1)
print("in the bar!")
def test1(func):
print("Modifying bar function!")
return func
bar = test1(bar)
bar()
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
Modifying bar function!
in the bar!
Process finished with exit code 0
这下就没有修改被装饰函数的调用方式了
4,嵌套函数
def foo():
print("in the foo!")
def bar():
print("in the bar!")
bar()
foo()
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
in the foo!
in the bar!
Process finished with exit code 0
Looking at one
# 局部作用域和全局作用域的访问顺序
x = 1
def grandpa():
x = 2
def dad():
x = 3
def son():
x = 4
print(x)
son()
dad()
grandpa()
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
4
Process finished with exit code 0
5,装饰器(=嵌套函数+高阶函数)
哎呀,绕了一大圈,终于阔以回归主题了(激动的心,颤抖的手,哈哈哈)
老规矩,先上代码
import time
def timer(func): # 嵌套函数,即装饰函数
def deco():
start_time = time.time()
func()
stop_time = time.time()
print("the func run time %s" % (stop_time - start_time))
return deco
def test1(): # 被装饰函数1
time.sleep(1)
print("in the test1!")
def test2(): # 被装饰函数2
time.sleep(1)
print("in the test2!")
test1 = timer(test1)
test1()
test2 = timer(test2)
test2()
运行结果为
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
in the test1!
the func run time 1.0118858814239502
in the test2!
the func run time 1.0042293071746826
Process finished with exit code 0
look,是不是满足了装饰器的定义和原则,不过这个…,总觉得调用方式看着有点别扭
咱们换个稍微上档次的方式
import time
def timer(func): # 嵌套函数
def deco():
start_time = time.time()
func()
stop_time = time.time()
print("the func run time %s" % (stop_time - start_time))
return deco
@timer # 语法糖,相当于test1 = timer(test1)
def test1():
time.sleep(1)
print("in the test1!")
@timer # 语法糖,相当于test2 = timer(test2)
def test2():
time.sleep(1)
print("in the test2!")
test1()
test2()
当然运行结果和上面的一样。(@timer被叫做语法糖,具体为什么这样叫咱也不知道,就是在要装饰的函数上面写上@+装饰器的函数名,即上面的@timer)
咱们再深入一下,假如还有个被装饰函数test3里面带参数呢?那该怎么玩
import time
def timer(func): # 嵌套函数
def deco(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
stop_time = time.time()
print("the func run time %s" % (stop_time - start_time))
return deco
@timer # 语法糖,相当于test1 = timer(test1)
def test1():
time.sleep(1)
print("in the test1!")
@timer # 语法糖,相当于test3 = timer(test3)
def test3(name, age): # test3(name)=deco(name)
time.sleep(1)
print("test3:", name, age)
test1()
test3("hpl", "18 years old")
运行结果
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/first.py
in the test1!
the func run time 1.0006630420684814
test3: hpl 18 years old
the func run time 1.0067644119262695
Process finished with exit code 0
这儿传参数确实很棒!
最后,Decorator top Edition
# 装饰器-highest
user, pwd = "hpl", "123"
def auth(auth_type):
print("auth func:", auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
username = input("username:").strip()
password = input("password:").strip()
if user == username and pwd == password:
print("\033[32;1mUser has passed authentication!\033[0m")
res = func(*args, **kwargs)
return res
else:
exit("\033[31;1mInvalid username and password!!!\033[0m")
return wrapper
return outer_wrapper
def index():
print("welcome to index page!")
@auth(auth_type="local")
def home():
print("welcome to home page!")
return "from home"
@auth(auth_type="ldap")
def bbs():
print("welcome to bbs page!")
index()
home()
bbs()
运行结果
嗯,这个模拟网页登录确实还不错!!!
二,迭代器与生成器
1,列表生成式(有b格)
# 列表生成式
a = [i*4 for i in range(10)]
print(a)
print(a[5]) # 获取第五个元素的值
当你第一次看到,我擦,还阔以这样玩循环(哈哈)
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
[0, 4, 8, 12, 16, 20, 24, 28, 32, 36]
20
Process finished with exit code 0
当然一般也都会这样写
a = []
for i in range(10):
a.append(i*4)
print(a)
运行结果和上面的一样
果然第一种(列表生成式)更上档次一点
2,生成器
①
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,假如要创建一个包含一亿个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
a = (i*4 for i in range(10))
print(a)
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
<generator object <genexpr> at 0x0000019804BC40B0>
Process finished with exit code 0
那我要想获取其中某个位置元素的值可以吗?
a = (i*4 for i in range(10))
print(a)
print(a[5])
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
<generator object <genexpr> at 0x0000023FE2E140B0>
Traceback (most recent call last):
File "G:/Project1/self_taught/fourth_week/iterator.py", line 18, in <module>
print(a[5])
TypeError: 'generator' object is not subscriptable
Process finished with exit code 1
很好,报错了!!!
这说明什么呢?生成器只有在你调用时才能生成相应的数据,而且只记录当前位置
那么问题来了该如何取其中的值呢?
a = (i*4 for i in range(100))
# print(a)
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
print(a.__next__())
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
0
4
8
12
16
Process finished with exit code 0
so,就一个个慢慢的往下取吧…
②斐波那契数列
斐波那契数列(Fibonacci sequence),指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……从第3项开始,每一项都等于前两项之和。
# 斐波那契数列
def fib(max1):
n, a, b = 0, 0, 1
while n < max1:
print(a)
a, b = b, a + b
n += 1
fib(10)
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
0
1
1
2
3
5
8
13
21
34
Process finished with exit code 0
仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第三个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(a)改为yield a就可以了:
def fib(max1):
n, a, b = 0, 0, 1
while n < max1:
# print(a)
yield a
a, b = b, a + b
n += 1
f = print(fib(10))
简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不再是一个普通函数,Python 解释器会将其视为一个 generator
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
<generator object fib at 0x000001D6ED1D60B0>
Process finished with exit code 0
这就是一个生成器了,那么要访问里面的内容呢
def fib(max1):
n, a, b = 0, 0, 1
while n < max1:
# print(a)
yield a
a, b = b, a + b
n += 1
f = fib(10)
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
0
1
1
2
3
Process finished with exit code 0
害,这样访问说实话有点累,挺费力的,咱们直接用个循环来实现
# 斐波那契数列
def fib(max1):
n, a, b = 0, 0, 1
while n < max1:
# print(a)
yield a
a, b = b, a + b
n += 1
f = fib(10)
for i in f:
print(i)
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
0
1
1
2
3
5
8
13
21
34
Process finished with exit code 0
不错,这样就方便那多了!
③生成器并行
# 生成器并行
import time
def consumer(name):
print("\033[31;1m%s\033[0m ready to eat dumplings!" % name)
while True:
dumplings = yield
print("Dumpling \033[34;1m%s\033[0m is here,"
"it's eaten by \033[31;1m%s\033[0m!" % (dumplings, name))
a = consumer("pig")
a.__next__()
b = "Pork stuffing"
a.send(b)
# send()的作用是使xx赋值为发送的值,然后让生成器执行到下个yield
def producer(name):
c = consumer("duck")
c1 = consumer("chicken")
c.__next__()
c1.__next__()
print("\033[36;1m%s\033[0m going to start making dumplings!" % name)
for i in range(3):
time.sleep(1)
print("Made 20 dumplings!")
c.send(i)
c1.send(i)
producer("dog")
变色:(\033[31;1m这里写的是要变色的内容\033[0m)其中前面的31代表红色,32是绿色,33是黄色,依次后面还有许多
运行结果为
这里将生成器的并行体现的淋漓尽致
3,迭代器
我们已经知道,可以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象: Iterable。
可以使用isinstance()判断一个对象是否是Iterable对象:
# isinstance() 函数来判断一个对象是否是一个已知的类型
from collections.abc import Iterable
print(isinstance([], Iterable))
print(isinstance({}, Iterable))
print(isinstance("abc", Iterable))
print(isinstance(100, Iterable))
print(isinstance((i for i in range(5)), Iterable))
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
True
True
True
False
True
Process finished with exit code 0
发现这几个里面只有数字是不可迭代的
而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
from collections.abc import Iterator
print(isinstance([], Iterator))
print(isinstance({}, Iterator))
print(isinstance("abc", Iterator))
print(isinstance(100, Iterator))
print(isinstance((i for i in range(5)), Iterator))
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
False
False
False
False
True
Process finished with exit code 0
list,dict,str虽然都是Iterable(可迭代的),但都不是迭代器(Iterator)
当然把list,dict,dir等Iterable变成Iterator阔以使用iter()函数
from collections.abc import Iterator
print(isinstance(iter([]), Iterator))
print(isinstance(iter({}), Iterator))
print(isinstance(iter("abc"), Iterator))
G:\Python38\python.exe G:/Project1/self_taught/fourth_week/iterator.py
True
True
True
Process finished with exit code 0
当然也阔以使用dir()
a = [1, 2, 3]
print(type(a))
print(dir(a)) # 查看所有可以调用的方法,发现是没有next()
小结:
凡是可用于for()循环的对象都是Iterable类型,凡是作用于next()函数的对象都是Iterator类型
续 链接: 基础四(2).