Python装饰器
装饰函数
Python装饰器类似与Rust的过程宏(Procedural Macro),用于动态生成代码 。其基础语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def decorator_function (original_function ): def wrapper (*args, **kwargs ): before_call_code() result = original_function(*args, **kwargs) after_call_code() return result return wrapper @decorator_function def target_function (arg1, arg2 ): pass
装饰器本身也可以接受参数,此时需要再额外定义一个函数,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 def repeat (num_times ): def decorator (func ): def wrapper (*args, **kwargs ): for _ in range (num_times): func(*args, **kwargs) return wrapper return decorator @repeat(3 ) def say_hello (): print ("Hello!" ) say_hello()
装饰类
装饰器还可以用于生成类的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def log_class (cls ): """类装饰器,在调用方法前后打印日志""" class Wrapper : def __init__ (self, *args, **kwargs ): self .wrapped = cls(*args, **kwargs) def __getattr__ (self, name ): """拦截未定义的属性访问,转发给原始类""" return getattr (self .wrapped, name) def display (self ): print (f"调用 {cls.__name__} .display() 前" ) self .wrapped.display() print (f"调用 {cls.__name__} .display() 后" ) return Wrapper @log_class class MyClass : def display (self ): print ("这是 MyClass 的 display 方法" ) obj = MyClass() obj.display()
内置装饰器
@staticmethod定义类的静态方法
@classmethod类方法,类似与静态方法,第一个参数是类本身(通常命名为 <font style="color:rgb(51, 51, 51);background-color:rgb(250, 252, 253);">cls</font>,常用于实现构造器相关用法,实现单例模式)
<font style="color:rgb(51, 51, 51);background-color:rgb(250, 252, 253);">@property</font>将方法转换为属性,使其可以像属性一样访问
<font style="color:rgb(51, 51, 51);background-color:rgb(250, 252, 253);">@$attr.getter / @$attr.setter</font>类属性的 getter 和 setter
类
python 中所有的类都继承自 object 类,不管你是是否显示标记出 class MyClass(object)。
Python object 类的方法详解
在 Python 中,object 是所有类的基类,它提供了一系列内置方法作为所有对象的默认实现。理解这些方法对于掌握 Python 的面向对象编程至关重要。
object 类的主要方法
对象表示方法
这些方法控制对象的字符串表示形式:
__str__(self)
返回对象的"非正式"字符串表示(面向用户)
print(obj) 或 str(obj) 时调用
1 2 3 4 5 6 class Person : def __str__ (self ): return "Person Object" p = Person() print (p)
__repr__(self)
返回对象的"正式"字符串表示(面向开发者)
在 REPL 或 repr(obj) 时调用
1 2 3 4 5 6 class Person : def __repr__ (self ): return "<Person instance>" p = Person() repr (p)
比较方法
实现对象比较操作:
__eq__(self, other)
定义 == 操作符行为
__ne__(self, other)
定义 != 操作符行为(默认使用 __eq__ 的否定)
__lt__(self, other)
定义 < 操作符行为(小于)
__le__(self, other)
定义 <= 操作符行为(小于等于)
__gt__(self, other)
定义 > 操作符行为(大于)
__ge__(self, other)
定义 >= 操作符行为(大于等于)
哈希与布尔值
__hash__(self)
返回对象的哈希值(用于字典键、集合成员)
1 2 3 4 5 6 7 8 9 class Book : def __init__ (self, isbn ): self .isbn = isbn def __hash__ (self ): return hash (self .isbn) book = Book("978-0134757599" ) hash (book)
__bool__(self)
定义对象的布尔值(bool(obj) 或 if obj 时调用)
1 2 3 4 5 6 7 8 9 10 class Account : def __init__ (self, balance ): self .balance = balance def __bool__ (self ): return self .balance > 0 acc = Account(100 ) if acc: print ("Account has funds" )
属性访问方法
__getattribute__(self, name)
属性访问拦截器(每次属性访问都调用)
1 2 3 4 class LoggedAccess : def __getattribute__ (self, name ): print (f"Accessing: {name} " ) return super ().__getattribute__(name)
__getattr__(self, name)
当属性不存在时调用
1 2 3 class DynamicAttributes : def __getattr__ (self, name ): return f"Property {name} doesn't exist"
__setattr__(self, name, value)
设置属性时调用
1 2 3 4 5 class ValidatedSet : def __setattr__ (self, name, value ): if name == "age" and value < 0 : raise ValueError("Age cannot be negative" ) super ().__setattr__(name, value)
__delattr__(self, name)
删除属性时调用
类创建方法
__init_subclass__(cls)
当子类被创建时调用(类方法)
1 2 3 4 5 6 7 class Base : def __init_subclass__ (cls, **kwargs ): print (f"Subclass created: {cls.__name__} " ) cls.registry = [] class Child (Base ): pass
其他重要方法
__dir__(self)
返回对象的属性列表(dir(obj) 时调用)
1 2 3 4 5 6 class CustomDir : def __dir__ (self ): return ['name' , 'age' , 'city' ] obj = CustomDir() dir (obj)
__sizeof__(self)
返回对象在内存中的大小(字节)
1 2 import syssys.getsizeof(object ())
__format__(self, format_spec)
定义格式化输出(format(obj) 时调用)
1 2 3 4 5 6 7 8 9 10 11 12 class Temperature : def __init__ (self, celsius ): self .celsius = celsius def __format__ (self, spec ): if spec == 'f' : return f"{(self.celsius * 9 /5 ) + 32 :.2 f} °F" return f"{self.celsius} °C" t = Temperature(25 ) print (f"{t} " ) print (f"{t:f} " )
方法使用场景总结
方法类别
主要方法
使用场景
对象表示
__str__, __repr__
打印对象、调试输出
比较操作
__eq__, __lt__ 等
对象比较、排序
类型转换
__bool__, __hash__
布尔上下文、字典键
属性管理
__getattr__, __setattr__
动态属性、属性验证
类构造
__init_subclass__
类注册、元编程
内省/反射
__dir__, __class__
检查对象属性、类型
私有方法
类的私有方法和私有属性使用 __两个下划线开头
受保护类型的方法和私有属性使用_单个下划线开头(“受保护”意味着只有自身和子类可以访问)
专有方法
构造和析构函数:
init : 构造函数,在生成对象时调用
del : 析构函数,释放对象时使用
当自定义的类是类似于数据结构的类型:
setitem : 按照索引赋值
getitem : 按照索引获取值
重载运算符:
len : 获得长度
cmp : 比较运算
call : 函数调用
add : 加运算
sub : 减运算
mul : 乘运算
truediv : 除运算
mod : 求余运算
pow : 乘方
项目管理
Python中一个独立的.py文件就是一个单独的模块(module),而一个单独的文件夹就是一个包(package)。
1 2 3 4 my_package/ # 包名 = 目录名 ├── __init__.py ├── module1.py # 模块:my_package.module1 └── module2.py # 模块:my_package.module2
模块
一个模块可以包含多个函数,多个类,并且在其他文件中,可以通过_module_._function_/_module_._class_来导入。(或者是使用from _module_ import _function/class_)
每一个模块都有一个 __name__的属性——如果该文件作为模块使用,则__name__的值就是这个文件的文件名;如果直接运行该文件,则__name__为"__main__"。
1 2 if __name__ == "__main__" : main()
包
一个包(package)可以包括多个模块,多个子包(sub-package),以及一个 **__init__.py****文件。**任何包含__init__.py的文件夹都会被视作一个python的包,而没有这个文件的文件夹就只是一个普通的目录。
__init__.py用与导入包时候的初始化,其中的代码会在包导入的时候执行,通常用于初始化包级变量、设置包的环境、执行启动代码…
__init__.py中可以设置一个特殊的变量__all__,例如__all__ = ["module1", "helper"] ,所有__all__变量中包含的模块、函数、类都可以使用 from package import *一次性导入。例如:
1 2 3 4 5 6 7 8 9 from .basic import add, subtractfrom .advanced import sqrt__all__ = ["add" , "subtract" , "sqrt" ]
即使__init__.py是一个空文件,仍然标志着该文件夹是一个有效的包。Python 3.3+ 支持"命名空间包",允许没有 __init__.py 的包,但显式创建仍是推荐做法。
*args 和 **kwargs
*args是可变位置参数,用于接收任意数量的位置参数(positional arguments) ,并且把他们储存成一个元组 。
**kwargs是可变关键字参数,用于接收任意数量的关键字参数(keyword arguments) ,并且把他们储存成一个字典 。
1 2 3 4 5 6 7 8 9 10 11 def func (*args ): print ("位置参数:" , args) func(1 , 2 , 3 ) func("a" , "b" ) def func (**kwargs ): print ("关键字参数:" , kwargs) func(a=1 , b=2 ) func(name="Alice" )