Vanson's Eternal Blog

Python中的魔术方法详解

Python magic.png
Published on
/8 mins read/---

Python中的魔术方法

类相关

对象生命周期

__new__方法

普通类中

它通常用于自定义对象的创建过程。__new__ 的主要任务是创建并返回一个类的实例。通常,它会调用父类的 __new__ 方法来完成实际的实例创建。

 
def __new__(cls, *args, **kwargs):
    pass
 
  • cls:当前类的引用,即调用 new 的类。
  • *args:传递给构造函数的额外位置参数。
  • **kwargs:传递给构造函数的额外关键字参数。
class MyClass:
    def __init__(self, value):
      print(f"Initializing instance with value: {value}")
      self.value = value
    
    def __new__(cls, *args, **kwargs):
        print(f"Creating instance of {cls.__name__} with args: {args} and kwargs: {kwargs}")
        instance = super().__new__(cls)  # 调用父类的 __new__ 创建实例
        return instance
 
# 创建实例
obj = MyClass(123, key="value")
 
元类中

__new__ 用于元类时,它用于控制类的创建过程。

元类的 __new__方法的主要任务是创建并返回一个类对象。通常,它会调用父类的 __new__ 方法来完成实际的类创建。

def __new__(cls, name, bases, dct, **kwargs):
  pass
 
  • cls:当前元类的引用,即调用 __new__ 的元类。
  • name:新创建的类的名称。
  • bases:新创建的类的基类元组。
  • dct:包含类的属性和方法的字典。
  • **kwargs:传递给元类的额外关键字参数。
class MyMeta(type):
    def __new__(cls, name, bases, dct, **kwargs):
        print(f"Creating class {name} with bases: {bases} and attributes: {dct}")
        print(f"Additional kwargs: {kwargs}")
        return super().__new__(cls, name, bases, dct)
 
# 使用自定义元类创建类
class MyClass(metaclass=MyMeta, extra="info"):
    attr = "value"
 

输出

Creating class MyClass with bases: () and attributes: {'__module__': '__main__', '__qualname__': 'MyClass', 'attr': 'value'}
Additional kwargs: {'extra': 'info'}
 

__init__

__init__是实例化对象时的初始化方法。

class MyClass:
    def __init__(self, value):
        self.value = value
 
obj = MyClass(20)
print(obj.value)  # 输出:20
 

__del__

在对象被销毁时被调用。

 
class MyClass:
    def __del__(self):
        print("Object is being destroyed")
 
obj = MyClass()
del obj  # 输出:Object is being destroyed
 

上下文管理

__enter____exit__

用于定义对象的上下文管理行为,通常用于 with 语句。

 
class MyResource:
    def __enter__(self):
        print("Resource is being acquired")
        return self
 
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Resource is being released")
 
with MyResource():
    print("Inside the with block")
# 输出:
# Resource is being acquired
# Inside the with block
# Resource is being released
 
 

属性操作

__getattr__

用于定义访问不存在的属性时的行为。

 
class MyClass:
    def __getattr__(self, name):
        return f"Attribute {name} not found"
 
obj = MyClass()
print(obj.unknown)  # 输出:Attribute unknown not found
 

__setattr__

用于定义设置属性时的行为。

 
class MyClass:
    def __setattr__(self, name, value):
        if name == "value":
            self.__dict__[name] = value
        else:
            raise AttributeError(f"Cannot set attribute {name}")
 
obj = MyClass()
obj.value = 10
try:
    obj.other = 20
except AttributeError as e:
    print(e)  # 输出:Cannot set attribute other
 

__getattribute__

用于定义访问任何属性时的行为。

 
class MyClass:
    def __init__(self, value):
        self.value = value
 
    def __getattribute__(self, name):
        print(f"Accessing attribute {name}")
        return super().__getattribute__(name)
 
obj = MyClass(30)
print(obj.value)  # 输出:Accessing attribute value 30
 

运算符重载

__add__

用于定义对象的加法操作。

 
class MyClass:
    def __init__(self, value):
        self.value = value
 
    def __add__(self, other):
        if isinstance(other, MyClass):
            return MyClass(self.value + other.value)
        return NotImplemented
 
obj1 = MyClass(10)
obj2 = MyClass(20)
obj3 = obj1 + obj2
print(obj3.value)  # 输出:30
 

__eq__

 
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y
 
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2)  # 输出: True
 
 

__lt__

用于定义对象的小于关系。

class MyClass:
    def __init__(self, value):
        self.value = value
 
    def __lt__(self, other):
        if isinstance(other, MyClass):
            return self.value < other.value
        return NotImplemented
 
obj1 = MyClass(30)
obj2 = MyClass(40)
print(obj1 < obj2)  # 输出:True
 

类型转换

__str__

用户友好字符串表示

 
class Book:
    def __str__(self):
        return "《Python 核心编程》"
 
b = Book()
print(str(b))  # 输出: 《Python 核心编程》
 
 

__repr__

返回对象的“官方”字符串表示,通常用于调试。

 
class MyClass:
    def __init__(self, value):
        self.value = value
 
    def __repr__(self):
        return f"MyClass(value={self.value})"
 
obj = MyClass(40)
print(repr(obj))  # 输出:MyClass(value=40)
 

容器操作

__getitem__

用于定义通过索引访问元素的行为。

 
class MyList:
    def __init__(self, items):
        self.items = items
 
    def __getitem__(self, key):
        return self.items[key]
 
my_list = MyList([1, 2, 3])
print(my_list[1])  # 输出:2
 

__setitem__

用于定义通过索引设置元素的行为。

 
class MyList:
    def __init__(self, items):
        self.items = items
 
    def __setitem__(self, key, value):
        self.items[key] = value
 
my_list = MyList([1, 2, 3])
my_list[1] = 10
print(my_list.items)  # 输出:[1, 10, 3]
 

__iter__

用于定义对象的迭代行为。

 
class MyList:
    def __init__(self, items):
        self.items = items
 
    def __iter__(self):
        return iter(self.items)
 
my_list = MyList([1, 2, 3])
for item in my_list:
    print(item)  # 输出:1 2 3
 
 

__slots__

是一个类属性,用于限制实例的属性,并优化内存使用。通过定义 __slots__,可以明确指定类实例允许拥有的属性,防止动态添加其他属性。这不仅可以减少内存占用,还可以提高属性访问速度。

基本用法

是一个包含字符串的列表或元组,每个字符串代表一个允许的属性名。

 
class MyClass:
    __slots__ = ("attr1", "attr2")
 
    def __init__(self, value1, value2):
        self.attr1 = value1
        self.attr2 = value2
 
obj = MyClass(10, 20)
print(obj.attr1)  # 输出:10
print(obj.attr2)  # 输出:20
 
# 尝试添加未定义的属性
try:
    obj.attr3 = 30
except AttributeError as e:
    print(e)  # 输出:'MyClass' object has no attribute 'attr3'
 
节省内存

可以显著减少每个实例的内存占用,因为它避免了为每个实例创建一个完整的 __dict__

 
import sys
 
class MyClass:
    __slots__ = ("attr1", "attr2")
 
class MyNormalClass:
    pass
 
obj1 = MyClass(10, 20)
obj2 = MyNormalClass()
obj2.attr1 = 10
obj2.attr2 = 20
 
print(sys.getsizeof(obj1))  # 输出:48
print(sys.getsizeof(obj2))  # 输出:88
 
继承与 __slots__

当类继承自带有 __slots__ 的父类时,子类需要显式地定义自己的 __slots__, 否则子类实例会自动拥有一个 __dict__,从而抵消了节省内存的效果。

# 子类不定义 __slots__
class Parent:
    __slots__ = ("parent_attr",)
 
class Child(Parent):
    pass  # 没有定义 __slots__
 
obj = Child()
obj.parent_attr = 10
obj.child_attr = 20  # 子类实例有 __dict__,可以动态添加属性
 
# 子类定义 __slots__
 
class Parent:
    __slots__ = ("parent_attr",)
 
class Child(Parent):
    __slots__ = ("child_attr",)  # 子类显式定义 __slots__
 
obj = Child()
obj.parent_attr = 10
obj.child_attr = 20
 
try:
    obj.new_attr = 30
except AttributeError as e:
    print(e)  # 输出:'Child' object has no attribute 'new_attr'
 
 
使用 __slots____dict__

如果需要在使用__slots__的同时保留 __dict__,可以在 __slots__ 中显式地添加 __dict__

 
class MyClass:
    __slots__ = ("attr1", "attr2", "__dict__")
 
    def __init__(self, value1, value2):
        self.attr1 = value1
        self.attr2 = value2
        self.extra_attr = 30  # 动态添加的属性
 
obj = MyClass(10, 20)
print(obj.attr1)  # 输出:10
print(obj.attr2)  # 输出:20
print(obj.extra_attr)  # 输出:30