python装饰器详解

一、函数名的运用

函数名是⼀个变量,但它是⼀个特殊的变量。与括号配合可以执⾏函数的变量

1. 做变量

def func():
    print('呵呵')

a = func  # 把函数当成变量赋值给另外一个变量
a()  # 通过变量a调用函数

2. 做容器元素

 def func1():
    print('func1')

def func2():
    print('func2')    
   
def func3():
    print('func3')
          
def func4():
    print('func4')

list1 = [func1, func2, func3, func4]
for i in list1:
    i()

3. 做参

 def func1():
    print('func1')

def func2(arg):
    print('start')
    arg()  # 执行传递进来的arg
    print('end')

func2(func1)  # 把func1当成参数传递给func2

4. 做返回值

  def func1():
    print('这里是func1')

    def func2():
        print('这里是func2')
    return func2  # 把func2当成返回值返回

ret = func1()  # 调用func1,把返回值赋值给ret
ret()  # 调用ret

二、闭包

**1、定义:**一个内层函数中,引用了外层函数(非全局)的变量,这个内层函数就可以成为闭包。在Python中,我们可以使用__closure__来检测函数是否是闭包。
2、例子:

def print_msg(msg):
 # 这是外层函数
    def printer():
    # 这是内层函数
        print(msg)
    return printer   # 返回内层函数

func = print_msg("Hello")    #返回printer变量
func()          #实际上执行是printer()函数

现在我们进行如下操作:

>>> del print_msg
>>> func()
Hello
>>> print_msg("Hello")
Traceback (most recent call last):
...
NameError: name 'print_msg' is not defined

总结:
如果⼀个函数执⾏完毕,则这个函数中的变量以及局部命名空间中的内容都将会被销毁。在闭包中内部函数会引用外层函数的变量,而且这个变量将不会随着外层函数的结束而销毁,它会在内存中保留。也就是说,闭包函数可以保留其用到的变量的引用。

三、装饰器

1、开放封闭原则是指对扩展代码的功能是开放的,但是对修改源代码是封闭的。这样的软件设计思路可以保证我们更好的开发和维护我们的代码。
2、利用闭包解释装饰器:

def c():
    print('11111111')

def a(func):
    def b():
        print('aaaa')
        func()
    return b

c = a(c)  # 运行步骤:a(c)=>func()=c() =>返回值赋值 c=b
c()    #实际运行 b()=>打印aaa=>运行c()=>打印1111111

3、装饰器规范写法如下:

def a(func):
    def b():
        print(1111')
        func()
    return b

@a  # 装饰器语法
def c():
    print('cccccc!')
    
# 调用被装饰函数
c()

4、带参数的装饰器

def f1(func):  # f1是我们定义的装饰器函数,func是被装饰的函数
    def f2(*arg, **kwargs):  # *args和**kwargs是被装饰函数的参数
        func(*arg, **kwargs)
    return f2

5、装饰器修复技术
**(1)**被装饰的函数最终都会失去本来的__doc__等信息, Python给我们提供了一个修复被装饰函数的工具。在装饰器内部加入 @wraps(func)进行修复
(2)例子

 def a(func):
    @wraps(func)   #可以将这个除去试试
    def b():
        print('洒点水')
        func()
    return b

@a  # 装饰器语法糖
def create_people():
    """这是一个女娲造人的功能函数"""
    print('女娲真厉害,捏个泥吹口气就成了人!')

create_people()
print(create_people.__doc__)
print(create_people.__name__)

6、多个装饰器修饰一个函数执行顺序为从下至上
7、类装饰器:
(1)基础知识:__init__和__call__分别为类中的初始函数和直接调用函数,都属于魔法函数(python内置的)
(2)实例:

class D(object):
    def __init__(self, a=None):  #初始函数,调用类首先执行这个函数
        self.a = a
        self.mode = "装饰"

    def __call__(self, *args, **kwargs):
        if self.mode == "装饰":
            self.func = args[0]  # 默认第一个参数是被装饰的函数
            self.mode = "调用"
            return self
        # 当self.mode == "调用"时,执行下面的代码(也就是调用使用类装饰的函数时执行)
        if self.a:
            print("欢迎来到{}页面。".format(self.a))
        else:
            print("欢迎来到首页。")
        self.func(*args, **kwargs)

@D()
def index(name):
    print("Hello {}.".format(name))
    
if __name__ == '__main__':
    index('张三') #调用时调用类中的__call__函数

8、property属性方法
(1)我们把类中的一个只读属性定义为property属性方法,只有在访问它时才参与计算,一旦访问了该属性,我们就把这个值缓存起来,下次再访问的时候无需重新计算。
(2)例子:

class lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)
            return value

import math

class Circle:
    def __init__(self, radius):
        self.radius = radius

    @lazyproperty
    def area(self):
        print('计算面积')
        return math.pi * self.radius ** 2

c1 = Circle(10)
print(c1.area)
print(c1.area)#执行这个会发现这一步只返回了值,没有重新执行函数。

你可能感兴趣的:(python学习随笔)