文章目录
参考
项目 | 描述 |
---|---|
Python 官方文档 | https://docs.python.org/zh-cn/3/ |
搜索引擎 | Google 、Bing |
描述
项目 | 描述 |
---|---|
PyCharm | 2023.1 (Professional Edition) |
Python | 3.10.6 |
装饰器
概念
在 Python 中,装饰器(Decorators)是一种特殊的语法,用于修改 函数
或 类
的行为,以及为它们添加额外的功能。装饰器可以被认为是函数或类的 包装器
,它们接受一个函数或类作为输入,并 返回一个修改后的函数或类
。
装饰器提供了一种简洁和灵活的方式来修改函数或类的功能,而 无需
对它们的 源代码
进行 直接修改
。它们通常用于实现 横切关注点(Cross Cutting Concerns,横切关注点指的是在软件系统中与核心业务逻辑无关但又需要被多个模块或组件共享的功能或逻辑。)
的功能,例如日志记录、性能测量、输入验证等。
装饰器函数
在 装饰器模式
中,装饰器函数接受一个函数或类作为输入,并 返回
一个经过 修改
或 封装
的新 函数
或 类
,这个新函数或类通常具有额外的功能、行为或属性。
装饰器函数本身是一个函数,可以接受一个函数或类作为参数。在装饰器函数内部,通常定义一个内部函数(常将该函数称为 包装器
),用于封装或修改传入的函数或类。内部函数通常会在执行被装饰的函数或类之前或之后执行一些额外的逻辑或行为
。装饰器函数最后会将内部函数或其对象类型进行返回,以便在装饰器应用时 替换
被装饰的函数或类。
一个常见的装饰器函数结构
# 装饰器函数必须提供一个形参用以接受被
# 装饰的函数或类。否则,Python 将抛出异常错误。
def hint(func):
# 在装饰器函数内部定义一个内部函数(包装器)来
# 封装或修改传入的函数
def wrapper():
# 在执行被装饰函数前执行一段额外的逻辑
print(f'The {func.__name__} function is about to be executed.')
# 执行被装饰函数
func()
# 在执行被装饰函数后执行一段额外的逻辑
print(f'The {func.__name__} function is complete.')
# 将内部函数进行返回
return wrapper
装饰器函数的应用
装饰器语法是一种方便的语法糖,用于在 Python 中应用装饰器函数。装饰器语法使用 @
符号紧接着装饰器函数的名称,放置在要被装饰的 函数
或 类
的上方。对此,请参考如下示例:
# 装饰器函数必须提供一个形参用以接受被
# 装饰的函数或类。否则,Python 将抛出异常错误。
def hint(func):
# 在装饰器函数内部定义一个内部函数(包装器)来
# 封装或修改传入的函数
def wrapper():
# 在执行被装饰函数前执行一段额外的逻辑
print(f'The {func.__name__} function is about to be executed.')
# 执行被装饰函数
func()
# 在执行被装饰函数后执行一段额外的逻辑
print(f'The {func.__name__} function is complete.')
# 将内部函数进行返回
return wrapper
# 通过装饰器语法对 my_func() 函数应用装饰器 hint
@hint
def my_func():
print('I\'m a function of my_func.')
my_func()
执行效果
The my_func function is about to be executed.
I'm a function of my_func.
The my_func function is complete.
注:
-
在 Python 中,装饰器可以被某一个函数或类所应用。装饰器函数会在装饰器语法
被解析时立即执行,并将被装饰的函数或类作为参数传递给装饰器函数
。对此,请参考如下示例:def echo(func): print('Hello China') # 当 Python 解释器解析到装饰器语法时 # 将立即执行装饰器函数,而被装饰对象则 # 需要人为使用。因此,Hello World 将不会输出至控制台中。 @echo def inaction(): print('Hello World')
执行效果
Hello China
-
装饰器函数的返回值不一定需要为一个函数或类,也可以是 Python 支持的其他对象类型。对此,请参考如下示例:
import time def clock(func): # 记录 func() 函数开始执行的时间(以秒为单位) start_time = time.perf_counter() func() # 将 func() 函数的执行时间进行返回 return time.perf_counter() - start_time # my_func() 函数应用 clock 装饰器后,my_func # 函数将作为参数传递给装饰器函数。 @clock def my_func(): for i in range(99999999): pass print(my_func)
执行效果
1.138109399995301
成员方法
成员方法(Member Methods)
是面向对象编程中类的一部分,是定义在类中的函数。成员方法用于操作 类
或 实例对象
,并可以访问类或实例对象的属性和其他成员方法。
实例方法(Instance Methods)
实例方法是面向对象编程中定义在类中的方法,用于操作类的实例对象。实例方法是指 与实例对象相关联的方法
,通过实例对象来调用和执行。
在 Python 中,实例方法是类中定义的函数,其第一个参数 通常被约定为self
,self 引用
当前的 实例对象
。通过使用 self
参数,实例方法可以访问和操作实例对象的属性,以及调用其他 实例方法
。对此,请参考如下示例:
class MyClass:
def __init__(self):
self.name = 'RedHeart'
self.age = 18
self.sex = 'male'
def set_name(self, name):
# self.name 为该实例对象的 name 属性
# 而 name 则为该方法中的一个局部变量。
self.name = name
def rename(self, name):
# 在一个方法中通过 self 调用该实例对象的
# 另一个方法。
self.set_name(name)
# 实例化对象
redHeart = MyClass()
# 方法 redHeart 实例对象的 name 属性
print(redHeart.name)
# 通过调用实例方法间接修改实例对象的 name 属性
redHeart.rename('TwoMoons')
print(redHeart.name)
执行效果
RedHeart
TwoMoons
注:
实例方法能够被该实例对象所属的类所调用,但你必须为该实例方法的 self
形参传递一个实参,实参可以是一个实例对象,也可以是 Python 中的其他对象类型。对此,请参考如下示例:
class RedHeart:
def __init__(self):
self.name = 'RedHeart'
def rename(self, name):
self.name = name
class TwoMoons:
def __init__(self):
self.name = 'TwoMoons'
redHeart = RedHeart()
twoMoons = TwoMoons()
# 通过调用 RedHeart 类的实例方法
# 修改 TwoMoons 实例对象的名称
RedHeart.rename(twoMoons, 'BinaryMoon')
print(twoMoons.name)
执行效果
BinaryMoon
需要注意的是,仅当通过 类
调用实例方法时才能够控制 self
所代表的对象。若通过 实例对象
调用 实例方法
,则 Python 解释器将自动将该实例对象作为该实例方法的第一个实参进行传递。正是由于这一特点,Python 中的实例方法也被称为 绑定方法
。
类方法(Class Methods)
在 Python 中,类方法是与 类
相关联的方法,而不是与实例对象相关联。类方法在 类级别
上执行,而不是在 实例级别
上执行。
类方法使用 @classmethod
装饰器来进行标识,以指示该方法为类方法。类方法的第一个参数通常被 约定
为 cls
,cls
引用当前的类对象,即类本身(由此缘故,静态方法也被称为 绑定方法
),而不是该类所产生的实例对象。对此,请参考如下示例:
class MyClass:
@classmethod
def get_me(cls):
return cls
myClass = MyClass()
# 通过对象的身份标识(对象所处内存空间对应的内存地址)
# 判断两者是否为同一对象。
print(MyClass is MyClass.get_me())
print(myClass is MyClass.get_me())
执行效果
True
False
与实例方法不同
,类方法无法 直接
访问实例对象的属性,因为类方法是在没有实例对象的情况下执行的。但是,类方法可以访问和修改类级别的属性,以及执行与类相关的操作。对此,请参考如下示例:
class MyClass:
# 类级别(与类相绑定)的属性
name = 'MyClass'
def __init__(self):
# 实例对象级别(与实例对象相绑定)的属性
self.name = 'MyClass'
@classmethod
def set_name(cls, name):
cls.name = name
@classmethod
def rename(cls, name):
# 在一个类方法中尝试通过 cls 调用另一个类方法
cls.set_name(name)
# 尝试访问类级别的 name 属性
print(MyClass.name)
myClass = MyClass()
# 尝试通过类方法修改类级别的属性
MyClass.rename('RedHeart')
# 尝试访问类级别和实例对象级别的属性
print(MyClass.name)
print(myClass.name)
执行效果
MyClass
RedHeart
MyClass
注:
类的实例对象能够调用该类的类方法,但无法修改类方法中 cls
参数的值。
静态方法(Static Methods)
静态方法是定义在类中的方法,与实例对象和类对象无关
。静态方法在类级别上执行,而不是在实例级别上执行。静态方法 不会自动接收
实例对象或类对象作为参数(由此缘故,静态方法也被称为 非绑定方法
),因此在方法内部 无法直接访问
实例对象的属性或类对象的属性。
静态方法使用 @staticmethod
装饰器进行标识,它们在定义时 不需要额外的特殊参数
。静态方法可以直接通过类名调用,也可以通过类的实例对象调用。对此,请参考如下示例:
class MyClass:
@staticmethod
def echo():
print('Hello World')
myClass = MyClass()
myClass.echo()
MyClass.echo()
执行效果
Hello World
Hello World