前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​Python高性能计算之修饰符

​Python高性能计算之修饰符

作者头像
猫叔Rex
发布2022-01-24 13:37:41
5640
发布2022-01-24 13:37:41
举报
文章被收录于专栏:科学计算科学计算

  修饰符(有的也翻译成装饰符、装饰器,英文叫decorator)是Python中一个很有意思的feature,它可以向已经写好的代码中添加功能。这其实也叫元编程,因为程序的一部分在编译的时候尝试修改程序的另一部分。

高阶函数

  在学习Python的修饰符前,我们要知道几个a概念,首先是Python中的所有东西都是对象,所有我们定义的变量、类甚至与于函数,都是对象。函数是对象是个什么概念呢?就是函数之间可以相互赋值:

代码语言:javascript
复制
def first(msg):
    pritn(msg)

first("Hello")
>>> Hello

second = first
second("Hello")
>>> Hello

  上面的firstsecond都是指向同一个函数对象的。这种直接函数名之间的直接赋值,在C++中是不支持的。

  当然,函数也可以当作参数传递给其他函数,最常见的就是mapfilterreduce这三个函数了。

代码语言:javascript
复制
def func(x):
    return x*2
list(map(func, [1,2,3]))
>>>[2,4,6]
代码语言:javascript
复制
list(map(lambda x: x * 2, range(1,4)))
>>>[2,4,6]

下面我们自定义一个函数,其参数也是函数:

代码语言:javascript
复制
def add(x):
    return x + 1

def test(func, x):
    return add(x)

执行

代码语言:javascript
复制
test(add, 2)
>>> 3

在Python中,把其他函数当做参数的函数,叫做高阶函数

嵌套函数

  就是nested function,在函数中定义函数,这个我们之前写过,直接放上之前的链接:Python嵌套函数 闭包

Python修饰符

  下面回归正题,来讲Python修饰符。在Python中,使用@来表示修饰符,但这里我们先看下不使用@来完成一个修饰函数。

代码语言:javascript
复制
def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner

def ordinary():
    print("I am ordinary")

pretty = make_pretty(ordinary)
pretty()
>>> I got decorated
    I am ordinary

  在这个例子中,make_pretty()就是一个修饰器,在pretty = make_pretty(ordinary)语句中,ordinary函数被修饰,返回的函数赋值给了pretty。所以修饰器就像对函数又进行了一层的包装,下面来看使用修饰符@来对函数进行修饰,只需在定义函数的上一行加上@func即可。

代码语言:javascript
复制
def make_pretty(func):
    def inner():
        print("I got decorated")
        func()
    return inner

@make_pretty
def ordinary():
    print("I am ordinary")

ordinary()
>>> I got decorated
    I am ordinary

  修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。只可以在模块或类定义层内对函数进行修饰,不允许修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

  本质上讲,修饰符@类似于回调函数,把其它的函数作为自己的入口参数,在目的函数执行前,执行一些自己的操作,然后返回目的函数。当Python解释器读取到修饰符时,会调用修饰符的函数,来看下面的例子(这个例子只是为了解释Python读取到修饰符时会直接调用,这个修饰函数并没有返回目的函数)

代码语言:javascript
复制
# test.py
def test(func):
    print('This is test function step1')
    func()
    print('This is test function step2')

@test
def func():
    print('This is func...')

代码打印如下:

代码语言:javascript
复制
This is test function step1
This is func...
This is test function step2

这段代码中只是定义了两个函数,并没有调用它们,但仍然会有结果打印出来。

  当被修饰的函数中含有参数时,应该怎么来写呢?

  我们定义一个除法函数,参数a和b,返回a/b的结果。

代码语言:javascript
复制
def divide(a,b)
    return a / b

我们知道除法的除数不能是0,因此当我们令b=0时,就会报错。

代码语言:javascript
复制
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero

  所以我们需要增加判断机制,当除数为0时,需要告诉用户不能执行。但divide()函数中我们就只想完成除法的功能,判断机制就可以通过修饰符来完成。

代码语言:javascript
复制
def smart_divide(func):
    def inner(a, b):
        print("I am going to divide", a, "and", b)
        if b == 0:
            print("Whoops! cannot divide")
            return

        return func(a, b)
    return inner

@smart_divide
def divide(a, b):
    print(a/b)

可以看到,在修饰符函数内容的嵌套函数inner()中,嵌套函数的参数依然的(a,b),返回值也同样是func(a,b),这就是包含参数的函数被修饰时的写法。

代码语言:javascript
复制
divide(2,5)
>>> I am going to divide 2 and 5
    0.4

divide(2,0)
>>> I am going to divide 2 and 0
    Whoops! cannot divide

一般的,我们定义函数的参数可以设为*args, **kwargs,其中args表示位置参数,位置很重要,是以列表形式呈现;*kwargs:关键字参数,位置不重要。字典形式呈现。

代码语言:javascript
复制
def works_for_all(func):
    def inner(*args, **kwargs):
        print("I can decorate any function")
        return func(*args, **kwargs)
    return inner

@works_for_all
def func(*args, **kwargs):
    print(args)
    print(kwargs)

调用示例如下:

代码语言:javascript
复制
func(1,2,a=3,b=4)
>>> I can decorate any function
    (1, 2)
    {'a': 3, 'b': 4}

一个函数的多个修饰符

  Python中,一个函数可以有多个修饰符。

代码语言:javascript
复制
def star(func):
    def inner(*args, **kwargs):
        print("*" * 30)
        func(*args, **kwargs)
        print("*" * 30)
    return inner

def percent(func):
    def inner(*args, **kwargs):
        print("%" * 30)
        func(*args, **kwargs)
        print("%" * 30)
    return inner

@star
@percent
def printer(msg):
    print(msg)

printer("Hello")

打印结果如下:

代码语言:javascript
复制
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************

其实上面的:

代码语言:javascript
复制
@star
@percent
def printer(msg):
    print(msg)

就等效于下面的写法:

代码语言:javascript
复制
def printer(msg):
    print(msg)
printer = star(percent(printer))

因此执行的步骤为:

  • printer函数传入到percent函数中,percent函数直接返回inner函数给star函数
  • star函数中,传入的参数funcpercent的内嵌函数inner,此时在starinner中,先执行print("*" * 30),再执行func(),也就是会进入到percent->inner中执行
  • 此时将执行print("%" * 30),再执行func(),此时的funcprinter
  • 再执行percent->inner中的print("%" * 30),返回后又到了star->inner中,执行print("*" * 30)

写的有点绕,看下面的图会更加清晰,从左到右执行。

总结

  下面来总结一下修饰符的要点:

  1. 修饰符必须出现在函数定义前一行,不允许和函数定义在同一行;
  2. 修饰符的内嵌函数的参数跟被修饰的函数参数相同,内嵌函数的返回值跟被修饰函数的定义表达式一样
  3. 一个函数可以用多个修饰符,执行顺序是参考上图。
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 傅里叶的猫 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 高阶函数
  • 嵌套函数
  • Python修饰符
  • 一个函数的多个修饰符
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档