装饰器

装饰器

 

一、闭包

1、说明

闭包函数必须满足两个条件:

  1.函数内部定义的函数

  2.包含对外部作用域而非全局作用域的引用

闭包的特点:

  1.自带作用域

  2.延迟计算

2、使用

z = 100
def outer():
    x = 2
    y = 3

    def inner():
        print("x = %s" % x)
        print("y = %s" % y)
        print("z = %s" % z)  # 先查找局部参数,没有z,再查找全局参数。

    print(inner.__closure__)  # 查看使用的函数内部参数
    return inner

ret = outer()
# 一共引用了2个局部参数x、y,下面是结果。z是全局参数。
# (<cell at 0x052A0FF0: int object at 0x569ED730>, <cell at 0x052AC090: int object at 0x569ED740>)
ret()  # 延迟执行
# x = 1
# y = 2
# z = 100
from urllib.request import urlopen

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

python = index("http://www.python.org") # 返回的是get函数的地址
print(python()) # 执行get函数《并且将返回的结果打印出来
baidu = index("http://www.baidu.com")
print(baidu())
示例

 

二、装饰器

装饰器:外部函数传入被装饰函数名,内部函数返回装饰函数名。

特点:1.不修改被装饰函数的调用方式 2.不修改被装饰函数的源代码

1、函数相关的装饰器

(1)无参装饰器

在不修改主逻辑函数的情况下,增加额外的功能 

原函数

def index():  # 主逻辑函数
    time.sleep(random.randrange(1, 3))
    print("welcome to index page")

index()
# welcome to index page

方法1.定义额外的函数,在函数内部引用原函数。但函数的使用方法改变了,不可取。

import time, random

def index2(x):  # 主逻辑函数
    time.sleep(random.randrange(1, 3))
    print("welcome to index2 page")
    return "index2的返回值x:%s" % x

def timer(*args, **kwargs):
    start = time.time()
    my_func = index2(*args, **kwargs)
    end = time.time()
    print("主逻辑耗时:%s 秒" % (end - start))
    return my_func

ret = timer(10)  # 使用方法改变了
print(ret)
# welcome to index2 page
# 主逻辑耗时:1.0005226135253906 秒
# index2的返回值x:10

方法2.装饰器,函数的使用方法不变

import time, random

def timer(func):
    def wrapper(*args,**kwargs):
        start = time.time()
        my_func = func(*args,**kwargs)
        end = time.time()
        print("主逻辑耗时:%s 秒" % (end-start))
        return my_func
    return wrapper

@timer
def index1(x):  # 主逻辑函数
    time.sleep(random.randrange(1, 3))
    print("welcome to index1 page")
    return "index1的返回值x:%s" % x

def index2(x):  # 主逻辑函数
    time.sleep(random.randrange(1, 3))
    print("welcome to index2 page")
    return "index2的返回值x:%s" % x

ret = index1(10)  # 使用装饰器
print(ret)
# welcome to index1 page
# 主逻辑耗时:2.0000548362731934 秒
# index1的返回值x:10

ret = timer(index2)(10)  # 等价于上面index1
print(ret)
# welcome to index2 page
# 主逻辑耗时:1.0003700256347656 秒
# index2的返回值x:10

(2)有参装饰器

 装饰器里增加参数,其实就是再无参装饰器外层再套一层函数。

import time,random

def timer(*args_timer,**kwargs_timer):  # 在无参装饰器外层增加一层有参函数
    def outer(func):  # 与上面无参装饰器一样的使用
        def inner(*args_inner,**kwargs_inner):
            start = time.time()
            ret = func(*args_inner,**kwargs_inner)
            end = time.time()
            print("装饰器的参数0:%s" % args_timer[0])  # 增加的装饰器参数,根据实际情况使用
            print("主逻辑耗时:%s 秒" % (end - start))
            return ret
        print("装饰器的参数1:%s" % args_timer[1])  # 增加的装饰器参数,根据实际情况使用
        return inner
    print("装饰器的参数2:%s" % args_timer[2])  # 增加的装饰器参数,根据实际情况使用
    return outer

@timer(
"timer0","timer1","timer2") def index3(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index3 page") return "index3的返回值x:%s" % x
ret
= index3(100) print(ret) # 装饰器的参数2:timer2 # 装饰器的参数1:timer1 # welcome to index3 page # 装饰器的参数0:timer0 # 主逻辑耗时:2.000260829925537 秒 # index3的返回值x:100 ################################################################################## def index4(x): # 主逻辑函数 time.sleep(random.randrange(1, 3)) print("welcome to index4 page") return "index4的返回值x:%s" % x ret = timer("timer0","timer1","timer2")(index4)(100) # 相当于上面使用了有参装饰器的index3 print(ret) # 装饰器的参数2:timer2 # 装饰器的参数1:timer1 # welcome to index4 page # 装饰器的参数0:timer0 # 主逻辑耗时:2.000366449356079 秒 # index4的返回值x:100

2、类相关的装饰器

(1)装饰器是类

  装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

class Foo(object):
    def __init__(self, func):
        self._func = func

    def __call__(self):
        print('类装饰器 runing')
        self._func()
        print('类装饰器 ending')


@Foo # bar = Foo(bar)
def bar():
    print('bar')


bar() # Foo(bar)()

# 结果
# 类装饰器 runing
# bar
# 类装饰器 ending

(2)类自带的装饰器

@property、@staticmethod、@classmethod

待补充

 

(3)让类作为装饰器的参数

class MyClass(object):

    @staticmethod
    def clsfunc1():
        print("MyClass的func1使用(是staticmethod,不用实例化对象)")

    @staticmethod
    def clsfunc2():
        print("MyClass的func2使用(是staticmethod,不用实例化对象)")


def deco(cls):
    """使用类的staticmethod,"""
    def outer2(func):
        def outer1(*args,**kwargs):
            cls.clsfunc1()
            ret = func(*args,**kwargs)
            cls.clsfunc2()
            return ret
        return outer1
    return outer2


@deco(MyClass)  # 对函数使用
def myfunc(x):
    print(" myfunc() called.")
    return x

ret = myfunc(100)
print(ret)
# MyClass的func1使用(是staticmethod,不用实例化对象)
#  myfunc() called.
# MyClass的func2使用(是staticmethod,不用实例化对象)
# 100

######################################################## 

class Foo(object):

    @deco(MyClass)  # 跟函数一样,可使用多个装饰器
    def run(self,x):
        print("Foo is running")
        return x
foo = Foo()
ret = foo.run(999)
print(ret)
# MyClass的func1使用(是staticmethod,不用实例化对象)
# Foo is running
# MyClass的func2使用(是staticmethod,不用实例化对象)
# 999

(4)对整个类进行装饰(后续再扩展,涉及到__init__,__new__)

def deco(func):
    def wrapper(*args, **kwargs):
        print('before')
        ret = func(*args, **kwargs)
        print('after')
        return ret
    return wrapper


@deco
class Person:
    def __new__(cls, *args, **kwargs):  # 在类__init__先进行__new__
        print('__new__')
        return args,kwargs


ret = Person(123,123,x=2)
print(ret)
# before
# __new__
# after
# ((123, 123), {'x': 2})

 

4、使用多个装饰器

 多层装饰器

def outer1(func):
    def wrapper(*args,**kwargs):
        print("无参装饰器outer1")
        ret = func(*args,**kwargs)
        return ret
    return wrapper

def outer2(func):
    def wrapper(*args,**kwargs):
        print("无参装饰器outer2")
        ret = func(*args,**kwargs)
        return ret
    return wrapper


def outer3(x,y,z):
    def wrapper2(func):
        def wrapper1(*args, **kwargs):
            print("有参装饰器outer3")
            print("x:{},y:{},z:{}".format(x,y,z))
            ret = func(*args, **kwargs)
            return ret
        return wrapper1
    return wrapper2

@outer3(1,2,3)  # 3.把outer2套在里面  tmp3 = outer3(1,2,3)(tmp2)
@outer2  # 2.把outer1套在里面  tmp2 = outer2(tmp1)
@outer1  # 1.优先把index5套在里面 tmp1 = outer(index5)
def index5(x):
    print("主逻辑函数index5")
    return "index5的返回值x:%s" % x


ret = index5(100)
print(ret)
# 有参装饰器outer3
# x:1,y:2,z:3
# 无参装饰器outer2
# 无参装饰器outer1
# 主逻辑函数index5
# index5的返回值x:100

##############################################################

def index5(x):
    print("主逻辑函数index5")
    return "index5的返回值x:%s" % x

ret = outer3(1,2,3)(outer2(outer1(index5)))(100)
print(ret)
# 有参装饰器outer3
# x:1,y:2,z:3
# 无参装饰器outer2
# 无参装饰器outer1
# 主逻辑函数index5
# index5的返回值x:100

 4、其他(待补充)

待补充

三、示例

 1、登录

def index1():
    print("欢迎来到购物系统")
    print("消费100元")
    print("明天到货")
    return None

index1()
# 欢迎来到购物系统
# 消费100元
# 明天到货

#######################################################################

def auth(func):
    def wrapper(*args,**kwargs):
        user = input("用户名>>:")
        password = input("密码>>:")
        if user == "abc" and password == "123":
            ret = func(*args,**kwargs)
            return ret
        else:
            print("用户名或密码错误,请重新登录")
    return wrapper

@auth
def index2():
    print("欢迎来到购物系统")
    print("消费100元")
    print("明天到货")
    return None

index2()
# 用户名>>:abc
# 密码>>:123
# 欢迎来到购物系统
# 消费100元
# 明天到货

#
# 用户名>>:asdfsdf
# 密码>>:sdfsdf
# 用户名或密码错误,请重新登录

 

posted @ 2018-03-23 14:55  fat39  阅读(126)  评论(0编辑  收藏  举报