前言:
Python 中的魔术方法(Magic Methods),也称作特殊方法(Special Methods),是那些被双下划线包围的方法,例如 init。这些方法在 Python 中有特殊的含义,它们并不需要直接调用,而是在特定的情境下自动触发。通过定义或重写这些方法,可以自定义对象的行为,包括迭代、函数调用、属性访问等多种操作。
1.基本的魔术方法
1.1 有关属性
2 常见的几个魔术方法:
2.1 对象初始化:init(self[, …])
__init__我们很熟悉了,它在对象初始化的时候调用,我们一般将它理解为"构造函数"。这个方法在新建对象并进行初始化时被调用。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person('Alice', 30)
print(person.name) # Alice
2.1.1 new 和__init__的解释
new 方法
new 是一个静态方法(虽然通常不需要显式声明为 @staticmethod),它负责创建并返回一个新的实例。也就是说,在对象的创建过程中,new 方法会被首先调用。
new 方法的第一个参数是类本身(通常命名为 cls),该方法应该返回类的一个实例。
对于不可变类型,比如整型、字符串和元组等,修改实例是不可能的,所以这些类型通常都需要通过重写 new 方法来进行实例的创建。
init 方法
init 方法用于实例的初始化操作。当 new 方法创建一个实例并返回时,这个实例随即被传递给 init 方法的 self 参数,以执行初始化。
init 不应该返回任何值,它的返回值被忽略。
调用顺序:
首先,new 方法被调用创建实例。
然后,创建出的实例(即 new 的返回值)被传递给 init 方法的 self 参数。
最后,init 方法初始化实例。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class MyClass(object):
def __new__(cls, *args, **kwargs):
print("Creating instance")
instance = super(MyClass, cls).__new__(cls)
return instance
def __init__(self, value):
print("Initializing instance")
self.value = value
# 创建 MyClass 的一个实例
obj = MyClass("Hello")
# Creating instance
# Initializing instance
在这个例子中,当尝试创建 MyClass 的一个实例时,首先调用 new 方法来创建一个新的实例。创建成功后,这个新创建的实例会被自动传递给 init 方法以完成初始化操作。因此,你可以看到首先打印了 “Creating instance”,随后打印了 “Initializing instance”。
在大多数情况下,开发者无需重写 new 方法,只需关注 init 方法来初始化类的实例即可。但在处理不可变数据类型或者想要控制实例创建过程的高级用法时,可能需要自定义 new 方法。
2.2 字符串表现形式:str(self) 和 repr(self)
str 当你打印或者使用 str() 转换对象时被调用。
repr 用来给开发者看的字符串表现形式,它的目标是清晰无歧义。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person(name={self.name}, age={self.age})"
def __repr__(self):
return f"Person({self.name!r}, {self.age!r})"
person = Person('Alice', 30)
print(person) # Person(name=Alice, age=30)
print(repr(person)) # Person('Alice', 30)
2.3 属性访问:
getattr(self, name), setattr(self, name, value) 和 delattr(self, name)
这些方法负责管理属性的访问、设置和删除。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __getattr__(self, name):
return f"{name} not found"
def __setattr__(self, name, value):
self.__dict__[name] = value
def __delattr__(self, name):
del self.__dict__[name]
rec = Record(name="Alice", age=30)
print(rec.name) # Alice
rec.location = "New York"
print(rec.location) # New York
del rec.location
# print(rec.location) # Raises "location not found" due to __getattr__
2.4 算术运算符重载:
如 add(self, other), sub(self, other) 等
允许你定义对象在加减乘除等算术运算中的行为。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Length:
def __init__(self, value):
self.value = value
def __add__(self, other):
return Length(self.value + other.value)
l1 = Length(5)
l2 = Length(3)
l3 = l1 + l2
print(l3.value) # 8
2.5 容器类型方法:
len(self), getitem(self, key), setitem(self, key, value), delitem(self, key) 等
这使得对象可以表现得像容器类型一样,支持 len(), []访问等。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Shelf:
def __init__(self):
self._items = {}
def __setitem__(self, key, value):
self._items[key] = value
def __getitem__(self, key):
return self._items.get(key, None)
def __delitem__(self, key):
del self._items[key]
shelf = Shelf()
shelf['book'] = 'Python Programming'
print(shelf['book']) # Python Programming
del shelf['book']
2.6 call(self, [args…])
该方法允许类的实例跟函数一样表现,call 是 Python 中的一个特殊方法,它使得一个对象可以像函数一样被调用。当你尝试像调用函数一样调用一个对象时,如果这个对象的类中定义了 call 方法,那么这个 call 方法会被执行。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x):
return self.n + x
# 创建一个 Adder 实例,它会把传入的参数增加 10
adder = Adder(10)
# 由于定义了 __call__ 方法,我们可以将 adder 当作是函数来调用
result = adder(5) # 相当于调用 adder.__call__(5)
print(result) # 输出 15
在这个例子中,我们定义了一个类 Adder,它有一个初始化方法和一个 call 方法。call 方法接收一个参数 x 并且返回 self.n + x。当我们创建了一个 Adder 实例 adder 并且传入了初始值 10,这个实例就可以像一个函数那样被调用。调用 adder(5) 实际上会执行 adder.call(5),最终返回值是 15。
call 方法的使用场景主要是想使得实例的行为更类似函数或者是定义可调用对象,同时保留存储状态等功能,这在使用类装饰器或者设计类似函数式编程中的函数对象时会特别有用。
2.7 format(self, [args…])
format() 函数在 Python 中用于格式化输出,它允许你控制数据的显示形式。当你用 format(your_object) 调用它时,实际上会去查找 your_object 对应类的 format 魔术方法并调用它。你可以在自己的类中定义 format 方法来自定义格式化行为。
#!/usr/bin/env python
# coding=utf-8
"""
# @Time : 2024/4/7 8:25
# @Author : Summer
# @File : test.py
# @describe:
"""
class Order:
def __init__(self, order_id, price):
self.order_id = order_id
self.price = price
def __format__(self, format_spec):
if format_spec == 'id':
return f'Order ID: {self.order_id}'
elif format_spec == 'price':
return f'Order Price: ${self.price:.2f}'
else:
return f'Order ID: {self.order_id}, Price: ${self.price:.2f}'
# 创建一个 Order 对象
order = Order('123', 99.99)
# 使用 format() 进行格式化
formatted_order_id = format(order, 'id')
formatted_order_price = format(order, 'price')
formatted_order_default = format(order) # 没有指定 format_spec,或者 format_spec 为空字符串
print(formatted_order_id) # 输出 Order ID: 123
print(formatted_order_price) # 输出 Order Price: $99.99
print(formatted_order_default) # 输出 Order ID: 123, Price: $99.99
在这个例子中,我们定义了一个 Order 类,它有一个 format 方法,该方法接收一个参数 format_spec,它是一个字符串,指示如何格式化该对象。根据 format_spec 的值,format 返回不同的字符串。如果 format_spec 是 ‘id’,则只输出订单 ID;如果是 ‘price’,则只输出价格;如果 format_spec 不是这两种情况,它就会输出订单 ID 和价格。
这样,无论是直接调用 Python 内置的 format() 函数还是在字符串格式化时使用冒号 : 后跟格式说明符 {my_object:format_spec},都会使用对象的 format 方法进行格式化。
总结
对象的创建和销毁:
控制对象的创建 (new)、初始化 (init) 和销毁 (del) 的过程。
对象表示:
定制对象的字符串表示,包括通过 str 提供给用户的可打印版本,以及通过 repr 提供给开发者的官方字符串表示。
属性访问管理:
管理和自定义对象属性的访问、设置和删除 (getattr, setattr, delattr 等)。包括支持点号(.)运算符进行属性访问和动态属性。
容器类型模拟:
使自定义对象的行为类似于内置的容器类型,如序列和映射(getitem, setitem, delitem, len 等)。
算术和比较操作符重载:
定义对象如何相互作用,并对算术运算 (add, mul, 等) 和比较运算 (lt, eq, 等) 进行重载。
可调用对象:
通过 call 方法,将实例变为可调用,即像函数那样可以执行。
上下文管理器:
使对象可以用在 with 语句中,提供资源管理功能,如文件打开和关闭(enter, exit)。
迭代器和生成器:
让对象支持迭代,包括定义迭代器行为 (iter, next) 和生成器行为。
与 Python 内建类型及运动环境的集成:
包括定义对象的哈希 (hash),布尔值转换 (bool),大小 (len),以及格式化 (format) 等。
通过定义这些魔术方法,Python允许开发者以非常灵活和直观的方式来控制对象的行为。这一机制是Python的核心特性之一,为语言提供了极大的表达力和一致性。其实魔法方法远远不止这些,还有很多很多也值得我们去使用,Python中的魔法方法就跟它的名字一样有魔法,总是可以便捷的给你提供你所需要的各种需求,它们是Python面向对象编程的一大利器,使用魔术方法能构造出非常优美的代码结构。