Python Learning, Day4

1. 装饰器(decorator)的基础

1.1 基本概念

概念:

  1. 本质:函数
  2. 功能:装饰其他函数(为其他函数添加附加功能)

原则:

  1. 不能修改被装饰函数的源代码
  2. 不能修改被装饰函数的调用方式
  3. 不能修改被装饰函数的返回值

需要的知识储备:

  1. 函数即“变量”
  2. 高阶函数
  3. 嵌套函数
  4. 闭包

1.2 函数调用顺序:函数即"变量"

函数调用顺序注意两点:

  1. 函数没有声明之前,不能调用
  2. 声明的多个函数没有顺序之分

函数即“变量”:

  • 变量名是一个盒子(数、字符串……)的标签,函数是一个“函数体”的钥匙(地址)。有了标签钥匙,就可以取内容。
#函数调用顺序
def bar():
    print('in the bar')
def foo():
    print('in the foo')
    bar()
foo()    #上面定义顺序无所谓,python解释器一行行解释了,只要存在,就能用
 
 

三点注意:

  • 理解钥匙
  • 内存回收机制:钥匙被清空了,就不在了
  • python解释性:python解释器一行行解释,找到各个钥匙对应的内容

 

1.3 高阶函数

定义:

  • 把一个函数名当作实参传给另外一个函数   -->(在不修改被装饰函数条件下,为其添加功能)
  • 返回值中包含函数名  -->(不修改函数的调用方式)

 

# 高阶函数示范(不修改被装饰函数)
# 把钥匙inner交给函数outer
def inner():
    print ('in the bar')
def outer(func):
    res = func()  # func()等同于inner(),inner没有返回值,因此res=None
    print('收到的钥匙地址:%s' %func)   # 打印的是inner钥匙(内存地址)
    func()     # 钥匙():执行inner函数内容
    return func(), res, func  #理解这个地方很关键:钥匙房间返回值、钥匙地址
c = outer(inner)
print(c)  #(None, None, 0x000002293883C1E0)

 

#对@的理解:inner = outer(inner) 也是高阶函数的牛逼之处
#尝试1,不修改调用方式:失败(返回的钥匙还是收到的钥匙)
def inner(): print('in the inner') def outer(func): func() #inner内容 print('in the outer') #新加内容 return func #其实返回的钥匙还是收到的钥匙,只能后面用嵌套 outer(inner) print('------') inner = outer(inner) #形式对了,后面加入嵌套后就能改变钥匙

 

#尝试2,不修改调用方式:失败(返回的outer是有参数的)
def inner():
    print('in the inner')

def outer(func):
    func()  
    print('in the outer')  
    print('inner的地址:%s' % inner)
    print('outer的地址:%s' % outer)
    return outer  #修改返回的内容为outer
inner = outer(inner)  #功能多了,但调用方式还是不对,outer有参数
print('------')
print(inner)

高阶函数的经典之处在于:传进去的是inner钥匙,传出来的是outer钥匙,再inner = outer(inner)一下,虽然名字还叫inner,但此时inner变为了outer房间的钥匙。

不过还有一个问题,outer开门需要参数,inner不需要,因此调用方式还是不一样。(新功能已经具备)

因此,我需要返回一个具有新功能,又没有参数的钥匙。因为要传原来的钥匙,因此想到函数的嵌套,2层函数,外层接受inner钥匙,内层定义一个与原函数调用方式相同的用来返回的函数。

 

1.4 嵌套函数

定义:在一个函数体内创建另外一个函数

#函数嵌套示范
x=0
def grandpa():
    # x=1
    def dad():
        x=2
        def son():
            x=3
            print (x)
        #son()
    dad()
grandpa()
#结果:无,因为x=3, print(x)未被执行

 

1.5 闭包

定义:

  • 外函数中定义了一个内函数
  • 内函数里运用了外函数的临时变量
  • 外函数的返回值是内函数的引用(python中一切皆对象,有引用就好说,引用可理解为钥匙)

特点:

  • 内函数里不能修改外函数的内容,除非把闭包变量修改成可变数据类型
  • 外函数的临时变量不会随着外函数的结束而消失,而是保留给内函数

回顾:

  • 函数名:inner---->函数名字,存了函数所在位置的引用
  • 函数名():inner()---->函数名后紧跟一对括号,表示调用这个函数

 

#闭包
def outer(a):
    b = 10
    def inner():
        print(a+b)
    return inner

demo1 = outer(5)      #demo存的是outer的返回值,其实就是inner,
demo2 = outer(7)     #每次调用外函数,都返回不同的实例对向的引用,虽然功能一样,但不是同一个函数对向
print(demo1)
print(demo2)

 

2. 装饰器(decorator)

装饰器:高阶函数+嵌套函数+闭包

2.1 无参装饰器

概念:

#无参装饰器:记录函数inner运行时间
import time

#outer函数作为装饰器
def outer(para):
    def deco():
        """修饰函数: 拿到钥匙, 开门, 且记录运行时间"""
        start_time = time.time()
        res = para()  #保证被装饰函数原功能,以及记录返回值
        stop_time = time.time()
        print("新功能--->运行时间为:%s" % (stop_time - start_time))
        return res   #保证被装饰函数返回值不变
    return deco


#@outer  等同于下面的inner = outer(inner) ,必须写在被装饰函数上部
#原函数(内)
def inner():
    time.sleep(2)
    print("我是原函数,不能动")

#执行验证
inner = outer(inner)   #一个问题,为什么嵌套之后就不输出了??怎么做到的??
print("------")
inner()

 

2.2 有参装饰器

很简单,加入非固定参数定义即可

#有参装饰器
'''
以下为装饰器
'''
import time
def timer(func):
    def deco(*args, **kwargs):   #非固定参数
        start_time = time.time()
        res = func(*args, **kwargs)  #非固定参数
        stop_time = time.time()
        print("the func run time is %s" % (stop_time - start_time))
        return res
    return deco
'''
原代码
'''
@timer #bar1 = timer(bar1)
def bar1():
    time.sleep(2)
    print('in the bar')
@timer
def bar2(name, age):
    time.sleep(3)
    print('bar2:', name, age)

'''
按原来方式运行
'''
bar1()
bar2('alex', 22)

 

2.3 终极版:装饰器本身带参数

给下列程序添加功能,要求如下:

  • 进入home通过本地文件认证
  • 进入bbs通过远程ldap认证
  • 仅用一个装饰器

有点难:实在理解不了没关系,2.2就已经能解决90%的问题了。

#源代码
def index():#首页
    print("welcome to index page")

def home():
    print("welcome to home page")
    return "from home"

def bbs():
    print("welcome to bbs page")

#运行
index()
home()
bbs()

首先搭建框架:

本质:装饰器带个参数,多加了一层,整体下移一层

#装饰器框架(不能运行)
user, passwd = 'Zhu Yijun', 'abc123'


def auth(auth_type):  # authentication
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            if auth_type == 'local':
                """本地文件验证"""
                res = func(*args, **kwargs)
                return res  #成功才返回

            elif auth_type == 'ldap':
                """远程ldap验证"""

        return wrapper

    return outer_wrapper

@auth(auth_type='local')  # 本地文件验证
@auth(auth_type='ldap')  # 远程ldap验证
home()

整体代码如下:

#完整代码
#其实就是整体往下移了一层
import time
user, passwd = 'zhuyijun', 'abc123'

def auth(auth_type):  #authentication
    def outer_wrapper(func):
        def wrapper(*args, **kwargs):
            if auth_type == 'local':
                """本地文件验证"""
                username = input("Username:").strip()
                password = input("Password:").strip()
                if user == username and passwd == password:
                    print("\033[32;1mUser has passed authentication\033[0m")
                    res = func(*args, **kwargs)
                    print("------after authentication-----")
                    return res  # 保证返回值不变
                else:
                    exit("\033[31;1mInvalid username or password!\033[0m")
            elif auth_type == 'ldap':
                """远程ldap验证"""
                print("搞毛线ldap,不会。。。。")
        return wrapper
    return outer_wrapper


#源代码
def index():#首页
    print("welcome to index page")
@auth(auth_type='local')   #本地文件验证   #home = wrapper()
def home():
    print("welcome to home page")
    return "from home"
@auth(auth_type='ldap')    #远程ldap验证
def bbs():
    print("welcome to bbs page")

#运行
index()
home()
bbs()

 

3. 生成器

 

 

列表生成:

#列表生成式
#生成0-9的平方的列表

#平方函数
def square(a):
    sum = a**2
    return sum

a = [square(i) for i in range(10)]  #列表生成式:使代码更简洁
print(a)

 

生成器(generator):不用像列表生成式那样先全部把数据准备好,而是一边循环一边计算(用到才存在,不用就不存在),这样可以节约大量的内存。尤其是上百万元素时候,可以节省很多时间

#简单生成器
a = (i**2 for i in range(10))  #[]变成()即可

特点:

  • 你调用它,它才生成;你不访问它,它根本不存在。换而言之,仅仅给你准备好了算法而已
  • 不能用切片去取,只能循环先生成

取值:next()方法

  • 可以通过next()函数获得generator的下一个返回值,只能一步一步走
  • 不知道前面,也不知道后面,只保留一个值,当前值
#最常用的取值方法是for循环,而不是next
g = (x**2 for x in range(10))
for n in g:
    print(n)

 

#斐波拉契数列函数
#假如需要执行10分钟
def fib(max): n, a, b = 0, 0, 1 while n < max: print(b) a, b = b, a + b #a, b = c, d:表示a=c,b=d,好处,c和d都是变化之前的 #不是下面格式 #a = b #b = a + b #a已经变了 n = n + 1 return 'done' fib(100)

 

#函数变成生成器
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b   #这个函数就不再是一个普通函数,而是一个generator
        a, b = b, a + b
        n = n + 1
    #return 'done'   #有没有无所谓

f = fib(10)
print(f.__next__())
print("可以干点别的")
print(f.__next__())
print("不用等待函数结束")
print(f.__next__())

yield b:

  • 返回当前值
  • 停在这,保存函数中断状态,想回来就回来

 

什么时候停:用到捕获错误

  • 直接复制:print(f.__next__()),超过会报错
  • 用for循环:虽然会停,但没有返回值
  • 因此用while+捕获错误
#想要拿到返回值,必须捕获StopIteration错误
f = fib(5)
while True:
    try:
        x = next(f)
        print('f:', x)
    except StopIteration as e: #一但出了"StopIteration"错误,就执行下面语句
        print('Generator return value:', e.value)   #上面return的作用在这
        break

 

send和next区别:

  • c.send(b1):唤醒且传值,,唤醒就是指调用
  • c.__next__():仅唤醒

吃包子程序:

  • 不同角色之间来回切换,感觉就是并行的
  • 看起来是单线程下的并行效果,其实是协程

 

#包子程序
import time
#消费者
def consumer(name):
    print("%s 准备吃包子啦!" %name)
    while True:
       baozi = yield  #保存当前状态,返回

       print("包子[%s]来了,被[%s]吃了!" %(baozi,name))



'''
#流程试验:
c = consumer("ZhuYijun")  #仅表示c只是一个生成器,已经不是函数了,不会打印:print("%s 准备吃包子啦!" %name)
c.__next__()    #next一下才会从头往下走,
b1 = "韭菜馅"
c.send(b1)   #send,把包子传进去了,被yield接收到了,赋给了包子
#c.__next__()  #单纯调用,不会传值
'''


#生产者
def producer(name):
    #先生成2个消费者
    c = consumer('A')
    c2 = consumer('B')
    #初始化,准备吃包子
    c.__next__()
    c2.__next__()
    print("我开始准备做包子啦!")
    #循环10次,每次生成1个包子
    for i in range(10):
        time.sleep(1)
        print("做了1个包子,分两半!")
        c.send(i)
        c2.send(i)

producer("alex")    #2个消费者,相当于3个任务同时运行

 

4. 迭代器

可迭代对象(Iterable):可直接for循环的对象:generator、set、tuple、dict……

  • 注:可用isinstance()判断是否是“可迭代对象 ”

生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

 

迭代器(Iterator):可以被next()函数调用并不断返回下一个值的对象

可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。因此list、dict、str等数据类型默认不是Iterator。

总结:

  • 凡是可作用于for循环的对象都是Iterable类型;
  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  • 集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
  • Python的for循环本质上就是通过不断调用next()函数实现的,封装了而已
  • 迭代器其实就是生成器,换一个称呼,比较底层,但要理解概念

 

转载于:https://www.cnblogs.com/zhuyijunn/p/9850053.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值