装饰器,global与nonlocal

装饰器

灵魂三问

  • 1.什么事装饰器
    器 => 工具
    装饰 => 指的是为被装饰的对象添加新功能

    装饰器本身可以是任意可调用的对象 => 函数
    被装饰的对象也可以是任意可调用的对象 => 函数
    目标:写一个函数来为另一个函数添加新功能

  • 2.为何要用装饰器
    开放封闭原则:软件一旦上线就应该对修改封闭,对扩展开发
    对修改封闭:
    - 1.不能修改功能的源代码
    - 2.也不能修改功能的调用方式
    对扩展开发:
    - 可以为原有的功能添加新的功能
    装饰器就是要在不修改功能源代码以及调用方式的前提下为员功能添加额外新的功能

  • 3.如何用装饰器

  • 如何用装饰器:
import time

def index():
    print('welcome to index page')
    time.sleep(2)

def outter(func):       #给index功能装饰一个检测运行时间的新功能
    #func = 最原始那个index的内存地址
    def wrapper():
        start = time.time()
        func()   #最原始那个index的内存地址()
        stop = time.time()
        print ('run time is %s' %(stop-start))
    return wrapper

index = outter(index)     #index = outter(原始那个index的内存地址)   #index = wrapper函数的内存地址
index()  #wrapper 偷梁换柱,现在的index是wrapper的内存地址

  • 装饰器修正1

如果需要index的返回值,上面如果res = index() 实际上是 res = wrapper()

import time

def index():
    print('welcome to index page')
    time.sleep(2)
    return 123

# ===================================================

def outter (func):
    #func = 最原始那个index的内存dizh
    def wrapper():
        start = time.time()
        res = func()    #最原始那个index的内存地址    把func的返回值指向res
        stop = time.time()
        print('run time is %s' %(stop-start))
        return res       #wrapper的返回值和最原始index的返回值相同
    return wrapper
index = outter(index)    #index = outter(最原始那个index的内存地址)  #index = wrapper函数的内存地址

# ===================================================

res = index()   #res = wrapper()
print(res)
  • 装饰器修正2

之前被装饰的函数是无参数的函数,如果被装饰的函数是有参数的函数


import time

def index():
    print('welcome to index page')
    time.sleep(3)
    return 123

def home(name):
    print('welcome %s to home page' %name)
    time.sleep(1)

# ===================================================

def outter(func):
    #func = 最原始那个home的内存地址
    def wrapper (*args,**kwargs):     #用了之前学的*和**的用法
        start = time.time()
        res = func(*args,**kwargs)       #如果被装饰的函数需要参数,可以把wrapper的参数传给被装饰的函数做参数
        stop = time.time()
        print('run time is %s' %(stop-start))
        return res
    return wrapper

index = outter(index)    #index = outter(最原始那个index的内存地址) #index = wrapper函数的内存地址
home = outter(home)   #home = outter(最原始那个home的内存地址) #home = wrapper函数的内存地址

# ===================================================

home('wood')   #wrapper('wood')
index()  #wrapper
  • 装饰器的语法糖

之前写的装饰器最后都要把被修饰的函数名偷梁换柱给outter
index = outter(index)
home = outter(home)
如果多个函数需要用到这个装饰器,我们就要写很多的像上面那样的代码
这要写重复的东西不是很low,python怎么能low呢
这里有一个装饰器的语法糖的概念

# @装饰器的名字:要在被装饰对象正上方单独一行写上

import time

def timmer(func):           #写一个装饰器
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs):
        stop = time.time()
        print('run time is %s' %(stop-start))
        return res
    return wrapper

@timmer         #@后+装饰圈的函数名,直接使用省了index = outter(index) 
def index():
    '''这是index功能 '''
    print('welcome to index page')
    time.sleep(2)
    return 123

@timmer       #@后+装饰圈的函数名,直接使用省了index = home(index) 
def home(name):
    '''这是home功能 '''
    print('welcome %s to home page' %name)
    time.sleep(1)

print(help(index))     #wrapper的注释
print(index.__name__)     #wrapper的名字

上面代码有一个新问题,函数有一些方法:
help可以调函数的注释
.__name__调函数名
还有很多.__的方法。
这个时候用了装饰器 index已经不是原来的index了,
现在index指的是wrapper的内存地址了,
想神不知鬼不觉的再调最原始的index方法怎么办

#from functools import wraps方法
from functools import wraps      #调用这个方法

import time

def timmer(func):           #写一个装饰器
    @wraps(func)       #在wrapper上面写一个@wraps(func)把最原始函数的那些都调过来
    def wrapper(*args,**kwargs):
        start = time.time()
        res = func(*args,**kwargs):
        stop = time.time()
        print('run time is %s' %(stop-start))
        return res
    return wrapper

@timmer         #@后+装饰圈的函数名,直接使用省了index = outter(index) 
def index():
    '''这是index功能 '''
    print('welcome to index page')
    time.sleep(2)
    return 123

@timmer       #@后+装饰圈的函数名,直接使用省了index = home(index) 
def home(name):
    '''这是home功能 '''
    print('welcome %s to home page' %name)
    time.sleep(1)

print(help(index))      #这就显示index的注释
print(index.__name__)    #index的名字

  • 无参装饰器

根据上面的装饰器可以确定一个无参装饰器的模板

#无参装饰器的模板
def outter(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        return res
    return wrapper

用这个写一个认证功能

import time

user_info = {'current_user':None}    #变量值是可变的

def auth(func):
    def wrapper(*args,**kwargs):
        if user_info['current_user'] is not None:
            res = func(*args,**kwargs)     
            return res      #return会结束函数
        inp_user = input('username>>>: ').strip()
        inp_pwd = input('password>>>: ').strip()
        if inp_user == 'egon' and inp_pwd == '123':
            # 记录登录状态
            user_info['current_user']=inp_user

            print('login successful')
            res = func(*args,**kwargs)
            return res
        else:
            print('user or password error')
    return wrapper

@auth
def index():
    """这是index功能"""
    print('welcome to index page')
    time.sleep(2)
    return 123

@auth
def home(name):
    """这是home功能"""
    print('welcome %s to home page' %name)
    time.sleep(1)

  • 有参装饰器

之前我们说的都是无参装饰器,装饰器本身不需要参数的,参数都是为了转给或转到原函数的,现在说有参装饰器,除了需要转给原函数的参数外,装饰器本身的功能也需要参数的情况

def outter2(xxx,yyy):
    #局部内的两个闭包函数的参数都是不能动的,在需要xxx,yyy就需要在往上找了,不能找到全局作用域去吧
    #再包一下,三层闭包函数,最外层的给xxx,yyy传值
    def outter(func):
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            print(xxx)
            print(yyy)
            return res
        return wrapper
    return outter

我们上面的装饰器是把认证的来源写死了,如果我们是从文件中调用数据来认证,如果我们是从mysql数据库里调用数据,ldap里面调用数据

import time

user_info = {'current_user':None}

def auth2(engine = 'file'):    #定义一个默认形参,engine默认=‘file’
    def auth(func):
        def wrapper(*args,**kwargs):
            if user_info['current_user'] is not None:
                res = func(*args,**kwargs)
                return res
            inp_user = input('username>>>: ').strip()
            inp_pwd = input('password>>>: ').strip()

            if engine == 'file':
                print('基于文件的认证')
                if inp_user == 'egon' and inp_pwd == '123':
                    # 记录登录状态
                    user_info['current_user']=inp_user

                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('user or password error')
            elif engine == 'mysql':
                print('基于mysql数据的认证')
            elif engine == 'ldap':
                print('基于ldap的认证')
            else:
                print('无法识别认证源')
        return wrapper
    return auth

@auth2(engine = 'mysql') # @auth ===> index=auth(最原始那个index的内存地址)===》index=wrapper
def index():
    """这是index功能"""
    print('welcome to index page')
    time.sleep(2)
    return 123

@auth2(engine = 'file')
def home(name):
    """这是home功能"""
    print('welcome %s to home page' %name)
    time.sleep(1)

index() #wrapper()
home('egon')
  • 补充作用域,global与nonlocal
x = 1
def func():
   x = 2

func()
print(x)     #x在全局里找,x=1  1是整型不能在局部直接改


x = []
def func():
   x.append(1)
   x.append(2)
   x.append(3)

func()
print(x)     #x是列表,是可变的,可以在局部直接改

# global: 在局部声明变量是全局变量
x = 1
def func():
    global x       #global我就是要改
    x = 2

func()
print(x)      #print 2

# nonlocal:在局部声明变量是外层函数的变量,如果外层没有,就在往更外存找,知道在外层的函数
x = 333
def f1():
    x = 222
    def f2():
        x = 111
        def f3():
            nonlocal x
            x = 0
        f3()
        print('f2内部的x: ',x)
    f2()
    print('这是f1内部的x: ',x)

f1()
print(x)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值