Python中的对象行为与特殊方法(一)对象的创建与销毁
Python中类调用__new__()类方法来创建实例,调用__init__()方法来初始化对象,对象的销毁则调用__del__()方法。
__new__()方法第一个参数为类cls,通常返回cls的一个实例,然后新实例的__init__()方法将以类似于__init__(self[, ...])的方式被调用,self是创建的新的实例,其它的参数和传递给__new__()的参数一样。
如果__new__()方法不返回cls 的实例,那么__init__()将不会被调用。
对于一个类来说:
1 2 3 4 5 | class Foo( object ): def __init__( self , name): self .name = name f = Foo( 'bar' ) |
当进行f = Foo('bar')的操作时,实际上执行以下步骤来创建和初始化实例:
1 2 3 | f = Foo.__new__(Foo, 'bar' ) if isinstance (f, Foo): Foo.__init__(f, 'bar' ) |
__new__(cls[, ...])
__new__()方法是一个静态类方法,以实例的类为第一个参数。其余的参数是传递给对象构造表达式的参数,与传递给__init__()的相同。
一般通过使用super(currentclass, cls).__new__(cls[, ...])来调用超类的__new__()方法创建类的一个新的实例。
用户自定义对象的时候,很少定义__new__()方法。如果定义的话,一般是在定义元类或者定义一些继承自不可变的内置数据类型(整数、字符串、元组等)的对象的时候。
定义一些不可变数据类型的时候,因为对象是不可变的,那么在__init__()中是无法对对象进行修改的,而__new__()方法是在实例创建之前执行的:
1 2 3 | class Upperstr( str ): def __new__( cls , value = ''): return str .__new__( cls , value.upper()) |
上述Upperstr类创造的字符串实例全是大写的:
1 2 3 | >>> u = Upperstr( 'hello world' ) >>> u 'HELLO WORLD' |
也可以在单例模式中使用__new__()方法创建唯一的实例,放在类变量中,以后每次创建都返回这一个实例:
1 2 3 4 5 | class Singleton( object ): def __new__( cls , * args, * * kwargs): if not hasattr ( cls , '_inst' ): cls ._inst = super (Singleton, cls ).__new__( cls , * args, * * kwargs) return cls ._inst |
__init__(self[, ...])
__init__()方法用来初始化对象的属性,在实例创建完成时调用。参数为传递给类构造器表达式的那些参数。如果基类具有__init__()方法,那么继承的类的__init__()方法,而且必须显式调用:BaseClass.__init__(self, [args...])。
构造器的一个特殊限制是不可以返回任何值,这样做将导致运行时抛出一个TypeError。
__init__()方法一般含有大量的self.attr = attr的赋值语句:
1 2 3 4 5 6 7 | class Foo( object ): def __init__( self , name, color, num = 1 ): self .name = name self .color = color self .num = num f = Foo( 'bar' , 'yellow' ) |
如果不想重复这样的赋值操作,有以下几个办法:
1 2 3 4 5 | class Foo( object ): def __init__( self , name, color, num = 1 ): print locals () f = Foo( 'bar' , 'yellow' ) |
__init__()方法的局部命名空间如下:
1 | { 'color' : 'yellow' , 'self' : <__main__.Foo object at 0x00000000055C4780 >, 'num' : 1 , 'name' : 'bar' } |
self变量为创建的实例本身,其余要给实例赋值的变量name,color,num均在其中。而实例属性是存储在实例的__dict__变量中,我们只需要将这些要赋值的变量放入__dict__即可:
1 2 3 4 | class Foo( object ): def __init__( self , name, color, num = 1 ): self .__dict__.update( locals ()) del self .__dict__[ 'self' ] |
1 2 3 4 5 6 7 | >>> f = Foo( 'bar' , 'yellow' ) >>> f.name 'bar' >>> f.color 'yellow' >>> f.num 1 |
这样当实例属性很多的时候,就简洁了很多。但当使用特性或者描述符来包装对象的属性时,会产生问题,因为特性或者描述符包装的属性并不保存于__dict__之中,解决方案是使用setattr内置函数:
1 2 3 4 5 6 7 8 | def attr_from_locals(locals_dict): self = locals_dict.pop( 'self' ) for k,v in locals_dict.items(): setattr ( self , k, v) class Foo( object ): def __init__( self , name, color, num = 1 ): attr_from_locals( locals ()) |
如果__init__()方法中使用了一些不用赋值给实例的本地变量的时候,再使用locals()会包含这些本地变量,我们可以再精准的传递__init__()中的参数。
通过一个函数f的f.__code__属性可以得到函数的字节码对象c,字节码对象的属性c.co_varnames可以得到局部变量名称的元组(函数参数在前,函数内局部变量在后),c.co_argcount可以得到函数位置参数的数量,利用切片可以去除函数内局部变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 | def attr_from_locals(locals_dict): self = locals_dict.pop( 'self' ) code = self .__init__.__func__.__code__ args = code.co_varnames[ 1 :code.co_argcount] for k in args: setattr ( self , k, locals_dict[k]) class Foo( object ): def __init__( self , name, color, num = 1 ): x = 1 attr_from_locals( locals ()) f = Foo( 'bar' , 'yellow' ) |
可以看到x变量并未赋值给实例:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> f.name 'bar' >>> f.color 'yellow' >>> f.num 1 >>> f.x Traceback (most recent call last): File "<pyshell#87>" , line 1 , in <module> f.x AttributeError: 'Foo' object has no attribute 'x' |
__del__(self)
当实例要被销毁的时候,会调用对象的__del__()方法。注意当使用del a这样的语句时,并不会调用__del__()方法,只是减少一个引用计数,当对象的引用计数减至0时,才会调用__del__()。
__del__()方法也很少有必要定义,定义了此方法的实例,无法被python的循环垃圾收集器收集。
需要执行清除操作时(关闭文件、关闭网络连接等)可以定义,当然更好的办法是定义一个close()方法显式的执行关闭操作。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探