python 魔术方法

在 Python 中,魔术方法是指一类特殊的方法,它们以双下划线(__)开头和结尾,并且具有特殊的行为。这些方法也被称为特殊方法、魔法方法或魔法属性。

__init__方法

__init__() 方法是 Python 中的一个特殊方法,也被称为构造函数或初始化函数。它在对象被创建时自动调用,用于对对象进行初始化操作。具体来说,__init__() 方法的主要作用有:

  1. 初始化类的属性:通过在 __init__() 方法中为类的属性赋值,可以在创建对象时为这些属性赋初值。

  2. 实现类的特殊行为:在 __init__() 方法中可以编写其他特殊行为,如创建文件、打开数据库连接等。

需要注意的是,__init__() 方法的第一个参数必须是 self,代表当前对象。在 __init__() 方法中设置的类属性,都必须以 self. 开头,以便在对象被创建时正确地初始化属性的值。

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

    def say_hi(self):
        print("Hello, my name is", self.name, "and I'm", self.age, "years old.")

person = Person("Alice", 25)
person.say_hi() # 输出 "Hello, my name is Alice and I'm 25 years old."

在上面的例子中,定义了一个 Person 类,并在 __init__() 方法中以参数的形式接收了 nameage 两个属性。当创建 Person 对象时,这两个属性分别被赋值为传入的参数值。在 say_hi() 方法中,对象可以访问这两个属性,并输出它们的值。

__new__方法

__new__是Python中一个特殊的方法,用于创建对象并返回其实例。它在对象实例化时调用,负责创建类的实例。相较于__init__方法,则是更接近对象的构造函数,因为它被调用时类的实例尚未创建,可以在它的方法体内实现一些针对对象创建的逻辑操作。

__new__方法是一个类方法,会在对象实例化时被调用,用于创建并返回一个类的实例。它的参数包括类本身以及其他类构造函数(即__init)所需要的参数。如果__new__方法返回的是当前类实例,那么接下来会自动调用__init__方法,以便对类实例进行初始化。__new__方法通常用于创建一些不可变或定制的数据类型,如元组、字符串、数字等。此外,在自定义元类时,也经常会覆盖__new__方法来控制类的实例化过程。

__init__方法则是一个实例方法,会在类实例化并创建后被调用,用于初始化该实例。__init__方法的参数包括self以及其他需要初始化实例变量的参数。__init__方法经常用于设置实例的默认属性值、创建实例变量等操作。

需要注意的是,__init__方法确保只有在__new__方法成功地返回一个实例后才会被调用,因此在__init__方法中我们可以放心地访问实例变量。另外,一旦__init__方法完成执行,实例就被认为是完全初始化了的,并随时可以使用。

单例模式

单例模式是一种设计模式,旨在确保某个类在任何时候只有一个实例,并提供了访问该实例的全局访问点,避免了多次实例化相同类的对象所带来的资源浪费和不必要的复杂性。在

Python 的单例模式通常可以通过覆盖类的__new__方法方法来实现。

在 Python 中,__new__方法方法是在构造函数 init 之前被调用的,用于创建一个对象实例,并将其返回。每次实例化一个类时,__new__方法方法都会被自动调用,但是在单例模式中,我们需要控制 __new__方法方法只返回同一个实例。

# 实例化一个单例
#如果类属性__instance为None,那么就创建一个对象,并且赋值为这个对象的引用,保证下次调用这个方法时能够知道之前已经创建过对象了,这样就保证了只有1个对象
class Test(object):
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            cls.__instance = object.__new__(cls)
        return cls.__instance
a = A()
b = A()
print(id(a))
print(id(b))
#运行结果:
4391023224
4391023224

以下是一种常见的用于实现单例模式的方式(增加了线程安全处理):

import threading

class Singleton:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls)
        return cls._instance

这段代码是一个简单的 Python 单例模式实现,并增加了线程安全处理。在该实现中,使用了 threading.Lock() 对象来创建互斥锁,确保多个线程同时访问时只有一个线程可以执行实例化操作。

具体来说,该实现包含如下步骤:

  1. 定义一个类变量 _instance,初始值为 None

  2. 定义一个 threading.Lock() 对象 _lock

  3. 覆盖 __new__ 方法,在方法中先检查 _instance 是否存在,如果不存在则先获取 _lock 锁。

  4. 得到锁之后重新检查 _instance 是否为 None,如果是的话就调用父类的 __new__ 方法,创建一个新的实例并将其保存在 _instance 中。

  5. 最后返回 _instance 变量中保存的实例。

通过装饰器对类实现单例模式

def wrapper(cls):
   instance ={} #创建一个字典用来存放创建出来的单列对象
    def _fun(*args,**kwargs):
        #判断该类在不在字典中,
        if cls not in instance:
            #没创建就创建一次,并把它加入到字典中
            instance[cls]=cls(*args,**kwargs)
        #在字典中说明创建过了,不做任何操作
        return instance[cls]
    return _fun
@wrapper
class Test2(object):
    def __init__(self,name):
        self.name=name

这段代码实现了 Python 中的单例模式,通过装饰器(@wrapper)为指定类(Test2)添加了单例特性,即对于该类的多次实例化,始终只返回同一个实例化对象。

具体实现方式是在装饰器函数 wrapper 中创建了一个字典 instance,用于存放已经实例化过的对象。在每次调用被装饰的类时,会先判断该类是否已经在字典中存在实例化对象,如果存在则直接返回该对象,否则新建一个对象,并将其添加到字典中,然后返回该对象。

在本例中,被装饰的类是 Test2,该类的构造函数有一个参数 name,表示该类的实例化对象的名称。因此,在使用该类时需要提供一个 name 参数来进行实例化。

下面是使用该类的示例代码:

t1 = Test2("instance1")
t2 = Test2("instance2")

print(t1)  # 输出:<__main__.Test2 object at 0x7f506b365790>
print(t2)  # 输出:<__main__.Test2 object at 0x7f506b365790>

print(t1.name)  # 输出:instance1
print(t2.name)  # 输出:instance1

可以看到,虽然我们分别实例化了两个不同的对象 t1t2,但它们的内存地址相同,即它们都是同一个实例化对象。同时,由于 t2 是在 t1 实例化后创建的,所以它的 name 属性也与 t1 相同,即 "instance1"

具体使用:日志记录器

import logging

class Logger:
    _instance = None
    _lock = threading.Lock()

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            with cls._lock:
                if not cls._instance:
                    cls._instance = super().__new__(cls)
                    # 定义日志记录器的配置信息
                    log_format = '%(asctime)s %(levelname)s: %(message)s'
                    log_level = logging.DEBUG 
                    log_file = 'app.log'

                    # 创建和配置日志记录器
                    handler = logging.FileHandler(log_file)
                    formatter = logging.Formatter(log_format)
                    handler.setFormatter(formatter)

                    logger = logging.getLogger('myapp')
                    logger.setLevel(log_level)
                    logger.addHandler(handler)

                    cls._instance.logger = logger

        return cls._instance
logger = Logger().logger
logger.debug('This is a debug log message.')
  • 创建日志记录器的配置信息包括:日志格式、日志级别和日志文件名等参数。

  • 创建和配置日志记录器的过程包括:创建文件处理器(FileHandler)并设置格式化器(Formatter)、创建记录器(getLogger)并设置日志级别和处理器。

  • logger 属性表示日志记录器实例,并通过 logger.debug() 方法记录一条调试信息,该信息将按照事先定义好的格式写入到名为 "app.log" 的文件中。

  • 将创建好的日志记录器保存在 logger 变量中,然后将它赋值给 cls._instance.logger 属性,最终返回一个日志记录器实例对象。这样,在程序的其他地方,我们可以通过 Logger() 类的实例来获取这个 logger 属性,进而使用日志功能来记录各种级别的日志信息。

通过以上代码,我们可以创建和使用一个全局唯一的日志记录器,在应用程序中随时记录和输出各种级别的日志信息,以便实现对应用程序的监控和调试。

上下文管理器

上下文管理器协议(Context Manager Protocol)是 Python 语言中的一种动态管理资源的方式,用于在作用域结束时自动释放资源,保证代码的正确性和可维护性。

使用 with 语句可以简化资源管理的过程,避免开发人员忘记手动释放资源。

在 Python 中,实现了 Context Manager 协议的对象可以与 with 语句结合使用。with 语句负责管理上下文并在加入和退出上下文时通知上下文对象。

应用背景:上下文管理器协议适用于需要临时获得某些资源,使用完毕后需要及时释放的场景。

比如文件、网络连接、数据库连接等,这些资源通常需要手动打开、关闭。使用上下文管理器协议可以简化这一流程,使代码更加简洁、安全和易于维护。

上下文管理器协议:

  • enter: 进入,

    • enter方法返回的结果被as后面的变量接收,with ... as f

  • exit: 退出

    • with中所有的语句执行完毕执行 执行 exit

在python中所有实现了上下文管理器协议的对象 都可以用使用with操作

一个上下文管理器的 exit 方法返回的是一个布尔值,用来表示上下文管理器是否处理了异常。如果返回 True,那么异常被上下文管理器处理了,程序会继续运行;如果返回 False,那么异常将会被重新抛出到上层调用栈中。如果没有发生异常,或者__exit__方法没有返回任何值,默认情况下被视为True。

退出与此对象相关的运行时上下文。参数描述导致上下文退出的异常。如果该上下文退出时没有异常,三个参数都将为 None 。如果提供了一个异常,并且该方法希望抑制该异常(即防止它被传 播),它应该返回一个真值。否则,在退出此方法后,异常将被正常处理。注意 exit() 方法 不应该重新抛出传递进去的异常;这是调用者的责任。

class MyContextManager:

    def __init__(self, filename, mode, encoding='utf-8'):
        self.filename, self.mode, self.encoding = filename, mode, encoding

    def __enter__(self):
        print('__enter__')
        # raise ValueError("error")
        self.file_obj = open(self.filename, self.mode, encoding=self.encoding)
        return self.file_obj

    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        上下文管理器协议中的 `__exit__` 方法用于处理上下文中的异常以及资源的释放。该方法有三个参数:

        - `exc_type`:表示异常类型,表示异常发生时的异常类型,如果未发生异常,则此参数为 None。
        - `exc_val`:表示异常值,表示异常发生时的异常值,如果未发生异常,则此参数为 None。
        - `exc_tb`:表示异常回溯,表示异常发生时的回溯信息,如果未发生异常,则此参数为 None。

        上下文管理器协议中的 `__exit__` 方法通常有两种返回值:

        - 如果返回 None,则表示上下文管理器自己处理异常,即我们认为异常已经处理完毕了,Python 解释器不会将异常传递给上层代码。
        - 如果返回 True,则表示上下文管理器已经成功处理了异常,并且异常已经被处理完毕了,Python 解释器不会将异常传递给上层代码。
        - 如果返回 False 或其他任意非空值,则表示上下文管理器没有成功处理异常,Python 解释器会将异常传递给上层代码继续处理。

        :param exc_type:
        :param exc_val:
        :param exc_tb:
        :return:
        """
        self.file_obj.close()
        return True


if __name__ == '__main__':
    print('start')
    with MyContextManager('lemon.txt', 'w') as mm:
        print('1111')
        raise ValueError("error")
        mm.write('lemon')
        print('2222')
    print('end')

# 输出
start
__enter__
1111
end

上述代码实现了一个简单的上下文管理器,用于打开文件并在使用完毕之后自动关闭文件。在该类中,重载了 __enter__ 方法和 __exit__ 方法。

当我们通过 with 语句创建一个 MyContextManager 类的对象时,会调用 __enter__ 方法。在 __enter__ 方法中,会打开指定的文件,并将文件对象返回给上下文管理器,以便于在 with 语句块内的代码中使用该文件对象进行读写操作。

with 语句块内的所有代码执行完毕后,会自动调用 __exit__ 方法,该方法中会先关闭文件,确保资源得到及时释放。然后,该方法会判断是否有异常抛出,在有异常抛出时,根据异常处理的需求返回相应的值,否则返回 True,表示异常已经被成功处理。

需要注意的是,上下文管理器协议中的 __exit__ 方法,在发生多个异常时也只会处理其中的一个异常,所以需要在该方法中处理所有可能发生的异常,以确保程序的健壮性。

下面是一个使用 with 语句实现文件读写的示例:

with open("test.txt", "r") as f:
    data = f.read()
    print(data)

在上面的示例中,open() 函数返回一个文件对象,使用 with 语句打开这个文件后,可以自动调用文件对象的 __enter__() 方法获取资源并返回文件对象。同时,with 语句包含的代码块中可以对文件对象进行操作,例如读取文件内容。当代码块执行完后,with 语句会自动调用文件对象的 __exit__() 方法释放资源。

__call__方法

在 Python 中,类可以通过定义 __call__() 方法实现对对象的调用操作。当一个类的实例被作为函数进行调用时,Python 会自动调用该实例的 __call__() 方法。

定义了 __call__() 方法的类的实例可以像函数一样被调用,可以带有参数和返回值。这种方式可以让我们使用类的实例来封装一个可调用的逻辑单元,在使用时具有更高的灵活性和可复用性。

class Counter:
    def __init__(self):
        self.counter = 0

    def __call__(self):
        self.counter += 1
        return self.counter

counter = Counter()
print(counter()) # 输出 1
print(counter()) # 输出 2

在上面的示例中,我们定义了一个 Counter 类,其构造函数初始化了一个计数器变量。__call__() 方法将计数器加 1 并返回计数器的值。通过创建 Counter 类的对象,并像函数一样调用它,可以实现对计数器进行增加的操作。

需要注意的是,使用 __call__() 方法不应该过度使用,因为它可能会导致代码的可读性降低。通常情况下,只有在需要定义一个可调用的逻辑单元时才应该使用这种方式。

通过 call 方法用类实现装饰器

如果要把类当做一个装饰器来用,有两步操作,

  • 首先在 init 方法中,把被装饰的函数赋值给一个实例属性,

  • 然后再了类里面实现一个 __call__()方法,在__call__()方法中调用原来的函数。

class Test(object):
    def __init__(self,func):  
        self.func = func
    def __call__(self, *args, **kwargs):
        print('这个是类装饰器')
        self.func(*args,**kwargs)

这段代码是一个类装饰器的示例,它通过在类定义中实现 __call__() 方法来实现对被装饰函数的增强。

当使用 Test 类对某个函数进行装饰时,首先会创建一个 Test 类的实例,并将被装饰函数作为参数传递给该实例的构造函数。然后,Python 会将该实例对象作为函数进行调用,由于 Test 类实现了 __call__() 方法,因此会自动调用该方法,并在调用被装饰函数之前打印一行提示信息。

下面是示例代码演示了如何使用 Test 类装饰器对函数进行装饰:

@Test
# Test(hello)
def hello(name):
    print("Hello, %s!" % name)

hello("World")

在上面的代码中,我们首先使用 @Testhello 函数进行装饰,然后调用 hello 函数并输出结果。执行代码时,会先打印一行提示信息 "这个是类装饰器",然后再输出 "Hello, World!"。

需要注意的是,类装饰器可以带有参数,从而实现更加灵活的装饰器功能。此时需要在 Test 类的构造函数中接收装饰器的参数,并在 __call__() 方法中正确地使用这些参数。

class repeat:
    def __init__(self, times):
        self.times = times

    def __call__(self, func):
        def wrapper(*args, **kwargs):
            for _ in range(self.times):
                result = func(*args, **kwargs)
            return result
        return wrapper

@repeat(3)
def hello(name):
    print(f"Hello, {name}!")

hello("World")

在上面的代码中,我们定义了一个名为 repeat 的类装饰器,并且该装饰器带有 times 参数。在类的 __init__() 方法中,我们将 times 参数保存为实例属性。

接下来,在 __call__() 方法中,我们使用传递给装饰器的函数作为参数,并返回一个新的函数 wrapper

wrapper 函数中,我们使用 for 循环来执行被装饰的函数 func,并将其执行 times 次。在每次执行结束后,我们将最后一次结果保存为 result 并返回。

最后,我们使用 @repeat(3)hello 函数进行装饰,并且执行 hello("World") 语句。由于使用了类装饰器,因此 hello 函数会执行 3 次,分别输出 "Hello, World!"。

需要注意的是,通过向类装饰器传递参数,可以动态地控制被装饰函数的行为。这种方式可以大幅提升代码的灵活性和复用性。

__str__ 方法

__str__ 方法是 Python 中的一个特殊方法,用于定义对象的字符串表示形式。当我们使用 print(obj)str(obj) 输出对象时,Python 会自动调用该对象的 __str__ 方法,并将其返回值作为字符串输出。如果未实现该方法,则输出默认的字符串表示形式 <__main__.ClassName object at 0x7f8a2327f390>

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})"

p = Person("Alice", 25)
print(p)  # 输出:Person(name=Alice, age=25)

在上面的代码中,我们定义了一个名为 Person 的类,并在其中实现了 __str__ 方法。在 __str__ 方法中,我们返回了一个字符串,该字符串由类中的属性 nameage 组成。然后我们创建了一个 Person 类的实例 p,并使用 print 输出它。由于已经实现了 __str__ 方法,因此输出的是我们定义的字符串。如果没有实现这个方法,输出将是默认的格式。

注意点:

  • 重写`str,必须要记得写return。

  • return返回的必须是一个字符串对象。

算术运算的实现

在 Python 中,不仅数字可以进行加法运算,还可以对字符串、列表与元组等序列类型的数据进行加法运算。这是因为 Python 提供了对序列类型对象进行操作的魔术方法,即 __add__() 方法, 可以让相应的对象变得可以相加。

class Test(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def __add__(self, other):
        print('对象之间使用了+号')
        return self.age+other.age
xiaoming = Test('小明',18)
laowang = Test('老王',48)
res = xiaoming + laowang

在这段代码中,我们定义了一个 Test 类,并在类中定义了一个魔术方法 __add__(),它的作用是在两个 Test 对象之间使用加号 + 时打印一条提示信息,并返回它们年龄属性的和。

然后我们创建了两个 Test 类的实例化对象 xiaominglaowang,分别表示小明和老王,并将它们的年龄分别设为 1848

接着,我们将 xiaominglaowang 之间使用加号 + 相加,并将结果赋值给变量 res。由于我们在类中实现了 __add__() 方法,因此程序会输出一条提示信息:

对象之间使用了+号

紧接着程序会计算并返回他们年龄之和,即 18 + 48 = 66

最终,将计算结果 66 赋值给变量 res

import os


class MyPath:
    def __init__(self, path):
        self.path = path

    def __truediv__(self, other):
        """
        对象/对象
        :param other:
        :return:
        """
        if isinstance(other, MyPath):
            return MyPath(os.path.join(self.path, other.path))
        elif isinstance(other, str):
            return MyPath(os.path.join(self.path, other))

    def __str__(self):
        return str(self.path)


a = MyPath("/tmp")
b = MyPath("bar")
c = a / b   # /tmp/bar
print(c)

# 输出
/tmp\bar

isinstance() 是 Python 内置函数,用来检查对象是否属于指定的类或类型。在 MyPath 类中的 __truediv__ 方法中,使用了 isinstance(other, MyPath) 来判断 other 是否为 MyPath 类的对象。如果 otherMyPath 类的对象,则执行 os.path.join(self.path, other.path),返回一个新的 MyPath 对象;否则,判断 other 是否为字符串类型,如果是,则将其与 self.path 进行拼接,同样返回一个新的 MyPath 对象。

这样做的好处在于,可以确保 / 运算符左右两侧的对象类型都正确,并且在执行路径拼接时使用了 os.path.join() 方法,能够处理不同操作系统下的路径分隔符差异和绝对路径拼接等问题,保证了路径拼接的正确性和可移植性。

以上代码实现了一个名为 MyPath 的类,该类封装了对路径字符串的相关操作。在该类中,重载了 / 运算符,用于实现路径拼接功能,并重载了 __str__ 方法,以便于对象打印输出。

当我们创建一个 MyPath 对象时,可以传入一个路径字符串,例如 MyPath("/tmp")MyPath("bar")。使用 / 运算符时,如果右侧是一个 MyPath 对象,则将两个路径字符串进行拼接;如果右侧是一个字符串,则将其与该对象的路径字符串进行拼接。

例如,执行 c = a / b 后,会将 /tmpbar 拼接为 /tmp/bar,并返回一个新的 MyPath 对象 c。此时,输出 c,会调用 __str__ 方法,以字符串形式输出 /tmp\bar

同时,我们也可以使用 Python 自带的 pathlib 模块实现类似的路径操作,如 p1 = Path('/tmp')p2 = Path('bar'),以及拼接路径的操作 p1 / p2,结果也为 /tmp/bar

from pathlib import Path


p1 = Path('/tmp')
p2 = Path('bar')
print(p1 / p2)

# 输出
\tmp\bar

其他算术运算符对应的魔术方法

__add__(self, other) 定义加法的行为:+
__sub__(self, other) 定义减法的行为:-
__mul__(self, other) 定义乘法的行为:*
__truediv__(self, other) 定义真除法的行为:/
__floordiv__(self, other) 定义整数除法的行为://
__mod__(self, other) 定义取余算法的行为:%
#自定义一个列表类型,实现对象可以之间可以 使用 -  来进行操作
# 要求:如果一个对象减去另一个对象,则把和被减对象中一样的数据给删除掉
# 如下:
"""
li1 = MyList([11, 22, 33,22, 44])
li2 = MyList([1, 22])
res = li1 - li2
"""
# res 打印的结果为[11,33,44]

class MyList(list):
    def __sub__(self, other):
        res = MyList()
        for elem in self:
            if elem not in other:
                res.append(elem)
        return res


li1 = MyList([11, 22, 33, 22, 44])
li2 = MyList([1, 22])
res = li1 - li2
print(res)

注意:other:[1,22]、self:[11, 22, 33, 22, 44]

MyList继承自内置的list类型。在重写的__sub__()方法中,遍历self对象(即li1),对其中的每个元素做一次判断,如果该元素不在other对象(即li2)中,则添加到结果列表中。最终返回res,即删除了两个列表中相同元素后的新列表。

这种方法直接继承了内置的list类型,并在此基础上重写了减法操作符。因此可以直接使用内置list类型的方法,包括append等,需要实现新功能时也更加灵活方便。

__hash__方法

定义当被 hash() 调用时的行为

__hash__ 方法是 Python 中的一个特殊方法,用于对对象进行哈希(Hash)操作,将其转换为整数类型的值。哈希操作可以将任意长度的输入(比如数字、字符串、文件等)映射到固定长度的输出(一般是一个整数),并保证相同的输入一定会映射到相同的输出。

在 Python 中,哈希操作常常用于散列表(Hash Table)的实现中,散列表是一种能够快速查找元素的数据结构,其核心思想就是将元素的哈希值作为索引,然后将元素存储在索引对应的位置上。

__hash__ 方法的默认实现是返回对象的内存地址,也就是 id(obj)。但是,如果自定义类需要将其对象用作字典或集合等可哈希的数据类型的键或元素,则需要重载 __hash__ 方法,确保相等的对象具有相同的哈希值。

__bool__ 方法

定义当被 bool() 调用时的行为,应该返回 True 或 False

__bool__ 方法是 Python 中的一个特殊方法,用于将对象转换为布尔类型的值。在 Python 中,任何对象都可以被当做条件表达式的判断条件,而 __bool__ 方法就规定了在进行这种判断时,对象应该返回的布尔值。

__bool__ 方法的默认实现是直接返回 True,也就是对于任何对象来说,其布尔值都是 True。但是,如果自定义类需要更加灵活地控制其对象的真值(True 或 False),则可以重载 __bool__ 方法,并在其中根据对象的属性或状态来确定其布尔值。

class MyClass(object):

    def __init__(self, name, id_):
        self.name = name
        self.id = id_

    def __hash__(self):  # 必须返回int类型
        return self.id

    def __bool__(self):
        return False if self.id == 0 else True


if __name__ == '__main__':
    one_dict = {}
    obj1 = MyClass('lemon', 0)
    obj2 = MyClass('keyou', 10002)
    one_lst = [10, 20]
    one_dict[obj1] = 100
    one_dict[obj2] = 200
    # one_dict[one_lst] = 300     # 字典中的key必须是支持获取hash值,hash(obj)
    print(one_dict[obj1])
    print(one_dict[obj2])
    print(one_dict)
    if obj1:
        print("id不为0")
    else:
        print("id为0")

# 输出
100
200
{<__main__.MyClass object at 0x000002F5682D2E88>: 100, <__main__.MyClass object at 0x000002F5682D2EC8>: 200}
id为0

Process finished with exit code 0

上述代码定义了一个名为 MyClass 的类,该类包含了一个构造函数 __init__ 和两个特殊方法 __hash____bool__

  • 其中,__init__ 方法用于初始化实例的属性,接受两个参数 nameid_,并分别将其赋值给实例的 nameid 属性。

  • __hash__ 方法是一个特殊方法,用于将对象转换为整数类型的值,以便于在进行哈希操作时使用。在该方法中,我们将对象的 id 属性作为哈希值,并返回该值。

  • __bool__ 方法也是一个特殊方法,用于将对象转换为布尔类型的值。在该方法中,我们判断对象的 id 属性是否为 0,如果为 0,则返回 False,否则返回 True。

  • __main__ 中,我们定义了一个空字典 one_dict,并创建了两个 MyClass 类的实例对象 obj1obj2,分别传入不同的参数值。然后,我们向字典中添加了两个键值对,其中键为 obj1obj2,值分别为 100 和 200。由于 MyClass 类已经重载了 __hash__ 方法,因此可以将其作为字典的键。

  • 最后,我们分别输出了字典中 obj1obj2 对应的值,并将整个字典进行了输出。然后,我们对 obj1 进行了布尔类型转换,并根据其结果输出不同的信息。

  • 总的来说,该程序演示了如何定义一个自定义类,并进行哈希和布尔类型转换操作,以及如何将自定义类的对象作为字典的键进行存储和访问。

序列类型的魔法方法

__len__(self)

定义当被 len() 调用时的行为(返回容器中元素的个数)

__getitem__(self, key)

定义获取容器中指定元素的行为,相当于 self[key]

__setitem__(self, key, value)

定义设置容器中指定元素的行为,相当于 self[key] = value

__delitem__(self, key)

定义删除容器中指定元素的行为,相当于 del self[key]

__iter__(self)

定义当迭代容器中的元素的行为

__reversed__(self)

定义当被 reversed() 调用时的行为

__contains__self, item)

定义当使用成员测试运算符(in 或 not in)时的行为

  • index: 查找序列中指定元素的位置。

  • count: 统计序列中指定元素的个数。

class MySequence:


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

    # def __iter__(self):
    #     return iter(self.data)

    def __getitem__(self, item):
        return self.data[item]

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

    def __setitem__(self, key, value):
        self.data[key] = value

    def __delitem__(self, key):
        del self.data[key]

    def __contains__(self, item):
        return item in self.data

    def __reversed__(self):
        return reversed(self.data)

    def index(self, value):
        return self.data.index(value)

    def count(self, value):
        return self.data.count(value)

    def __str__(self):
        return 'MySequence[]'

    def __del__(self):
        print("对象被删除")


if __name__ == '__main__':
    one_lst = [10, 20, 30, 40, 10, 20, 30]
    obj1 = MySequence(one_lst)
    print(len(obj1))  # __len__(self)
    print(obj1[1])    # __getitem__(self, item):
    obj1[0] = 100     # __setitem__(self, key, value)
    del obj1[1]       # __delitem__(self, key)
    print(obj1[1:2])  # __getitem__(self, item)
    for i in obj1:    # __getitem__(self, item)
        print(i)
    print(100 in obj1)  # __contains__(self, item)
    print(list(obj1))   # __len__(self) , __getitem__(self, item):
    print(list(reversed(obj1)))  #  __reversed__(self)
    print(obj1.index(100))       # index(self, value):
    print(obj1.count(100))       # count(self, value):
    print(obj1)      # __str__(self):
    del obj1         # __del__(self):

# 输出
7
20
[30]
100
30
40
10
20
30
True
[100, 30, 40, 10, 20, 30]
[30, 20, 10, 40, 30, 100]
0
1
MySequence[]
对象被删除

更多的魔术方法参考地址:https://www.cnblogs.com/nmb-musen/p/10861536.html