【python学习】python装饰器

1 函数初步

#普通函数定义与调用
def hi(name="nihao"):
    return "hi" +name
print(hi())

"""hinihao"""
#我们可以将函数名赋给一个变量,传递的是引用
greet = hi
print(greet())
"""hinihao"""

#函数无返回值时输出None
def hi(name="nihao"):
    print( "hi" +name)
print(hi())

2 函数形参调用顺序

a,接收单个参数
*args接收元组型参数
**kwargs接收字典型参数
def func(a,*args,**kwargs):
    """不管如何调用func()函数,第一个参数a是必需的,否则报错typeerror"""
    print(a,end=">>")
    print(args,end=">>")
    print(kwargs)

func(3,4,5,6)
#输出结果:3>>(4, 5, 6)>>{}
func(3,text="你好")
#输出结果:3>>()>>{'text': '你好'}
func(text="你好")
#func() missing 1 required positional argument: 'a'

嵌套函数

def hi(name="nihao"):
    """嵌套的函数"""
    print("你好,中国")
    def greet():
        print("你好世界")
    def welcome():
        print("欢迎来北京")
    print(greet())  #greet()函数无返回值,故print函数输出None
    print(welcome()) #无返回值,输出None
print(hi()) #无返回值,输出None
"""
你好,中国
你好世界
None
欢迎来北京
None
None
"""


def hi(name="nihao"):
    print("你好,中国")
    def greet():
        return "你好世界"
    def welcome():
        return "欢迎来北京"
    print(greet())
    print(welcome())
print(hi())
greet() #显示语法错误,并不能在嵌套函数外部访问到内部的函数
"""
你好,中国
你好世界
欢迎来北京
None
"""

3 从函数中返回函数

#从函数中返回函数
def hi(name="zhang"):
    def greet():
        return "now you are in greet func"
    def welcome():
        return "now you are in welcome"

    if name == "zhang":
        return greet
    else:
        return welcome
a = hi()    #执行hi()函数,返回greet方法的引用
print(hi())
print(a) #返回的只是函数名greet,也就是说a指向了greet()函数的函数名所在地址,并不会发生函数调用
print(a())  #a()调用方法
"""
<function hi.<locals>.greet at 0x0000028138E57B88>
<function hi.<locals>.greet at 0x0000028138E57D38>
now you are in greet func
"""

4 函数作为参数传递

#将函数作为参数传递给另一个函数
def hi():
    return "中国"

def hello(func):
    print("正在做一些准备工作")
    print(func())
    
hello(hi)  #传递hi函数的引用
"""
正在做一些准备工作
中国
"""

5 闭包-不改变外部函数参数

#闭包:在一个外层函数中定义了一个内部函数,内部函数使用了外部函数的临时变量,并且外部函数的返回值是内部函数
"""闭包:
1.嵌套
2.内部使用了外部变量
3.返回的是内部函数"""
#第一个装饰器
def a_new_decorator(a_func):
    a = 10
    def wrap_the_func():    
        print("a的值",a)
        print("在执行a_func()之前做一些工作")
        a_func()            #闭包:内部函数引用了外部函数的参数a_func
        print("在执行a_func()之后做一些工作")
    return wrap_the_func    #返回函数引用
    """函数a_new_decorator()中有临时变量:a_func以及wrap_the_func;后者指向函数的地址"""

def func1():
    print("我是一个需要装饰器的函数")

func1 = a_new_decorator(func1)  #把函数wrap_the_func的引用传递给了func1
func1()

"""
a的值 10
在执行a_func()之前做一些工作
我是一个需要装饰器的函数
在执行a_func()之后做一些工作
"""

6 闭包-改变外部函数参数-nonlocal

#内部函数改变外部函数的参数的值,需要在内部函数中声明变量nonlocal
def a_new_decorator(a_func):
    """nonlocal"""
    a = 10
    def wrap_the_func():
        nonlocal a
        a += 10    
        print("a的值",a)
        print("在执行a_func()之前做一些工作")
        a_func()            
        print("在执行a_func()之后做一些工作")
    return wrap_the_func    

def func1():
    print("我是一个需要装饰器的函数")
    
func1 = a_new_decorator(func1)  
func1()

7 装饰器

#可以看到,装饰器对函数进行封装,并且能修改被封装函数的行为
"""开放(与那函数功能更加强大)、封闭(不能改变原函数)"""
def a_new_decorator(a_func):    #装饰器函数
    print("-------->1")
    print("被装饰函数的地址",a_func)
    def wrap_the_func():
        print("在执行a_func()之前做一些工作")
        #调用被装饰函数
        a_func()
        print("在执行a_func()之后做一些工作")
    print("-------->2")
    return wrap_the_func

@a_new_decorator                #所以@a_new_decorator等同于func1 = a_new_decorator(func1)
def func1():                    #被装饰函数
    print("我是一个需要装饰器的函数")
print("-------->3")
func1()
print("-------->4")
print(func1.__name__)

#使用装饰器,把装饰函数的引用传递给被装饰器函数,在执行函数时自动调用装饰器函数而不能更改被装饰函数
#从输出来看,@a_new_decorator相当于进行了一次传参调用
"""
-------->1
被装饰函数的地址 <function func1 at 0x000001BC7822ECA8>
-------->2
-------->3
在执行a_func()之前做一些工作
我是一个需要装饰器的函数
在执行a_func()之后做一些工作
-------->4
wrap_the_func
"""

8 登陆验证

"""使用装饰器在进行支付之检查登录状态"""
import time 

is_login = False

def login_required(func):
    """被装饰函数含有参数,内部参数也应定义相应数量的参数"""
    def wrapper(goods):
        if is_login:
            #执行付款操作
            func(goods)
        else:
            print("请先登录")
            login_status = login()
            if login_status:
                func(goods)
            else:
                print("用户名或密码错误")
                login()
    return wrapper

@login_required
def pay(goods):
    total_price = 0
    if isinstance(goods,dict):
        print("所有商品列表如下")
        for grocery,price in goods.items():
            print("商品%s价格%f" % (grocery,price))
            total_price += price
        print("一共需支付%f" % total_price)
        print("付款中")
        time.sleep(2)

def login():
    username = input("请输入用户名")
    passwd = input("请输入密码")
    if username == "admin" and passwd == "1234":
        print("登录成功")
        is_login = True
    else:
        print("登陆失败")
    return is_login

goods = {"三只松鼠":46.58,"蓝月亮":35,"Martin":5668}
pay(goods)

9 装饰器装饰多个函数

def decorator(func):
    """装饰器装饰多个函数,接收不定参数使用形参*args,**kwargs"""
    def wrapper(*args):
        print("----->1")
        #func(args)
        #test1() missing 1 required positional argument: 'b'
        func(*args)
        print("----->2")
    return wrapper

@decorator
def test1(a,b,c=10):
    print("和为",a+b+c)

@decorator
def test2():
    print("你好")

test1(6,8)
test2()

10 装饰器带返回值

"""
定义装饰器一般格式:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        ...
        func(*args,**kwargs)
        ...
    return wrapper
返回值的通用性:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        ...
        return func(*args,**kwargs)
    return wrapper
    或者:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        result = func(*args,**kwargs)
        return result
    return wrapper
"""
def decorator(func):
    def wrapper(*args,**kwargs):
        print("你好")
        result = func(*args,**kwargs)
        return result
    return wrapper

@decorator
def test(a,b):
    print(a+b)
    return "世界"

f = test(2,3)
print(f)

11 装饰器带参数

"""
装饰器带参数
"""
def decorator(args1):
    def wrapper_outer(func):
        def wrapper(*args,**kwargs):
            print("---装饰前",args1)
            if args1 == "AA":
                func(*args,**kwargs)
                print("----->进入AA家")
            elif args1 == "BB":
                func(*args,**kwargs)
                print("----->进入BB家")
            else:
                func(*args,**kwargs)
                print("路由错误")
            print("---装饰后",args1)
        return wrapper
    return wrapper_outer

@decorator("AA")
def test1():
    print("------>test1")

def test(a):
    print("------>test",a)
    return 'hello'

test1()
"""
---装饰前 AA
------>test1
---装饰后 AA
"""

12 多层装饰器

"""
多层装饰器
    装饰顺序按就被装饰函数近的原则进行装饰
    装饰后的最外层装饰器的函数引用指向被装饰函数
"""
def decorator(args1):
    def wrapper_outer(func):
        def wrapper_butterfly(*args,**kwargs):
            print("---装饰前",args1)
            func(*args,**kwargs)            #func = wrapper_paper
            print("---装饰后",args1)
        return wrapper_butterfly
    return wrapper_outer
    
def decorator2(args1):
    def wrapper_outer(func):
        def wrapper_paper(*args,**kwargs):
            print("---装饰前",args1)
            func(*args,**kwargs)
            print("---装饰后",args1)
        return wrapper_paper
    return wrapper_outer

"""根据就近原则,先运行装饰器@decorator("包装纸"),返回一个wrapper_paper引用给外层装饰器,
也就是说装饰器@decorator2("蝴蝶结")装饰的对象是wrapper_paper,最后返回wrapper_butterfly
的引用给test1"""

@decorator2("蝴蝶结")
@decorator("包装纸") 
def test1(): #test1 = wrapper_butterfly
    print("------>test1")

test1() 
"""
---装饰前 蝴蝶结
---装饰前 包装纸
------>test1
---装饰后 包装纸
---装饰后 蝴蝶结
"""

13 @property

1.修饰方法,使方法可以像属性一样访问。

class DataSet(object):
  @property
  def method_with_property(self): ##含有@property
      return 15
  def method_without_property(self): ##不含@property
      return 15

l = DataSet()
print(l.method_with_property) # 加了@property后,可以用调用属性的形式来调用方法,
							  #后面不需要加()
print(l.method_without_property())  #没有加@property , 必须使用正常的调用方法的形式,
									#即在后面加()

2.与所定义的属性配合使用,这样可以防止属性被修改

class DataSet(object):
    def __init__(self):
        self._images = 1
        self._labels = 2 #定义属性的名称
    @property
    def images(self): #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户
    			      #进行使用,而且用户没办法随意修改。
        return self._images 
    @property
    def labels(self):
        return self._labels
l = DataSet()
#用户进行属性调用的时候,直接调用images即可,而不用知道属性名_images,因此用户无法更改属
#性,从而保护了类的属性。
print(l.images) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()

14总结

"""
装饰器函数不带参数:
    def decorator(func):
        def wrapper():
        ...
        ...
        func()
        ...
    return wrapper
装饰器函数带不变参数:
    def decorator(func):
            def wrapper(参数个数与被装饰函数保持一致):
            ...
            ...
            func()
            ...
        return wrapper
装饰器函数带可变参数的装饰器,可实现一个装饰器装饰多个函数:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        ...
        func(*args,**kwargs)
        ...
    return wrapper
装饰器函数带有返回值的通用性:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        ...
        return func(*args,**kwargs)
    return wrapper
    或者对返回值进行加工:
    def decorator(func):
        def wrapper(*args,**kwargs):
        ...
        result = func(*args,**kwargs)
        return result
    return wrapper
装饰器的使用步骤:
    1.定义装饰器
    闭包 + 参数
    2.使用
    @装饰器名字
    def 被装饰函数():
        ...
    3.函数调用:A()
@装饰器的后台执行:
    1.将被装饰函数作为参数传递给装饰器
    2.执行装饰器
    3.将装饰器的返回值赋给被装饰函数
开发时的使用:
    1.登录验证
    2.flask中的路由就是通过装饰器完成
    3.日志文件
"""

参考:https://zhuanlan.zhihu.com/p/64487092 python @property的介绍与使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值