Python魔法方法与单例模式详解

小林子
小林子
发布时间:2026-10-14 | 5人浏览过
原创

本文深入解析Python中常用的魔法方法,包括__init__、__new__、__call__、__str__、__del__、上下文管理器及属性访问系列方法,并详细阐述单例模式的三种实现方式:模块导入、__new__重写及自定义元类,帮助开发者掌握Python核心机制。

前言

本文旨在介绍Python中常用的魔法方法,并探讨面向对象编程中至关重要的单例模式实现。

魔法方法

在Python中,一切事物皆被视为对象,因为Python是一种面向对象的编程语言。Python为类和对象提供了大量内置方法,这些方法通常被称为“魔法方法”。它们会在特定条件下自动触发执行,行为如同魔法一般。

__init__方法

该方法用于接收由__new__方法返回的空对象,并对其进行初始化操作,此方法不返回任何值。


class Test():
    def __init__(self, name):
        self.name = name
        
    def test(self):
        print(self.name)
   
t = Test('xu')
t1 = Test('python')

__new__方法

当类被调用以实例化对象时,__new__方法是首先被触发的方法,其作用是实例化一个空对象并将其返回。


class Test():
    def __new__(cls,*args, **kwargs):
        return object.__new__(cls, *args, **kwargs) 
    
    def __init__(self, name):
        self.name = name

__call__方法

若希望将一个对象变为可调用对象(即可以通过加括号进行调用),需要在该对象的类中定义__call__方法。调用该可调用对象时,返回的值即为__call__方法的返回值。


class Test():
    
    def __init__(self):
        self.name = 'python'
    
    def __call__(self, *args, **kwargs):  # self是Test类的对象
        print(self)  # <__main__.Test object at 0x000001C78CE78FD0>
        print(self.name)
        
t = Test()
t()  # python

__str__方法

当对象被访问并打印时,会触发此方法的执行。该方法必须返回一个字符串类型的值。


class Test():
    def __init__(self, name):
        self.name = name
 
    def __str__(self):
        return self.name
   
t = Test('xu')
print(t1)  # xu

__del__方法

当对象被删除时,__del__方法会自动触发。由于Python的垃圾回收机制会自动清理程序中不再使用的资源,因此如果对象仅占用应用程序资源,通常无需定义__del__方法。然而,若对象涉及操作系统资源(如已打开的文件对象),由于Python垃圾回收机制无法直接处理系统资源,此时需要为对象定义__del__方法,以便在对象被删除后自动触发以回收操作系统资源。


class Test:
    def __init__(self):
        self.x = open('a.txt',mode='w')
        # self.x = 占用的是操作系统资源

    def __del__(self):
        print('run')
        # 发起系统调用,告诉操作系统回收相关的系统资源
        self.x.close()

obj = T()
del obj # obj.__del__() 

__enter__与__exit__方法

在使用with上下文管理语句时,会触发对象中的__enter__方法,并将__enter__方法的返回值赋值给as子句声明的变量。当with语句正常结束时,会触发__exit__方法,该方法的三个参数分别代表异常类型、异常值和溯源信息。如果with语句代码块中出现异常,则with语句后的代码通常不会执行;但如果__exit__方法返回值为True,异常将被清除,with代码块后的代码仍会正常执行。相关代码如下:


class Open:
    def __init__(self):
        self.name = 'open'

    def __enter__(self):
        print('with语句执行时会首先执行的方法,返回值会赋值给as声明的变量')
        return self.name

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中的代码块执行完毕时执行exit')
        print(exc_type, '如果出现异常表示异常类型')
        print(exc_val, '表示异常的值')
        print(exc_tb, '表示异常的溯源信息')
        return 123  # 非零 非空 非None为真

    
with Open() as test:
    print(test)
    raise TypeError('看一下错误信息')
print('我会不会被执行呢')  # 当__exit__方法返回值为真时,会被执行,否则不会被执行

item系列方法

item系列方法包括__setitem__、__getitem__和__delitem__。这三种方法分别在中括号赋值或修改值、中括号取值以及中括号删除值时触发。例如,可以自定义一个字典类,并自定义中括号赋值、取值和删除值的行为:


class MyDict(dict):

    def __setitem__(self, key, value):
        print('执行setitem', key, value)  # 执行setitem, x, 1
        self.__dict__[key] = value

    def __getitem__(self, item):
        print('执行getitem', item)  # 执行getitem x
        print(self.__dict__[item])  # 1

    def __delitem__(self, key):
        print('执行delitem', key)  # 执行delitem x
        self.__dict__.pop(key)


d = MyDict()
d['x'] = 1
print(d['x'])
del d['x']

attr系列方法

attr系列方法包括__setattr__、__getattr__和__delattr__。其中,__setattr__在添加或修改属性时触发,__delattr__在删除属性时触发,而__getattr__在使用点号调用属性且该属性不存在时触发。具体代码如下:


class Test:
    def __init__(self):
        self.name = 'python'

    def __setattr__(self, key, value):
        print('添加/修改属性setattr')
        self.__dict__[key] = value
        # self.key = value  # 会出现无线递归,因为对象.属性会调用__setattr__方法

    def __delattr__(self, item):
        print('删除属性delattr')
        self.__dict__.pop(item)

    def __getattr__(self, item):
        print('属性不存在时调用getattr')
t = Test()
t.x = 'x'
print(t.y)
del t.x

单例模式

单例模式是一种软件设计模式,旨在保证无论调用多少次,一个类产生的对象都指向同一个内存地址,即整个系统中仅存在一个对象。实现单例模式的方式有多种,其核心原则是确保一个类只实例化一个对象,关键点在于如何判断该类是否已经实例化过对象。

以下介绍几种常见的实现方式,供参考:

模块导入的方式

这种方式的原理是:模块导入后仅运行一次,后续再次使用该模块中的类时,直接从内存中查找已有的实例。


# cls_singleton.py
class Foo(object):
    pass

instance = Foo()

# test.py
import cls_singleton

obj1 = cls_singleton.instance
obj2 = cls_singleton.instance
print(obj1 is obj2)  # True

通过__new__方法

其原理是判断类是否已有实例,若有则直接返回,若无则保存到_instance变量中。


class Test:

    _instance = None

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

    def __new__(cls, *args, **kwargs):
        # if cls._instance:
        #     return cls._instance                 # 有实例则直接返回
        # else:
        #     cls._instance = super().__new__(cls) # 没有实例则new一个并保存
        #     return cls._instance                 # 这个返回是给是给init,再实例化一次,也没有关系

        if not cls._instance:                         # 这是简化的写法,上面注释的写法更容易提现判断思路
            cls._instance = super().__new__(cls)
        return cls._instance


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True

自定义元类的方式

这种方式的原理基于类的调用过程:类定义时会调用元类下的__init__方法,而类被调用(即实例化对象)时会触发元类下的__call__方法。


class Mymeta(type):

    def __init__(cls, name, bases, dic):
        super().__init__(name, bases, dic)
        cls._instance = None                    # 将记录类的实例对象的数据属性放在元类中自动定义了

    def __call__(cls, *args, **kwargs):                   # 此call会在类被调用(即实例化时触发)
        if cls._instance:      # 判断类有没有实例化对象
            return cls._instance
        else:        # 没有实例化对象时,控制类造空对象并初始化
            obj = cls.__new__(cls, *args, **kwargs)
            obj.__init__(*args, **kwargs)
            cls._instance = obj             # 保存对象,下一次再实例化可以直接返回而不用再造对象
            return obj


class Test(metaclass=Mymeta):
    def __init__(self, name, age):
        self.name = name
        self.age = age


t1 = Test('python', 18)
t2 = Test('python1', 18)
print(t1 is t2)  # True

结语

至此,关于Python常用魔法方法(双下划线)的文章介绍完毕。如需了解更多Python魔法方法相关内容,请搜索本站历史文章或继续浏览相关文章,希望大家今后多多支持本站!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表