网站首页 > 技术文章 正文
Python 的魔法方法(也称为特殊方法)允许我们自定义对象的行为。这些方法通常以双下划线包围,如 __init__ 和 __str__。了解和使用这些魔法方法可以使我们的自定义类在 Python 中表现得像内置类型一样。以下是一些常用的魔法方法及其用途:
1. 对象创建与初始化
- __new__: 用于创建对象。它在 __init__ 之前调用,负责返回新的实例。
- __init__: 用于初始化新创建的对象。它在 __new__ 之后调用,负责为对象分配属性。
2. 属性访问与管理
- __getattr__: 当访问对象中不存在的属性时被调用。
- __getattribute__: 不论属性是否存在,都会被调用。
- __setattr__: 用于设置属性的值。
- __delattr__: 用于删除属性。
- __call__: 允许对象像函数一样被调用。
3. 字符串表示
- __str__: 控制 str(object) 调用时的字符串表示,通常用于生成易读的字符串。
- __repr__: 控制 repr(object) 调用时的字符串表示,通常用于生成有助于调试的字符串。
4. 数值运算
- __add__, __sub__, __mul__, __truediv__, __floordiv__: 分别处理加法、减法、乘法、真除法和整数除法(地板除法)。
- __radd__, __rsub__, __rmul__, __rtruediv__, __rfloordiv__: 反射版本,处理操作数顺序颠倒的情况。
- __iadd__, __isub__, __imul__, __itruediv__, __ifloordiv__: 处理复合赋值运算符(如 +=、-= 等)。
5. 位运算
- __invert__: 处理取反运算符 ~。
- __and__, __or__, __xor__: 分别处理按位与、按位或和按位异或运算符。
6. 矩阵运算
- __matmul__: 处理矩阵乘法运算符 @。
7. 类与继承
- __init_subclass__: 在类被子类化时调用,允许在子类创建时执行额外的初始化操作。
通过理解和使用这些魔法方法,我们可以灵活地控制自定义对象的行为,使它们能够适应各种编程需求。接下来我们详细示例一下。
一:数值运算符
默认情况下,自定义对象不支持 +、-、* 等数值运算符。可以通过定义某些魔法方法来实现这一功能:
- __add__ 定义了对象与值相加时的行为 (object + value)
- __sub__ 定义了对象与值相减时的行为 (object - value)
- __mul__ 定义了对象与值相乘时的行为 (object * value)
- __truediv__ 定义了对象与值做真除法时的行为 (object / value)
- __floordiv__ 定义了对象与值做整数除法时(地板除法)的行为 (object // value)
class Dog:
def __add__(self,x):
return f'+ {x} called'
def __sub__(self,x):
return f'- {x} called'
def __mul__(self,x):
return f'* {x} called'
def __truediv__(self,x):
return f'/ {x} called'
def __floordiv__(self,x):
return f'// {x} called'
dog = Dog()
print(dog + 2)
print(dog - 2)
print(dog * 2)
print(dog /2)
print(dog //2)
比如一个实际的例子:在处理文件路径的 Python 库 pathlib 中,我们可以使用 / 与 Path 对象一起将路径拼接在一起,尽管 / 通常是一个真除法运算符。
import pathlib
p=pathlib.Path('/a/b')
print(p)
print(p/ 'c')
print(p / 'apple')
二:反射数值运算符
对于大多数数值运算符,都有一个对应的反射版本。
- __add__ 处理 object + value
- __radd__(反射的 __add__)处理 value + object
- __sub__ 处理 object - value
- __rsub__(反射的 __sub__)处理 value - object
class Dog:
def __add__(self,x):
return f'self + {x}'
def __radd__(self,x):
return f'{x} + self'
def __sub__(self,x):
return f'self - {x}'
def __rsub__(self,x):
return f'{x} - self'
dog = Dog()
print( dog + 1 )
print( 1 + dog )
print( dog - 1 )
print( 1 - dog )
三:复合赋值运算符
上面的示例是处理 object + 5 和 5 + object,那么 object += 5 又该如何处理呢?
对于复合赋值运算符,我们也有相应的魔法方法。
- __iadd__ 处理 object += value
- __isub__ 处理 object -= value
- __imul__ 处理 object *= value
- __itruediv__ 处理 object /= value
- __ifloordiv__ 处理 object //= value
class Dog:
def __iadd__(self,x):
return f'self += {x}'
def __isub__(self,x):
return f'self -= {x}'
dog = Dog()
dog += 1
print(dog)
print(type(dog))
dog = Dog()
dog -= 1
print(dog)
print(type(dog))
四:__init__ VS __new__
__init__ 和 __new__ 都是用于初始化类对象的重要魔法方法,但它们并不相同。
- __new__ 用于创建对象。
- __init__ 在对象被 __new__ 创建后,为对象分配属性。
下面是一个同时重写 __new__ 和 __init__ 的简单示例:
class Dog:
def __new__(cls,*args,**kwargs):
print(f'running __new__ {args} {kwargs}')
return super().__new__(cls)
def __init__(self,name,age):
print('running __init__')
self.name = name
self.age = age
dog = Dog('rocky',3)
__new__ 和 __init__ 的一些显著区别
- __new__ 在 __init__ 之前运行。
- __new__ 返回实例,而 __init__ 必须返回 None。
- __new__ 创建并返回实例,而 __init__ 负责为新创建的实例分配属性(如 name、age 等)。
- __new__ 是一个类方法,而 __init__ 是一个实例方法。
什么时候使用每个方法:
- 当我们希望控制对象的创建时,使用 __new__。一个重要的用例是创建单例模式(Singleton),在这种模式下,一个类只能有一个实例。
- 我们使用 __init__ 来初始化对象,并为其分配属性。相比 __new__,我们更常使用 __init__。
五:__getattr__ VS __getattribute__ VS __delattr__
这两个方法都与通过 object.attribute 访问对象的属性有关,但它们之间有一个细微的区别。
- __getattr__ 只有在对象中不存在该属性时才会被调用。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __getattr__(self,name):
return f'{name} not found!'
dog = Dog('rocky',3)
print(dog.name)
print(dog.age)
print(dog.gender)
print(dog.breed)
- __getattribute__ 无论属性是否存在,都会被调用。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __getattribute__(self,name):
return f'{name} is returned!'
dog = Dog('rocky',3)
print(dog.name)
print(dog.age)
print(dog.gender)
print(dog.breed)
- __delattr__ 方法定义了我们如何使用 del 关键字删除属性。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __delattr__(self,name):
print(f'deleting {name}')
super().__delattr__(name)
dog = Dog('rocky',3)
del dog.name
六:__str__ VS __repr__
- __str__ 控制当我们调用 str(object) 时返回的字符串。
- __repr__ 控制当我们调用 repr(object) 时返回的字符串。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f'Dog STR( {self.name=} {self.age=} )'
def __repr__(self):
return f'Dog REPR( {self.name=} {self.age=} )'
dog = Dog('rocky',3)
print(str(dog))
print(repr(dog))
print(dog)
dog
__str__ 和 __repr__ 似乎几乎完全相同,只是调用方式不同——一个用 str(),另一个用 repr()。两者的区别在于各自的使用场景。
- __str__ 旨在为终端用户提供易于理解和人类可读的字符串表示。
- __repr__ 则更多用于调试和开发阶段。
- print 方法是调用__str__ 。
- notebook 隐式输出时是调用的__repr__ 。
七:__call__
默认情况下,自定义对象不能像函数一样被调用。
但如果我们定义了 __call__ 方法,就可以像调用函数一样调用我们的对象。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __call__(self,lastname):
print(self.name,lastname)
dog = Dog('rocky',3)
dog('test')
八:反转__invert__
我们可以使用 __invert__ 魔法方法来定义在对象前面加上 ~ 时的行为。
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __invert__(self):
return 'inverted doc'
dog = Dog('rocky',3)
~dog
Pandas 的 Series 使用 __invert__ 魔法方法,使我们能够使用 ~ 运算符来翻转布尔系列——True 变成 False,False 变成 True。
import pandas as pd
bool_series = pd.Series([True, False, True, False])
print("\nOriginal Series:")
print(bool_series)
inverted_series = ~bool_series
print("\nInverted Series:")
print(inverted_series)
九:其他位运算魔法方法
除了取反运算符(~)之外,还有其他位运算魔法方法——主要是按位与(&)、按位或(|)和按位异或(^)。
- 要重写 & 运算符,我们可以使用 __and__。
- 要重写 | 运算符,我们可以使用 __or__。
- 要重写 ^ 运算符,我们可以使用 __xor__。
class Dog:
def __and__(self, other):
return f' & {other} called!!'
def __or__(self, other):
return f' | {other} called!!'
def __xor__(self, other):
return f' ^ {other} called!!'
dog= Dog()
print( dog & 100 )
print( dog | 100 )
print( dog ^ 100 )
十:__init_subclass__
默认情况下,如果我们让一个类继承另一个类,并在类定义中添加额外的参数,我们会遇到错误。这是因为 Python 的类继承机制不允许在继承时添加额外的参数。
__init_subclass__ 是一个特殊的魔法方法,用于处理类继承时的一些自定义逻辑。它会在类被子类化时自动调用,允许我们在子类创建时执行一些额外的初始化操作或验证。
以下是一个示例,展示了如何使用 __init_subclass__ 方法:
class Base:
def __init_subclass__(cls, extra_arg=None, **kwargs):
super().__init_subclass__(**kwargs)
cls.extra_arg = extra_arg
if extra_arg is None:
raise TypeError("Must provide 'extra_arg' ")
class Derived(Base, extra_arg="This is an extra argument"):
pass
print(Derived.extra_arg)
十一:__matmul__ 用于 @ 运算符
在 Python 3.5 中,@ 运算符(矩阵乘法)被引入(与 @ 装饰器语法不同)。
如果我们在自定义类中定义了 __matmul__ 方法,我们可以在自定义对象上使用 @ 运算符进行矩阵乘法操作或其他值定义的处理。以下是一个示例代码:
class Dog:
def __matmul__(self, other):
return f' self @ {other} called!!'
Dog() @ 100
猜你喜欢
- 2025-05-22 Python 模块导入(import)实战指南
- 2025-05-22 19-3-Python-类的常用内置方法
- 2025-05-22 认识python全栈框架reflex:快速打造工具类、模型调用web应用
- 2025-05-22 Python super()函数:调用父类的构造方法
- 2025-05-22 失业程序员复习python笔记——类
- 2025-05-22 Python 中 必须掌握的 20 个核心函数及其含义,不允许你不会
- 2025-05-22 python魔法方法__call__详解
- 2025-05-22 python函数调用10种常见的语法错误
- 2025-05-22 Python常用函数整理
- 2025-05-22 Python函数调用最常用的3种方法:
- 05-25Python 3.14 t-string 要来了,它与 f-string 有何不同?
- 05-25Python基础元素语法总结
- 05-25Python中的变量是什么东西?
- 05-25新手常见的python报错及解决方案
- 05-2511-Python变量
- 05-2510个每个人都是需要知道Python问题
- 05-25Python编程:轻松掌握函数定义、类型及其参数传递方式
- 05-25Python基础语法
- 257℃Python短文,Python中的嵌套条件语句(六)
- 257℃python笔记:for循环嵌套。end=""的作用,图形打印
- 256℃PythonNet:实现Python与.Net代码相互调用!
- 251℃Python操作Sqlserver数据库(多库同时异步执行:增删改查)
- 251℃Python实现字符串小写转大写并写入文件
- 106℃原来2025是完美的平方年,一起探索六种平方的算吧
- 90℃Python 和 JavaScript 终于联姻了!PythonMonkey 要火?
- 81℃Ollama v0.4.5-v0.4.7 更新集合:Ollama Python 库改进、新模型支持
- 最近发表
- 标签列表
-
- python中类 (31)
- python 迭代 (34)
- python 小写 (35)
- python怎么输出 (33)
- python 日志 (35)
- python语音 (31)
- python 工程师 (34)
- python3 安装 (31)
- python音乐 (31)
- 安卓 python (32)
- python 小游戏 (32)
- python 安卓 (31)
- python聚类 (34)
- python向量 (31)
- python大全 (31)
- python次方 (33)
- python桌面 (32)
- python总结 (34)
- python浏览器 (32)
- python 请求 (32)
- python 前端 (32)
- python验证码 (33)
- python 题目 (32)
- python 文件写 (33)
- python中的用法 (32)