码迷,mamicode.com
首页 > 编程语言 > 详细

Python之装饰器

时间:2018-05-08 14:30:39      阅读:223      评论:0      收藏:0      [点我收藏+]

标签:display   开放   index   主页   理解   enc   意义   import   结束   

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()
代码

.

Python之装饰器

标签:display   开放   index   主页   理解   enc   意义   import   结束   

原文地址:https://www.cnblogs.com/tootooman/p/9007510.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!