Python装饰器abstractmethod、property、classmethod、staticmethod及自定义装饰器
总览:
@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__.
装饰器应用场景
-
引入日志
-
函数执行时间统计
-
执行函数前预备处理
-
执行函数后清理功能
-
权限校验等场景
-
缓存
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
python设计模式(十六):命令模式
“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。这就是命令模式(Command Pattern) 命令模式应该有一下几个角色: Command: 定义命令的接口,声明执行的方法,可以理解为一个基类。 ConcreteCommand: 命令接口实现对象,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 Receiver: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。 Invoker: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象,相当于使用命令对象的入口。 Client: 创建具体的命令对象,组装命令对象和接收者,或许,把这个Client称为装配者会更好理解,因为真正使用命令的客户端是从Invoker来触发执行。 示例code: #-*-coding:utf-8-*-classCommand: """声明...
- 下一篇
python设计模式(十七):迭代器模式——迭代器与生成器
迭代模式:对外提供一个接口,实现顺序访问聚合数据,但是不显示该数据的内部机制。这就是Python中大名鼎鼎的迭代器。 实现迭代模式对于Python来说没有多余的代码,寥寥几行代码足可以实现迭代模式。 示例code: #-*-coding:utf-8-*-defFibonacciSequence(n): x=0 y=1 i=1 whileTrue: yieldy ifi==n: break x,y=y,x+y i+=1if__name__=='__main__': test=FibonacciSequence(7) next(test) 1 next(test) 1 next(test) 2 next(test) 3 next(test) 5 next(test) 8 next(test) 13 next(test) 以上是使用迭代模式输出斐波那契数列的前n列,较传统的实现方法而言更加的简洁。 迭代器模式常应用场景是在只提供接口而不暴露内部机制的场景中,yield关键词在python协程中也有应用。 迭代器、生成器、可迭代对象概念 生成器:对于一个数据集合,生成器并不记住每个元素值,但在...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果