python基础(15)装饰器@

装饰器@

函数装饰器

内置函数装饰器

@staticmethod

被@staticmethod修饰的方法是静态方法,从而可以实例化使用 C().f(),也可以不实例化调用该方法 C.f()

image-20200923183403338

@classmethod

@classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。

如下,被@classmethod修饰后,访问类的函数不需要实例化:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class A(object):
    bar = 1
    def func1(self):  
        print ('foo') 
    @classmethod
    def func2(cls):
        print ('func2')
        print (cls.bar)
        cls().func1()   # 调用 foo 方法
 
A.func2()   # 不需要实例化

不使用@classmethod修饰与使用@classmethod修饰的区别:

image-20200923184232563

@property

@property可用来创建私有属性,可把一个方法变成属性来调用:

image-20200923201144148

使用@property后,get_score方法变成了一个int属性,不能再加括号来调用。

可对比加@property和不加@property时,print(type(sc.get_score))的区别,一个是<class 'int'>,另一个是<class 'method'>

函数装饰器的原理

假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示:

#funA 作为装饰器函数
def funA(fn):
    #...
    fn() # 执行传入的fn参数
    #...
    return '...'

@funA
def funB():
    #...

实际上,上面程序完全等价于下面的程序:

def funA(fn):
    #...
    fn() # 执行传入的fn参数
    #...
    return '...'

def funB():
    #...

funB = funA(funB)

其底层执行了如下 2 步操作:

  1. B 作为参数传给 A() 函数;
  2. A() 函数执行完成的返回值反馈回 B

实例:

#funA 作为装饰器函数
def funA(fn):
    print("C语言中文网")
    fn() # 执行传入的fn参数
    print("http://c.biancheng.net")
    return "装饰器函数的返回值"

@funA
def funB():
    print("学习 Python")

程序执行流程为:

image-20200923210418004

带参数的函数装饰器

在分析 funA() 函数装饰器和 funB() 函数的关系时,细心的读者可能会发现一个问题,即当 funB() 函数无参数时,可以直接将 funB 作为 funA() 的参数传入。但是,如果被修饰的函数本身带有参数,那应该如何传值呢?

比较简单的解决方法就是在函数装饰器中嵌套一个函数,该函数带有的参数个数和被装饰器修饰的函数相同。例如:

def funA(fn):
    # 定义一个嵌套函数
    def say(arc):
        print("Python教程:",arc)
    return say

@funA
def funB(arc):
    print("funB():", a)
funB("http://c.biancheng.net/python")

程序执行结果为:

image-20200923210654515

显然,通过 funB() 函数被装饰器 funA() 修饰,funB 就被赋值为 say。这意味着,虽然我们在程序显式调用的是 funB() 函数,但其实执行的是装饰器嵌套的 say() 函数。

但还有一个问题需要解决,即如果当前程序中,有多个(≥ 2)函数被同一个装饰器函数修饰,这些函数带有的参数个数并不相等,怎么办呢?

最简单的解决方式是用 *args**kwargs 作为装饰器内部嵌套函数的参数,*args**kwargs 表示接受任意数量和类型的参数。举个例子:

def funA(fn):
    # 定义一个嵌套函数
    def say(*args,**kwargs):
        fn(*args,**kwargs)
    return say

@funA
def funB(arc):
    print("C语言中文网:",arc)

@funA
def other_funB(name,arc):
    print(name,arc)
funB("http://c.biancheng.net")
other_funB("Python教程:","http://c.biancheng.net/python")

运行结果为:

image-20200923211007339

函数装饰器可以嵌套:

上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:

@funA
@funB
@funC
def fun():
    #...

上面程序的执行顺序是里到外,所以它等效于下面这行代码:

fun = funA( funB ( funC (fun) ) )
posted @ 2020-08-31 00:28  Whatever_It_Takes  阅读(118)  评论(0编辑  收藏  举报