python基础(15)装饰器@
装饰器@
函数装饰器
内置函数装饰器
@staticmethod
被@staticmethod
修饰的方法是静态方法,从而可以实例化使用 C().f()
,也可以不实例化调用该方法 C.f()
。
@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
修饰的区别:
@property
@property
可用来创建私有属性,可把一个方法变成属性来调用:
使用@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
步操作:
- 将
B
作为参数传给A()
函数; - 将
A()
函数执行完成的返回值反馈回B
。
实例:
#funA 作为装饰器函数
def funA(fn):
print("C语言中文网")
fn() # 执行传入的fn参数
print("http://c.biancheng.net")
return "装饰器函数的返回值"
@funA
def funB():
print("学习 Python")
程序执行流程为:
带参数的函数装饰器:
在分析 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")
程序执行结果为:
显然,通过 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")
运行结果为:
函数装饰器可以嵌套:
上面示例中,都是使用一个装饰器的情况,但实际上,Python
也支持多个装饰器,比如:
@funA
@funB
@funC
def fun():
#...
上面程序的执行顺序是里到外,所以它等效于下面这行代码:
fun = funA( funB ( funC (fun) ) )