前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >python高阶教程-修饰符与面向切面编程

python高阶教程-修饰符与面向切面编程

作者头像
羽翰尘
修改2019-11-26 16:07:33
9930
修改2019-11-26 16:07:33
举报
文章被收录于专栏:技术向技术向技术向

本文由腾讯云+社区自动同步,原文地址 http://blogtest.stackoverflow.club/book/senior_python/decorator/

本篇内容来自原创小册子《python高阶教程》,点击查看目录

从面向对象编程到面向切面编程

面向对象编程(Object Oriented Programming)是编程史上的一个跨越,它完成了过程与数据的封装,使得每个类都只完成自己特有的功能,提高的代码的可重用性。面向对象编程的主要特点是继承、多态与封装。

但是,在某些情况下,面向对象编程反而会降低代码可重用性。比如在和数据库相关的操作中,每个类初始化时都要完成数据库的连接,在使用完毕后释放连接。类似的操作会造成“固定流程”的代码在类中反复出现,这就催生了面向切面编程。

面向切面编程(Aspect Oriented Program)做了很好的补充,它可以完成代码的动态切入。一般上,我们把重复使用的代码片段成为切面,而这些代码片段切入的目标成为切入点。所以,面向切面编程就是把所有可能重复的代码、固定流程的代码分离出来做成切面,在需要时切入到目标类或者目标函数中。

在python中,面向切面编程是通过修饰符完成的,修饰符的符号是@.

简单的修饰符

def decorator(func):
    print("func's name is ", func.__name__)
    return func
# way 1
def a():
    pass
a = decorator(a)
# way 2
@decorator
def b():
    pass
# run it
a()
b()

运行上述代码后,输出为

func's name is a
func's name is b

这个例子比较简单,稍微解释下。方法一和方法二是等价的, decorator函数就是在执行func之前先打印func的名字,然后返回func的函数指针,这个指针成为了新的func. 所以后续在执行func时,首先会打印函数名,然后才会执行真正的func函数体。

函数有参数

def decorator(func):
    def wrapper(*arg, **kw):
        print("func's name is", func.__name__)
        return func(*arg, **kw)
    return wrapper  
    
# way 1
def a(arg):
    print("arg of a is", arg)
a = decorator(a)
# way 2
@decorator
def b(arg):
    print("arg of b is", arg)
# run it
a(1)
b(2)

运行上述代码后,运行结果为:

func's name is a
arg of a is 1
func's name is b
arg of b is 2

这次的代码稍微复杂了一些,但是从第一种修饰的方法来看,即a = decorator(a),会好很多。在函数有参数的情况下,我们执行a(1)就相当于执行了decorator(a)(1), 这就出现了函数的嵌套。

第一层函数为decorator,参数为a, 即被修饰的函数func,返回值为函数wrapper.

第二层函数为wrapper,参数为1,返回为被修饰的函数的返回值

结合decorator(a)(1),可知decorator(a)返回wrapper,然后再调用wrapper(1), 在wrapper(1)中再调用a(1).

修饰符有参数

def decorator(arg_of_decorator):
    def second(func):
        def wrapper(*arg, **kw):
            print("arg of decorator is", arg_of_decorator)
            print("func's name is", func.__name__)
            return func(*arg, **kw)
        return wrapper
    return second
# way 1
def a(arg):
    print("arg of a is", arg)
a = decorator("arg os dec for a")(a)
# way2
@decorator("arg of dec for b")
def b(arg):
    print("arg of b is", arg)
# run it
a(1)
b(2)

运行上述代码后,输出为:

arg of decorator is arg of dec for a
func's name is a
arg of a is 1
arg of decorator is arg of dec for b
func's name is b
arg of b is 2

同样的,我们使用方法一写出完整的调用链,即decorator("arg of dec for a")(a)(1),可以看出这里的函数嵌套是三层的,因为这里有三个不同层次的参数需要传递。

第一层为decorator, 参数为”arg of dec for a”,返回值为second

第二层为second, 参数为a, 返回值为wrapper

第三层为wrapper, 参数为1,即被修饰的函数的参数,返回值为被修饰的函数的返回值

用函数修饰类

def decorator(arg_of_decorator):
    def second(class_name):
        def wrapper(*arg, **kw):
            print("class is", class_name)
            print("arg of decorator is", 
                arg_of_decorator)
            return class_name(*arg, **kw)
        return wrapper 
    return second 
@decorator("use function")
class test:
    def __init__(self, num):
        self.num = num 
    def print(self):
        print(self.num) 
ins = test(1)
ins.print()

运行上述代码后,输出为

class is <class '__main__.test'>
arg of decorator is use function
1

从调用链来看,全部代码执行完毕相当于decorator(“use function”)(test)(1).print(), 虽然连续进行了4次调用过程,但是由于最后的print是由对象调用方法来实现的,所以修饰符函数内部只有3次嵌套。

第一层是decorator,参数为”use function”, 返回值为second

第二层为second, 参数为test, 返回值为wrapper

第三层为wrapper,参数为1, 返回值为test(1)

用类修饰类

class decorator:
    def __init__(self, arg_of_decorator):
        print("dec init arg is", arg_of_decorator) 
        self.arg_of_decorator = arg_of_decorator
    def __call__(self, class_name): 
        print("dec call arg is", class_name)
        class second:
            def __init__(self,class_name):
                self.class_name = class_name 
            def __call__(self, *args):
                return self.class_name(*args)
        self.class_name = class_name 
        return second(self.class_name)
@decorator("use class")
class test:
    def __init__(self,num):
        self.num = num 
    def print(self):
        print(self.num) 
ins = test(2)
ins.print()

运行上述代码后,输出为:

dec init arg is use class
dec call arg is <class '__main__.test'>
2

完整的调用过程为decorator(“use class”)(test)(2).print(),同样的,修饰符的类有三个参数需要传递。分别是”use class”, test, 2

使用类的好处在于init方法和call方法可以各自传一个参数,由输出可知,init方法传入的是”use class”, call 方法传入的是test, 紧接着我们只需要嵌套定义一个类完成参数2的传入即可。所以使用嵌套类second,在嵌套init方法传入被修饰的类名test,在嵌套call 方法中传入参数2.

注意这里嵌套类的init方法不是必须的,即可以改为下述代码,输出不变

class decorator:
    def __init__(self, arg_of_decorator):
        print("dec init arg is", arg_of_decorator) 
        self.arg_of_decorator = arg_of_decorator
    def __call__(self, class_name): 
        print("dec call arg is", class_name)
        class second:
            def __init__(self):
                pass 
            def __call__(self, *args):
                return class_name(*args)
        self.class_name = class_name 
        return second()

那是否可以把*args参数用second类的嵌套init方法传入呢? 答案是不行。因为second类的init方法接受参数是在类的实例化也即调用类的时候完成的,这个过程在decorator类的return second中,这就要求decorator类完成三个层次的参数传入,是不可能实现的。

所以,两个嵌套类最多完成三个层次的参数传入。

最后从调用链的角度看第一个例子

如果从调用链来理解,写出调用关系,即执行a()后真正的执行过程为decorator(a)(),这里有一个函数的依次调用顺序。

第一层为decorator,参数为被修饰的函数,即func,返回值为被修饰的函数

第二层为func,即真正定义的函数。

这里不能说是两层的嵌套函数,因为decorator返回的是一个函数,而不是函数的执行结果。

那为什么不能返回函数的执行结果呢? 即如下代码:

def decorator(func):
    print("func's name is ", func.__name__)
    return func()

原因在于如果返回函数的执行结果,a = decorator(a)执行完毕后a就变成了一个数值而不是函数。

那为什么其他例子可以返回函数的执行结果呢?

因为其他例子的decorator里面有一个或者多个嵌套函数,在执行a = decorator(a)或者a= decorator(“arg”)(a)之后,返回的是一个函数的引用,decorator的最内层嵌套函数还没有执行

最内层的嵌套函数要一直等到a(1)出现时才执行。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从面向对象编程到面向切面编程
  • 简单的修饰符
  • 函数有参数
  • 修饰符有参数
  • 用函数修饰类
  • 用类修饰类
  • 最后从调用链的角度看第一个例子
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档