Python 中的一些魔法函数

Python 中的一些魔法函数

本文介绍 Python 的一些魔法函数,如:init、new、call 等,说说这些函数有说明神奇的作用。

1.__init__

__init__ 方法是类的构造方法(Constructor)。当创建一个类的实例对象时,__init__ 方法会被自动调用,用于为对象的属性赋初始值,这些属性可以在类的其他方法中被访问和操作。

__init__ 方法是一个特殊的实例方法,它的第一个参数必须是 self,代表正在创建的实例对象本身。

class MyClass:
    def __init__(self, arg1, arg2):
        self.attribute1 = arg1
        self.attribute2 = arg2

作用:

  • 属性初始化:用于为对象的属性赋初始值,这些属性可以在类的其他方法中被访问和操作。
  • 执行必要的设置和配置:可以在 __init__ 方法中执行一些必要的设置和配置操作,例如打开文件、建立数据库连接、初始化其他相关的对象等。

2.__getattribute__

__getattribute__ 方法是一个特殊的方法,用于控制对象属性的访问。当访问对象的属性时,Python 解释器会尝试调用这个方法来获取属性的值。

它定义在类中,方法的签名是 def __getattribute__(self, name),其中 self 是对象本身,name 是要访问的属性的名称(字符串形式)。

class CustomObject:
    def __init__(self):
        self.attribute = "Value of the attribute"

    def __getattribute__(self, name):
        print(f"Accessing attribute: {name}")
        return super().__getattribute__(name)

obj = CustomObject()
print(obj.attribute) 

注意事项:

  • 如果在 __getattribute__ 方法内部直接访问对象的属性,可能会导致无限递归。为了避免这种情况,需要使用 super().__getattribute__(name) 来获取属性的值。
  • 如果 __getattribute__ 方法抛出 AttributeError 异常,那么 Python 会按照正常的属性访问规则来查找属性,如果仍然找不到,才会引发 AttributeError 异常。

3.__new__

__new__ 方法用于创建对象并返回该对象。它在对象创建过程中先于 __init__ 方法被调用。

__new__ 方法是类方法,第一个参数是 cls,代表要实例化的类,后续参数与 __init__ 方法的参数相同,用于传递给对象初始化的数据。

class MyClass:
    def __new__(cls, *args, **kwargs):
        instance = super(MyClass, cls).__new__(cls)
        return instance

作用:

  • 控制对象的创建过程:可以自定义对象的创建方式,例如返回一个已经存在的对象、创建一个子类的对象或者从其他数据源创建对象等。
  • 实现单例模式:通过在 __new__ 方法中控制对象的创建,确保类只有一个实例被创建。

__init__ 方法的关系:

  • 调用顺序:先调用 __new__ 方法创建对象,然后将创建的对象作为 self 参数传递给 __init__ 方法进行初始化。
  • 返回值:__new__ 方法需要返回创建的对象实例,而 __init__ 方法没有返回值。

4.__repr__

__repr__ 方法是一个用于表示对象的“官方”字符串表示的特殊方法。它的主要目的是返回一个能够明确表示该对象的字符串,以便于开发者理解和调试。

__repr__ 方法是在类内部定义的一个实例方法,其方法签名为 def __repr__(self):,方法需要返回一个字符串。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f'Person(name="{self.name}", age={self.age})'

person = Person('Alice', 25)
print(repr(person))  

作用:

  • 调试和开发:当您在调试代码时,打印对象或在交互式解释器中查看对象时,Python 会调用 __repr__ 方法来获取对象的表示形式,帮助您快速了解对象的基本信息。
  • 日志记录:在记录日志时,如果涉及到对象的记录,__repr__ 方法可以提供对象的清晰表示。

5.__str__

__str__ 方法也是一个用于对象字符串表示的特殊方法。它用于返回对象的字符串表示,该字符串应当以用户易于理解的方式呈现对象的信息。

__repr__ 方法一样,__str__ 方法也是在类内部定义的实例方法,方法签名为 def __str__(self):,并且需要返回一个字符串。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f'{self.name}, {self.age} years old'

    def __repr__(self):
        return f'Person(name="{self.name}", age={self.age})'

person = Person('Alice', 25)
print(str(person))  
print(repr(person)) 

__repr__ 的区别:

  • 目的不同:__repr__ 方法的目的是产生一个明确的、无歧义的对象表示,主要用于调试和开发;而 __str__ 方法的目的是产生一个对用户友好、可读性强的对象描述。
  • 默认调用:在使用 print() 函数或 str() 函数时,会默认调用 __str__ 方法;而在使用 repr() 函数或在交互式解释器中直接输入对象时,会默认调用 __repr__ 方法。

6.__setattr__

__setattr__ 方法是一个特殊方法,用于在为对象的属性赋值时进行自定义的操作或控制。

__setattr__ 方法定义在类中,方法签名为 def __setattr__(self, name, value):,其中 self 是对象本身,name 是要设置的属性名称(字符串),value 是要赋给属性的值。

class CustomClass:
    def __setattr__(self, name, value):
        if name == 'protected_attribute' and value < 0:
            raise ValueError('protected_attribute cannot be negative')
        super().__setattr__(name, value)

obj = CustomClass()
obj.protected_attribute = 10  # 正常赋值
obj.protected_attribute = -5  # 触发异常

作用:

  • 数据验证和过滤:可以在设置属性值之前,对输入的值进行验证和过滤,确保属性值的合法性和安全性。
  • 日志记录:记录属性赋值的操作和相关信息,用于跟踪和审计。
  • 实现特殊的赋值逻辑:例如在设置属性时触发其他相关操作,或者根据特定条件动态地计算和设置属性值。

注意事项:

__setattr__ 方法内部,如果直接使用 self.name = value 的形式进行属性赋值,会导致无限递归调用 __setattr__ 方法。应该使用 super().__setattr__(name, value) 来正确地设置属性。

7.__getattr__

__getattr__ 方法是一个特殊方法,用于在通过常规方式找不到对象的属性时被调用。

__getattr__ 方法定义在类中,方法签名为 def __getattr__(self, name),其中 self 是对象实例,name 是要获取的属性名称(字符串形式)。

class DynamicAttributes:
    def __getattr__(self, name):
        if name == 'dynamic_property':
            return "This is a dynamically generated property"
        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'")

obj = DynamicAttributes()
print(obj.dynamic_property)  

在上述示例中,当访问 obj 对象的 dynamic_property 属性时,由于该属性不存在,所以 __getattr__ 方法被调用并返回相应的值。

作用:

  • 动态属性获取:当访问一个对象不存在的属性时,__getattr__ 方法可以动态地计算或返回一个值,而不是引发 AttributeError 异常。
  • 实现懒加载:仅在实际需要访问某个属性时才进行相关数据的加载或计算,提高性能和资源利用率。

__getattr____getattribute__ 方法的区别:

  • 调用时机:当访问对象的任何属性时,都会首先尝试调用 __getattribute__ 方法来获取属性的值。仅当通过常规属性查找方式(即在对象的实例字典和类的字典中都没有找到指定属性)找不到属性时,才会调用 __getattr__ 方法。
  • 使用频率:__getattribute__ 方法始终在属性访问时被调用,使用相对较为频繁。__getattr__ 方法仅在属性未通过常规方式找到时才被调用,使用场景相对较少。
  • 处理异常:如果在 __getattribute__ 方法内部引发了 AttributeError 异常,并且没有被捕获处理,那么后续不会再调用 __getattr__ 方法,而是将该异常向上传播。如果在 __getattr__ 方法中没有找到指定属性并且没有处理异常,或者引发了 AttributeError 异常,那么该异常将向上传播。
  • 避免无限递归:在 __getattribute__ 方法内部访问实例的属性时,需要使用 super().__getattribute__(name) 来避免无限递归。由于它是在常规属性查找失败时才被调用,所以在其内部访问其他已存在的属性时,一般不会导致无限递归问题。

8.__delattr__

__delattr__ 方法是一个特殊方法,用于在删除对象的属性时进行自定义的操作或控制。

__delattr__ 方法定义在类中,方法签名为 def __delattr__(self, name),其中 self 是对象实例,name 是要删除的属性名称(字符串形式)。

class CustomObject:
    def __init__(self):
        self.my_attribute = 42

    def __delattr__(self, name):
        if name == 'my_attribute':
            print(f"Deleting attribute: {name}")
        super().__delattr__(name)

obj = CustomObject()
del obj.my_attribute  

作用:

  • 执行额外的清理或逻辑处理:在删除属性时,可能需要执行一些与该属性相关的清理操作,例如关闭文件、释放资源等。
  • 控制属性的删除权限:可以通过在 __delattr__ 方法中添加条件判断,来决定是否允许删除某个属性,或者在特定条件下执行其他操作而不是直接删除属性。

9.__ del__

__del__ 方法是一个特殊的方法,称为析构方法(Destructor)。当对象即将被垃圾回收器回收时,__del__ 方法会被自动调用。

__del__ 方法定义在类中,方法签名为 def __del__(self):,其中 self 代表要被销毁的对象实例。

class ResourceHolder:
    def __init__(self, filename):
        self.filename = filename
        self.file = open(filename, 'r')

    def __del__(self):
        if hasattr(self, 'file') and self.file:
            self.file.close()
            print(f"File {self.filename} closed.")

rh = ResourceHolder('example.txt')

作用:

  • 资源清理:释放对象占用的系统资源,如关闭文件、释放网络连接、清除临时数据等。
  • 执行对象销毁前的其他必要清理逻辑

需要注意的点:

  • 无法准确确定 __del__ 方法的执行时间:Python 的垃圾回收器的运行是由其内部机制决定的,不一定能准确预测何时会调用 __del__ 方法来销毁对象。
  • 可能导致循环引用问题:如果对象之间存在循环引用,可能会导致它们的引用计数不会降为 0,从而使垃圾回收器无法及时回收这些对象,直到循环引用被打破或者通过其他方式手动触发垃圾回收。
  • 不应该过度依赖:一般情况下,更好的资源管理方式是通过 with 语句(对于支持上下文管理协议的对象)或手动显式地调用清理方法,而不是完全依赖 __del__ 方法。

10.__iter__

__iter__ 方法是一个特殊方法,用于使对象可迭代。当对一个对象使用迭代操作(如 for 循环)时,如果该对象实现了 __iter__ 方法,就可以进行迭代。

__iter__ 方法定义在类中,方法签名为 def __iter__(self):,它需要返回一个迭代器对象。

class MyIterable:
    def __init__(self, data):
        self.data = data

    def __iter__(self):
        return iter(self.data)  # 假设 self.data 是一个可迭代的对象

my_iterable = MyIterable([1, 2, 3, 4, 5])

for item in my_iterable:
    print(item)

作用:

  • 定义对象的迭代行为:决定了如何迭代该对象中的元素。
  • 与迭代器模式结合:使得对象能够与各种需要迭代的场景(如循环、列表推导式等)进行交互。

11.__len__

__len__方法是一个特殊方法,用于定义对象的长度。当您对一个对象使用 len() 函数时,如果该对象实现了 __len__ 方法,len() 函数会调用这个方法来获取对象的长度。

__len__ 方法定义在类中,方法签名为 def __len__(self):,它需要返回一个表示对象长度的整数。

class CustomList:
    def __init__(self, items):
        self.items = items

    def __len__(self):
        return len(self.items)

custom_list = CustomList([1, 2, 3, 4, 5])
print(len(custom_list))  

作用:

  • 自定义长度计算:对于自定义的类,可以根据类的属性或数据结构来定义如何计算对象的长度。
  • 与内置函数 len() 配合:使自定义对象能够与 len() 函数兼容,方便获取对象的长度信息。

12.__ call__

__call__ 方法是一个特殊方法,它使得类的实例可以像函数一样被调用。

__call__ 方法定义在类内部,方法签名为 def __call__(self, *args, **kwargs),其中 self 代表类的实例本身,args 和 kwargs 用于接收传递给实例的参数。

class Adder:
    def __init__(self, initial_value=0):
        self.value = initial_value

    def __call__(self, x):
        self.value += x
        return self.value

adder = Adder()
print(adder(5))  
print(adder(3)) 

作用:

  • 使对象具有函数的行为:可以将实例作为可调用对象来使用,为对象添加了函数式编程的特性。
  • 实现状态保持和封装:在实例内部可以维护一些状态信息,每次调用时基于这些状态进行操作。
  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值