满足低调之心基础四(1)

一,装饰器

定义:本质是函数,装饰其他的函数,就是为了给其他的函数添加一些附加的功能

原则:
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).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值