总览:
@abstractmethod:抽象方法,含abstractmethod方法的类不能实例化,继承了含abstractmethod方法的子类必须复写所有abstractmethod装饰的方法,未被装饰的可以不重写
@ property:方法伪装属性,方法返回值及属性值,被装饰方法不能有参数,必须实例化后调用,类不能调用
@ classmethod:类方法,可以通过实例对象和类对象调用,被该函数修饰的方法第一个参数代表类本身常用cls,被修饰函数内可调用类属性,不能调用实例属性
@staticmethod:静态方法,可以通过实例对象和类对象调用,被装饰函数可无参数,被装饰函数内部通过类名.属性引用类属性或类方法,不能引用实例属性
案例讲解:
@abstractmethod
用于程序接口的控制,正如上面的特性,含有@abstractmethod修饰的父类不能实例化,但是继承的子类必须实现@abstractmethod装饰的方法
# -*- coding:utf-8 -*-
from abc import ABC, abstractmethod
class A(ABC):
@abstractmethod
def test(self):
pass
class B(A):
def test_1(self):
print("未覆盖父类abstractmethod")
class C(A):
def test(self):
print("覆盖父类abstractmethod")
if __name__ == '__main__':
a = A()
b = B()
c = C()
前两个分别报错如下:
a = A()
TypeError: Can't instantiate abstract class A with abstract methods test
b = B()
TypeError: Can't instantiate abstract class B with abstract methods test
第三个实例化是正确的
@ property
将一个方法伪装成属性,被修饰的特性方法,内部可以实现处理逻辑,但对外提供统一的调用方式,实现一个实例属性的get,set,delete三种方法的内部逻辑,具体含义看示例code。
# -*- coding:utf-8 -*-
# -*- coding:utf-8 -*-
class Data:
def __init__(self):
self.number = 123
@property
def operation(self):
return self.number
@operation.setter
def operation(self, number):
self.number = number
@operation.deleter
def operation(self):
del self.number
@ classmethod,staticmethod
类方法classmethod和静态方法staticmethod是为类操作准备,是将类的实例化和其方法解耦,可以在不实例化的前提下调用某些类方法。两者的区别可以这么理解:类方法是将类本身作为操作对象,而静态方法是独立于类的一个单独函数,只是寄存在一个类名下。类方法可以用过类属性的一些初始化操作。
# -*- coding:utf-8 -*-
class Test:
num = "aaaa"
def __init__(self):
self.number = 123
@classmethod
def a(cls, n):
cls.num = n
print(cls.num)
@classmethod
def b(cls, n):
cls.a(n)
@classmethod
def c(cls, n):
cls.number = n
@staticmethod
def d(n):
Test.b(n)
分别通过类对象、实例化对象来调用
装饰器函数机制
谈装饰器的原理就不得不先理解Python的闭包,在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包即内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
装饰器是建立在闭包的基础上,将被装饰函数传入闭包函数中执行则形成了装饰器。
闭包:
# -*- coding:utf-8 -*-
def test(a):
def add(a):
print(a+2)
return add(a)
if __name__ == '__main__':
test(2)
2
装饰器原型:
# -*- coding:utf-8 -*-
def B(fn):
def b():
return fn()+3
return b
@B
def add():
return 3
if __name__ == '__main__':
str = fn()
print(str)
----------------
6
当被装饰函数带参数:
# -*- coding:utf-8 -*-
def B(fn):
def b(*args, **kwargs):
return str(fn(*args, **kwargs))+"装饰"
return b
@B
def fn(*args, **kwargs):
num = 0
for i in args:
num +=i
for i in kwargs.values():
num += i
return num
if __name__ == '__main__':
num = fn(1, 2, 3, a=4, b=5)
print(num)
----------------
15装饰
当装饰函数带额外参数,应该在装饰函数外再包裹一层函数
# -*- coding:utf-8 -*-
def A(n):
def B(fn):
def b(*args, **kwargs):
return n+str(fn(*args, **kwargs))+"装饰"
return b
return B
@A("包裹前缀")
def fn(*args, **kwargs):
num = 0
for i in args:
num +=i
for i in kwargs.values():
num += i
return num
if __name__ == '__main__':
num = fn(1, 2, 3, a=4, b=5)
print(num)
----------------
包裹前缀15装饰
当多个装饰函数装饰一个函数时的执行顺序
# -*- coding:utf-8 -*-
def A(fn):
print(1)
def run():
print(2)
fn()
print('a')
return run
def B(fn):
print(3)
def run():
print(4)
fn()
print('b')
return run
def C(fn):
print(5)
def run():
print(6)
fn()
print('c')
return run
@A
@B
@C
def test():
print(7)
if __name__ == '__main__':
test()
----------------
5
c
3
b
1
a
2
4
6
7
由此可以得出多装饰情况下的运行情况:初始化运行从下到上,内部执行顺序从内到外。因为开始执行时会按照装饰顺序,组装成一个函数,而这个函数从最外层的return执行到最内层,故有上述顺序,就像剥洋葱一样。
类装饰器
# -*- coding:utf-8 -*-
class Add:
def __init__(self, fn):
print("初始化")
self.num = 44
self.fn = fn
def __call__(self, *args, **kwargs):
print("类装饰器开始工作")
return self.fn(self.num)
@Add
def test(n):
return 4+n
if __name__ == '__main__':
num = test()
print(num)
----------------
初始化
类装饰器开始工作
48
类装饰器使用地方较少,核心是通过复写类的回调方法__call__.
装饰器应用场景
-
引入日志
-
函数执行时间统计
-
执行函数前预备处理
-
执行函数后清理功能
-
权限校验等场景
-
缓存