调用时机
创建类对象的过程中,首先调用new方法生成对象实例,然后自动(隐式)回调init方法。
为什么需要new方法?
init方法在实例化后执行,控制私有地址空间的值的初始化过程。
new方法在实例化前执行,控制类实例化的过程。有些属性在类实例化后将不可更改,只能在实例化过程去改变。不仅仅是操控属性,甚至改变输出,返回任意类实例。
new方法如何改变属性?
场景
如果子类继承了int、str、tuple这类不可变类型,可以通过new方法改变其属性
原理
不可变类型的特点是实例化后,属性无法被更改。如a = 10
,执行new的阶段会在内存中开辟了一个地址块存放10这个值,之后无法再修改这个地址块的值。因此自定义赋值逻辑应当在new方法中执行。
实现
class PositiveInteger(int):
"""构造一个永远为正的整数类型"""
def __new__(cls, value, *args, **kwargs):
# new方法开辟内存空间,存放value的值,此后不允许在修改这块内存空间的值
return super(PositiveInteger, cls).__new__(cls, abs(value), *args, **kwargs)
new方法如何改变返回的类实例?
场景
随便举几个例子:
- 单例模式(概念不再赘述,懂的都懂)
- 通过Book类创建对象,根据参数的类型和数量等自动分类为SportBook、ScienceBook、ArtBook
实现
class GlobalMaster(object):
"""构造一个单例模式的类
类属性instance保存本类的实例化对象,类属性和对象属性的区别此处不再赘述
"""
instance = None
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = super(GlobalMaster, cls).__new__(cls)
# 只要运行于同一个进程空间,类的实例化返回的永远是同一个对象
return cls.instance
class Book(object):
"""构造一个书籍类的实例化总入口,根据参数返回不同的书籍类对象
例子中省略了SportBook等类的创建,都是普通地`clss SportBook:`写法。
Book类承担的是分类功能,`a = Book("sport")`之后,a.rent()和a.return()等方法调用的都是SportBook的方法,而不是Book的方法。
"""
def __new__(cls, value, *args, **kwargs):
if value == "sport":
return super(Book, cls).__new__(SportBook)
elif value == "science":
return super(Book, cls).__new__(ScienceBook)
elif value == "art":
return super(Book, cls).__new__(ArtBook)
else:
raise ValueError(f"未知的书籍类型: {value}")