python中装饰器的理解

2020-09-22 14:05:00
六月
来源:
https://www.runoob.com/w3cnote/python-func-decorators.html
转贴 754

1、基础知识

    a.一般函数定义

    def hi(name="yasoob"):    
        return "hi " + name
    print(hi())    # output: 'hi yasoob'
     # 我们甚至可以将一个函数赋值给一个变量,比如
    greet = hi
     # 我们这里没有在使用小括号,因为我们并不是在调用hi函数
     # 而是在将它放在greet变量里头。我们尝试运行下这个
    print(greet()) # output: 'hi yasoob'
         # 如果我们删掉旧的hi函数,看看会发生什么!
    del hi
    print(hi())#outputs: NameError
    print(greet())#outputs: 'hi yasoob'

    b.函数中定义函数,函数嵌套 (闭包)    

    def hi(name="yasoob"):    
        print("now you are inside the hi() function")
    def greet():        
        return "now you are in the greet() function"
    def welcome():        
        return "now you are in the welcome() function"
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")
    hi()
    #output:now you are inside the hi() function
    #now you are in the greet() function
    #now you are in the welcome() function
    #now you are back in the hi() function
    # 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
    # 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
    greet()
    #outputs: NameError: name 'greet' is not defined

     c.函数中返回函数

        def hi(name="yasoob"):    
            def greet():        
                return "now you are in the greet() function"
            def welcome():        
                 return "now you are in the welcome() function"
        if name == "yasoob":        
                 return greet
        else:        
                 return welcome
         a = hi()
         print(a)
         #outputs: <function greet at 0x7f2143c01500>
         #上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
         #现在试试这个
         print(a())
         #outputs: now you are in the greet() function

    d.将函数作为参数传递给函数

        def hi():    
            return "hi yasoob!"
        def doSthBeforeHi(func):    
            print("I am doing some boring work before executing hi()")
            print(func())
         doSthBeforeHi(hi)
        #outputs:I am doing some boring work before executing hi()
        #  hi yasoob!

2、装饰器实现

    a.简单的装饰器

        def debug(func):
            def wrapper():
                print('[debug]:enter {}()'.format(func.__name__))
                func()
                return func
         return wrapper
        @debug                           
        def say_hello():
            print('hello')
        #say_hello = debug(say_hello)    # 添加功能并保持原函数名不变,可以使用@语法糖实现同样功能(@debug)
        say_hello()

    b.带参数的装饰器

             指定装饰器函数接受和原函数一样的参数,解决被装饰函数的参数传递问题

        def debug(func):
            def wrapper(sth):  # 指定和被包装函数一样的参数
                print "[DEBUG]: enter {}()".format(func.__name__)        
                return func(sth)    
             return wrapper  # 返回包装过函数
        @debug
        def say(sth):
            print("hello {}!".format(sth))

           使用可变参数*args和关键字参数**kwargs,让装饰器就可以用于任意目标函数

              def debug(func):
                    def wrapper(*args, **kwargs):   #接受任意类型的参数
                        print("[DEBUG]: enter {}()".format(func.__name__))
                        print("Prepare and say...",)
                    return func(*args, **kwargs)
               return wrapper  # 返回
               @debug
               def say(something):
                   print "hello {}!".format(something)

3、函数装饰器,基于类实现的装饰器

def logging(level):
    def wrapper(func):
        def inner_wrapper(*args, **kwargs):
            print "[{level}]: enter function {func}()".format(
                level=level,
                func=func.__name__)            
                return func(*args, **kwargs)        
        return inner_wrapper   
    return wrapper
@logging(level='INFO')
def say(something):
        print("say {}!".format(something))
# 如果没有使用@语法,等同于
# say = logging(level='INFO')(say)
@logging(level='DEBUG')
def do(something):
    print("do {}...".format(something))
    if __name__ == '__main__':
        say('hello')
        do("my work")

 基于类实现的装饰器:装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重载了__call__()方法,那么这个对象就是callable的。

4、@property装饰器,@staticmethod装饰器,@classmethod装饰器,property()函数

内置的装饰器和普通的装饰器原理是一样的,只不过返回的不是函数,而是类对象,所以更难理解一些。

@property:

@property 语法糖提供了比 property() 函数更简洁直观的写法。
被 @property 装饰的方法是获取属性值的方法,被装饰方法的名字会被用做 属性名。
被 @属性名.setter 装饰的方法是设置属性值的方法。
被 @属性名.deleter 装饰的方法是删除属性值的方法。
class Student:
    def __init__(self):
        self._age = None
    @property
    def age(self):
        print('获取属性时执行的代码')
        return self._age
    @age.setter
    def age(self, age):
        print('设置属性时执行的代码')
        self._age = age
    @age.deleter
    def age(self):
        print('删除属性时执行的代码')
        del self._age
student = Student()
# 设置属性
student.age = 18
# 获取属性
print('学生年龄为:' + str(student.age))
# 删除属性
del student.age

property():

property(fget=None, fset=None, fdel=None, doc=None) -> property attribute

说明:

fget 是获取属性值的方法。

fset 是设置属性值的方法。

fdel 是删除属性值的方法。

doc 是属性描述信息。如果省略,会把 fget 方法的 docstring 拿来用(如果有的话)

class Student:
    def __init__(self):
        self._age = None

    def get_age(self):
        print('获取属性时执行的代码')
        return self._age

    def set_age(self, age):
        print('设置属性时执行的代码')
        self._age = age

    def del_age(self):
        print('删除属性时执行的代码')
        del self._age

    age = property(get_age, set_age, del_age, '学生年龄')

student = Student()
# 注意要用 类名.属性.__doc__ 的形式查看属性的文档字符串
print('查看属性的文档字符串:' + Student.age.__doc__)

# 设置属性
student.age = 18
# 获取属性
print('学生年龄为:' + str(student.age))
# 删除属性
del student.age
class People:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    def get_age(self):
        return self.__age
    def set_age(self, age):
        if isinstance(age, int):
            self.__age = age
        else:
            raise ValueError
    def del_age(self):
        print("删除年龄数据!")
    # 核心在这句
    age = property(get_age, set_age, del_age, "年龄")    
    obj = People("jack", 18)
    print(obj.age)
    obj.age = 19
    print("obj.age:  ", obj.age)
    del obj.age

通过语句age = property(get_age, set_age, del_age, "年龄")将一个方法伪装成为属性。其效果和装饰器的方法是一样的。

property()函数的参数:

第一个参数是方法名,调用 实例.属性 时自动执行的方法

第二个参数是方法名,调用 实例.属性 = XXX时自动执行的方法

第三个参数是方法名,调用 del 实例.属性 时自动执行的方法

第四个参数是字符串,调用 实例.属性.__doc__时的描述信息。


参考: https://www.runoob.com/w3cnote/python-func-decorators.html

https://www.cnblogs.com/cicaday/p/python-decorator.html

https://www.jb51.net/article/168276.htm

https://www.liujiangblog.com/course/python/46

https://www.cnblogs.com/yangzhen-ahujhc/p/12300189.html

http://c.biancheng.net/view/4561.html

https://blog.csdn.net/jpch89/article/details/84026130

发表评论
评论通过审核后显示。