Python之装饰器

1、什么是闭包

定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

只要是闭包,就一定会有 .__closure__ 方法,查看闭包元素,且结果不为 None

.__closure__[0].cell_contents 查看第一个元素

例1:
def outer():
    x=10
    def inner():  #条件一:inner 就是内部函数
        print(x)  #条件二:引用外部环境的一个变量
    return inner #结论:内部函数 inner就是一个闭包

f=outer()   # 执行到 return innser 结束,return inner 返回 inner 的内存地址
print(f)  #  <function outer.<locals>.inner at 0x0000000001060268>
f()  # f() 装的是 Inner 内存地址指向的对象 print(x) = 10


例2:
def foo():
    print('foo')
    return bar


def bar():
    print('bar')

# foo()
b = foo()  #先返回 foo 内存地址的指向对象 print('foo'),好包含 bar 的内存地址
b() # foo 已经执行完了,剩下的 bar 内存地址指向的对象,print('bar')
闭包例子

闭包的意义:

返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

#应用领域:延迟计算(原来我们是传参,现在我们是包起来)

 from urllib.request import urlopen

def index(url):
    def get():
        return urlopen(url).read()
    return get

baidu=index('http://www.baidu.com')
print(baidu().decode('utf-8'))
View Code

 

2、什么是装饰器

理解:

  装饰器:我在知乎看到这样一个比方(我们的函数好比内裤,作用是遮羞。但在一些特定的环境,内裤明显满足不了我们的需求,冬天它没法为我们防风御寒。所以有了长裤,装饰器就像长裤,在不影响内裤作用的前提下,给我们的身子提供了保暖的功效)

装饰器是对原函数的一种扩展,本质是一个函数,在原函数不需要做任何代码变动的前提下增加额外功能。装饰器返回的也是一个函数对象。

场景:插入日志、性能测试、事务处理、权限校验等应用广泛。

遵守封闭开放原则:对源代码修改封闭,对功能外部增加开放。

 功能函数无参数

import  time
def timmer(func):
    def warpper(name):
        start_time=time.time()
        func(name)  # home(name)   不给参数,执行 home(name) 会报 home() 的参数传递错误
        stop_time=time.time()
        print('run time is %s'%(stop_time-start_time))
    return warpper

@timmer   # home=timmer(home),home 拿到一个 warpper 的内存地址,调用 home 的时候就是执行 warpper
def home(name):
    time.sleep(2)
    print('welcome to %s home page'%name)

home('dragon') # 实际上是运行 timmer 里的 warpper(name),所以 warpper 也要给个参数,不然会报 warpper的参数错误
简单装饰器例1
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)  #先执行 my_max(1,2),res=2,my_max 是有一个执行结果的
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper


@timmer
def my_max(x,y):
    print('my_max function')
    res=x if x > y else y
    return res  # 这里的 res 被 warpper 里的 res 接收了

res=my_max(1,2) #res=wrapper(1,2)
print('=====>',res)
简单装饰器例2
def show_time(f):
    start_time=time.time()
    f()
    end_time=time.time()
    print('spend %s'%(end_time-start_time))

def bar():
    print('bar...')
    time.sleep(3)

show_time(bar)
#问题:完成需求,但改变了调用方式,原本是  bar(),现在是 show_time(bar)


#解决方法:
def show_time(f):    #这个就是装饰器函数,装饰下面的 bar 函数,作为一个bar函数的扩展
    def inner(): # 当执行 show_time(f) 函数时,把 inner 函数放到内存,拿到 inner 的内存地址
        start_time = time.time()
        f()
        end_time = time.time()
        print('spend %s' % (end_time - start_time))
    return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))

def bar():
    print('bar...')
    time.sleep(3)

show_time(bar)
bar=show_time(bar)  # 拿到的只是 print(bar') 和 Inner 的内存地址
bar() # 执行 inner 函数,调用 inner 内存指向的内容

#结论:解决上面的问题,在不改变的调用的方式,扩展函数的功能

#调用方式的优化:@+函数名会把下面函数名当做参数传给@函数执行
@show_time   # 等于 bar=show_time(bar)
def bar():
    print('bar...')
    time.sleep(3)

bar()
在 bar 函数的基础上增加调用该函数使用了多久时间

功能函数带参数

def show_time(f):    # 装饰器函数,装饰下面的 foo 函数,并作为一个函数的扩展
    def inner(*args): #foo函数带了形参,这里也要带上形参
        start_time = time.time()
        f(*args)    #执行 foo 函数,把 foo 函数的传递过来的实参传给形参 args
        end_time = time.time()
        print('spend %s' % (end_time - start_time))
    return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))


@show_time  #给自己加上装饰器函数,装饰器函数下面的 inner 函数
def foo(*args):
    sums=0
    for i in args:
        sums+=i
    print(sums)
    time.sleep(1)

foo(1,2,5,7,9)
弄一个加法器

装饰器带参数

def logger(flag='true'):
    def show_time(f):    # 装饰器函数,装饰下面的 foo 函数,并作为一个函数的扩展
        def inner(*args): #foo函数带了形参,这里也要带上形参
            start_time = time.time()
            f(*args)    #执行 foo 函数,把 foo 函数的传递过来的实参传给形参 args
            end_time = time.time()
            print('spend %s' % (end_time - start_time))
            if flag=='true':   #inner 拿到 flag 参数后做判断,闭包概念,对外部作用域的变量进行引用
                print("日志记录")
        return inner # inner 的内存地址指向的是 print('spend %s' % (end_time - start_time))
    return show_time

@logger() 分成两部分:先执行 logger 函数,返回 show_time 内存地址,然后变成 @show_time 装饰器, show_time 装饰器把下面的函数名当做参数传递进 show_time 里面,执行show_time 返回 inner,foo(1,2,5,7,9) = inner(1,2,5,7,9),执行 inner
def foo(*args):
    sums=0
    for i in args:
        sums+=i
    print(sums)
    time.sleep(1)

foo(1,2,5,7,9)
例3:把结果写进日志文件里面,时间不写进去,正常输出

 

装饰器应用之登陆

   在电商平台中,我们可以看到,在不同的页面,如选择商品、购物车、金融支付等页面都能进行登陆且能记住登陆状态,登陆一次后就不需要在其它页面再次登陆。使用装饰器把登陆抽离出来。

    加入文件读写判断用户名密码
    用户选择不同页面登陆时,反回不同结果
需求
    创建登陆标志位(login_falg),用来判断是否已登陆
    商品commodity()金融finance()购物车shopp_cart()为三个独立函数
    使用带参装饰器,反回不同结果
    用户选择进行测试
分析
jingdong.db
{'user':{'alex':'123','hjc':'123'}}


weixin.db
{'user':{'alex1':'1234','hjc1':'1234'}}
用户数据库
login_status=False
with open('jingdong','r',encoding='utf8') as jd:
    jd=eval(jd.read().strip())
with open('weixin','r',encoding='utf8') as wx:
    wx=eval(wx.read().strip())


def type(auth_type='jingdong'):  # 第三部:判断页面的登录的类型,默认是 jingdong,目的是在login_type 可以引用一个 auth_type 变量
    def page(f): # 第二部:调用主页、书店,金融的功能函数
        global login_status #用于修改全局变量:login_status 登录状态,默认 false,要求用户验证登录,true就不认证
        def login_type():  # 第一步先写好这个函数,再往外套函数
            global login_status # 用于修改 全局变量
            if login_status == False: #默认为未登录状态,要求用户验证
                username = input('username: ')
                password = input('password: ')
                if auth_type=='jingdong': #如果登录页面的是 jingdong,去京东的用户文件里验证
                    if username in jd['user'] and password == jd['user'][username]:
                        print('welcome...')
                        f()  #成功后进入页面,执行页面函数,显示内容
                        login_status=True  #登录成功后,登录状态转为 true
                    else:
                        print('error')
                elif auth_type=='weixin':
                    if username in wx['user'] and password == wx['user'][username]:
                        print('welcome...')
                        f()
                        login_status = True
                    else:
                        print('error')
            else:
                print('用户已登录')
        return login_type #login_type 的内存地址,用于指向 login_type 的函数对象
    return page # 返回 page 函数的内存地址,指向 login_type 函数的内存地址,指向 login_type 的函数对象

@type()
def home():  # 主页
    print('welcome to home page...')

@type('weixin')
def book():  # 书店
    print('welcome to book page...')

@type()
def finance():  #金融页
    print('welcome to finance page...')

#start 启动显示如下页面

# 1.home
# 2.finance
# 3.book
#>>:2 检测用户登录状态,没登录就调用登录验证接口

while True:
    user_input=input('请输入:\n1:【主页】\n2:【书店】\n3:【金融】')
    if user_input =='1':
        home()
    elif user_input =='2':
        book()
    else:
        finance()
代码

.

posted @ 2018-05-08 13:18  H-JIACHENG  阅读(231)  评论(0编辑  收藏  举报