一文掌握python面向对象魔术方法(一)

目录

Python 中的魔术方法(Magic Methods)是一系列以双下划线开头和结尾的方法,它们在特定场景下会被 Python 解释器自动调用。这些方法让开发者可以定制类的行为,模拟类似内置类型的特性。

一、初始化和清理:

   1、构造方法 __init__(self, ...):  它是类中定义的一个构造方法,用于在创建类的实例时初始化对象的状态。每当通过 class_name() 创建一个新的类实例时,Python 会自动调用 __init__ 方法。

  2、__new__(cls, ...):  创建类实例时调用的第一个方法。它是类(class)级别的方法,主要负责创建并返回一个对象实例。当调用 cls() 创建类的新实例时,实际上首先调用的是 __new__ 方法,而不是 __init__ 方法。

  3、__del__(self): Python 中的一个特殊方法,称为析构方法或解构方法。当一个对象即将从内存中被删除(垃圾回收)之前,Python 解释器会尝试调用该对象的 __del__ 方法。它的主要用途是用于执行一些清理工作,例如关闭文件、网络连接,或者释放其他系统资源等。

二、属性访问:

1、__getattr__(self, name): 它允许一个对象在尝试访问一个不存在的属性时动态地创建和返回这个属性。 

2、__setattr__(self, name, value): 主要负责控制类属性的设置。当尝试给对象的实例或类变量赋值时,Python解释器会自动调用这个方法。

3、__getattribute__(self, name): 每次访问属性时均调用(优先级高于 __getattr__)。用来控制任何属性获取行为的核心方法。每当尝试访问对象的任何属性时,包括类属性和实例属性,Python解释器首先会调用__getattribute__方法。

4、__delattr__(self, name): 当尝试删除属性时调用。当尝试删除一个对象的属性时,Python 解释器会自动调用这个方法。

三、对象表示:

1、__str__(self): 主要用于提供对象的字符串表示形式。当需要将一个对象转换成字符串以便显示或者输出的时候,Python 的内置函数 str() 会调用这个方法。

2、__repr__(self): 全称是 Representation Method,返回对象的官方可读表示,通常用于调试。主要目的是创建一个能够完全确定地再现该对象的字符串,也就是说,这个字符串应该包含足够的信息,使得从这个字符串可以构造出一个等效的新对象。

3、__bytes__(self): 它属于对象的序列化机制,用于返回一个字节串(bytes object),表示对象的二进制表示形式。当需要将对象转化为 bytes 类型时,Python 会自动调用此方法。

四、比较和相等性:

1、__lt__(self, other): 又称-dunder 方法,定义小于运算符 <。

2、__le__(self, other): 定义小于等于运算符 <=。

3、__eq__(self, other): 定义等于运算符 ==。

4、__ne__(self, other): 定义不等于运算符 !=。

5、__gt__(self, other): 定义大于运算符 >。

6、__ge__(self, other): 定义大于等于运算符 >=。

五、算术运算:

1、__add__(self, other): 定义加法运算符 +。

2、__sub__(self, other): 定义减法运算符 -。

3、__mul__(self, other): 定义乘法运算符 *。

4、__truediv__(self, other):用于在自定义类中定义真除法(精确除法,结果为浮点数)的行为。当你的类的实例与另一个对象通过 / 进行除法操作时,Python 解释器会自动调用这个方法。

5、__floordiv__(self, other):用于在自定义类中定义地板除法(即除法后向下取整到最接近的整数)的行为。当你的类的实例与另一个对象通过 // 进行除法操作时,Python 解释器会自动调用这个方法。

5、__mod__(self, other)。在自定义类中,当需要实现该类实例与另一个对象进行模运算(求余数运算)时,就会用到这个方法。当类的实例使用 % 运算符与其他对象进行求模运算时,Python 解释器会自动调用 __mod__() 方法。

6、__pow__(self, other[, modulo]),  当对一个类的实例使用乘方运算符 ** 进行计算时,Python 解释器会自动调用这个方法。


Python 中的魔术方法(Magic Methods)是一系列以双下划线开头和结尾的方法,它们在特定场景下会被 Python 解释器自动调用。这些方法让开发者可以定制类的行为,模拟类似内置类型的特性。

一、初始化和清理

        1、构造方法 __init__(self, ...):  它是类中定义的一个构造方法,用于在创建类的实例时初始化对象的状态。每当通过 class_name() 创建一个新的类实例时,Python 会自动调用 __init__ 方法。

class ClassName:
    def __init__(self, parameter1, parameter2, ...):
        # 初始化对象的属性或执行必要的设置
        self.attribute1 = parameter1
        self.attribute2 = parameter2
        ...

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

# 创建 Person 类的实例
john = Person("John Doe", 30)

# 访问实例的属性
print(john.name)  # 输出: John Doe
print(john.age)   # 输出: 30

  2、__new__(cls, ...):  创建类实例时调用的第一个方法。它是类(class)级别的方法,主要负责创建并返回一个对象实例。当调用 cls() 创建类的新实例时,实际上首先调用的是 __new__ 方法,而不是 __init__ 方法。

__new__ 方法的主要作用包括:

  1. 控制是否要创建一个新实例。
  2. 控制新实例的具体类型,即可以返回子类或者其他类型的实例。
  3. 对于不可变类型(如int、str等),可以通过重写 __new__ 来实现自定义的创建行为。
class ClassName:
    def __new__(cls, *args, **kwargs):
        # 在这里创建并返回一个新的实例
        instance = super().__new__(cls)
        # 可以对 instance 进行一些初始化操作,但这部分不属于 __new__ 的主要职责,通常在 __init__ 方法中完成
        return instance

其中,cls 是当前类,*args 和 **kwargs 则是可以传递给实例构造函数的任意位置参数和关键字参数。这个方法必须返回一个实例对象,如果不返回或者返回非实例对象,则后续的 __init__ 方法将不会被调用。

再看一个重写 __new__ 方法的例子,创建一个简单的计数器类 Counter,每次创建实例时自动增加一个全局计数,并且确保只创建一个实例,这实现了单例模式的一种形式:

class Counter:
    _instance_count = 0
    _instances = {}

    def __new__(cls, name):
        if name not in cls._instances:
            cls._instance_count += 1
            cls._instances[name] = super().__new__(cls)
            cls._instances[name].name = name
            cls._instances[name].count = cls._instance_count
        return cls._instances[name]

# 使用示例
counter1 = Counter("Instance1")
print(counter1.count)  # 输出: 1
counter2 = Counter("Instance2")
print(counter2.count)  # 输出: 2
counter3 = Counter("Instance1")
print(counter3 is counter1)  # 输出: True,因为"Instance1"只有一个实例

在这个例子中,Counter 类通过重写 __new__ 方法来控制实例的创建过程,使得每个不同的名字(name)都只会对应唯一的一个实例,并且每次创建新的实例时,都会更新 _instance_count 属性。

  3、__del__(self): Python 中的一个特殊方法,称为析构方法或解构方法。当一个对象即将从内存中被删除(垃圾回收)之前,Python 解释器会尝试调用该对象的 __del__ 方法。它的主要用途是用于执行一些清理工作,例如关闭文件、网络连接,或者释放其他系统资源等。

然而,需要注意的是:

  1. __del__ 方法并不保证一定会被执行,特别是当程序正常退出时,或者引用了该对象的对象自身也在同一时刻被销毁时,可能会导致 __del__ 方法无法执行。
  2. 不推荐在 __del__ 方法中抛出异常,因为处理这种异常的机制并不明确,可能导致未知的行为。
  3. Python 的垃圾回收机制是基于引用计数和循环检测的,当一个对象的引用计数变为0时,才会触发垃圾回收进而可能执行 __del__ 方法。

基本语法格式如下:

class MyClass:
    def __init__(self):
        # 初始化代码...

    def __del__(self):
        # 清理工作代码...
        print("MyClass object is being deleted.")

# 示例:
obj = MyClass()
# ... 在其他地方使用 obj ...
# 当 obj 不再被任何变量引用,且垃圾回收器准备回收它时,将调用 obj 的 __del__ 方法

尽管 __del__ 方法在某些情况下很有用,但建议尽量避免依赖它来进行关键资源的管理,而应尽可能利用上下文管理协议(with语句)或者 try-finally 结构来确保资源能够及时正确地释放。

二、属性访问

1、__getattr__(self, name): 它允许一个对象在尝试访问一个不存在的属性时动态地创建和返回这个属性。 

当对一个对象使用getattr函数或者尝试访问对象中不存在的属性时,__getattr__方法会被调用。这个方法可以用于实现一些高级的特性,比如懒加载属性或者动态创建属性。 下面是一个简单的例子来说明__getattr__的用法:

class MyClass:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, item):
        return f'属性 "{item}" 在 MyClass 中不存在,但是我会告诉你这个对象叫做 "{self.name}"'

obj = MyClass('我的对象')
print(obj.attr)  # 输出: 属性 "attr" 在 MyClass 中不存在,但是我会告诉你这个对象叫做 "我的对象"

在这个例子中,我们定义了一个MyClass类,它有一个__getattr__方法。当我们尝试访问obj.attr时,attr属性在MyClass中并不存在,所以__getattr__方法被调用,并返回了一个字符串,告诉我们这个对象的名字。 需要注意的是,__getattr__方法应该返回一个值,否则会抛出AttributeError异常。此外,__getattr__方法不应该用于替代正常的属性访问,它应该只在尝试访问不存在的属性时使用。

2、__setattr__(self, name, value): 主要负责控制类属性的设置。当尝试给对象的实例或类变量赋值时,Python解释器会自动调用这个方法。

在类中定义__setattr__(self, name, value)方法后,每次尝试给该类的实例属性赋值时,都会执行这个方法,其中:

  • self:表示调用该方法的对象实例;
  • name:要设置的属性名,是一个字符串;
  • value:要赋予的新值。
class MyClass:
    def __setattr__(self, name, value):
        if name == 'age':
            if not isinstance(value, int):
                raise ValueError("Age must be an integer.")
        super().__setattr__(name, value)

# 示例
obj = MyClass()
obj.age = 25  # 此处将调用__setattr__方法

在这个例子中,重写了__setattr__方法,对age属性进行特殊处理,确保其只能被设置为整数类型。如果尝试设置非整数类型的age,将会抛出一个ValueError异常。

注意,在自定义__setattr__方法时,通常需要通过super().__setattr__(name, value)调用父类(在这里是object)的__setattr__方法来完成实际的属性设置,否则可能会导致属性无法正确设置或者无限递归的情况。

3、__getattribute__(self, name): 每次访问属性时均调用(优先级高于 __getattr__)。用来控制任何属性获取行为的核心方法。每当尝试访问对象的任何属性时,包括类属性和实例属性,Python解释器首先会调用__getattribute__方法。

在类中定义__getattribute__(self, name)方法后,每次尝试获取该类实例或类本身的任何属性时,无论该属性是否存在,都会先调用此方法。其中:

  • self:表示调用该方法的对象实例;
  • name:要获取的属性名,是一个字符串。
class MyClass:
    def __init__(self, attr_value):
        self.attr = attr_value
        
    def __getattribute__(self, name):
        if name == 'attr':
            return f"The value of '{name}' is {super().__getattribute__(name)}"
        else:
            return super().__getattribute__(name)

# 使用示例
obj = MyClass(42)
print(obj.attr)  # 这里将调用__getattribute__方法

在这个例子中,重写了__getattribute__方法,对attr属性的获取进行了定制,返回了一个包含属性名及其值的字符串。对于其他属性,我们仍然使用父类的默认行为(即直接返回属性值)。

需要注意的是,由于__getattribute__方法对所有属性访问都起作用,所以在实现时要特别小心,避免造成循环引用或者其他意料之外的结果。如果不希望覆盖所有属性的获取方式,而是只针对某些特定属性操作,可以考虑使用@property装饰器以及相关的方法如__getattr__。同时,为了正常处理继承链上的属性查找,通常会在自定义逻辑之后调用super().__getattribute__(name)来继续正常的查找流程。

4、__delattr__(self, name): 当尝试删除属性时调用。当尝试删除一个对象的属性时,Python 解释器会自动调用这个方法。

  • self: 对象实例自身,这是面向对象编程中实例方法的第一个参数,代表调用该方法的对象。
  • name: 要删除的属性名,这是一个字符串,表示你想
  • 23
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值