파이썬에서 클래스 생성자의 __new__ 메서드의 기능은 무엇인가요?
Python 클래스의 일부 메서드 이름과 속성 이름은 앞뒤에 __ 이중 밑줄이 옵니다. 이러한 메서드와 속성은 일반적으로 Python의 특수 메서드 및 속성입니다. 이러한 메서드를 재정의하거나 직접 호출하여 특수 기능을 구현합니다. 오늘은 실제 프로그램에서 __new__ 구축 방법의 적용 시나리오에 대해 이야기해 보겠습니다.
우리는 일반적인 초기화 __init__ 메소드를 알고 있으며 이를 다시 작성하여 원하는 초기화 로직을 구현할 수 있습니다. 최근에는 데이터 리소스 로딩 및 캐싱 메커니즘 구현과 같은 실제 비즈니스 개발 과정에서 일종의 문제가 발생했습니다. 그 중 생성자는 __init__() 및 __new__입니다. 합리적으로 사용하면 프로그램 성능이 효과적으로 향상됩니다.
모두가 깊이 이해하고 각자의 비즈니스 요구에 따라 유연하게 사용하여 코드를 더욱 우아하게 만들 수 있기를 바랍니다.
다음으로, 클래스 초기화 과정에서 __new__ 메소드의 존재에 대해 예제를 통해 점차 자세히 설명하겠습니다!
class Solution(object): def __init__(self, name=None,data=None): self.name = name self.data = data #初始化加载数据 self.xml_load(self.data) def xml_load(self,data): print("初始化init",data) def Parser(self): print("解析完成finish",self.name) a = Solution(name="A111",data=10) a.Parser() b = Solution(name="A112",data=20) b.Parser() # print(a)与 print(b)返回了类的名称和对象的地址 print(a) print(b) # 可以使用内置函数id()查看python对象的内存地址 print(id(a)) print(id(b)) 初始化init 10 解析完成finish A111 初始化init 20 解析完成finish A112 <__main__.Solution object at 0x0000024A3AF28D48> <__main__.Solution object at 0x0000024A3B055C48> 2517839809864 2517841042504
참고:
1) 코드 인스턴스화 클래스 프로세스
일반적으로 코드에서 클래스 인스턴스를 초기화하려면 __init__() 메서드를 사용하세요. , 실행된 첫 번째 호출은 __new__() 메서드가 정의된 클래스에서 재정의되지 않은 경우 Python은 기본적으로 부모 클래스의 __new__() 메서드를 호출하여 인스턴스를 생성합니다. 공간을 생성한 다음 매번 인스턴스화된 객체를 생성한 다음 열린 공간을 사용하여 인스턴스화된 객체를 저장합니다. 인스턴스화된 객체를 다시 생성할 때 새로운 방법을 사용하여 인스턴스화된 객체를 저장할 공간을 엽니다. 객체를 상속받은 클래스에만 이 메서드가 있다는 점에 유의하세요.
2), 메모리 주소와 객체는 서로 변환될 수 있습니다
#通过_ctypes的api进行对内存地址的对象 import _ctypes obj = _ctypes.PyObj_FromPtr(id(a)) #打印出来通过内存地址寻找到的对象 print(obj)
print(id(a)) 및 print(id(b)) 메모리 주소(십진수)를 인쇄하고, print(a) 및 print( b ) 클래스 이름과 객체의 주소가 반환되지만 둘은 동일하지 않습니다. 클래스가 인스턴스화될 때마다 다른 개체 주소가 생성되고 할당되므로 코드 인스턴스화 프로세스 중에 반환되는 클래스 개체의 주소 참조도 다릅니다.
class Solution: """ 注:new方法是为实例化对象创建空间的方法,现在new方法被改写,没有将实例化对象引用返回给python的解释器 无法为实例化对象创建空间存储,所以运行代码会报错。也没有完成初始化操作。 """ def __new__(cls, *args, **kwargs): print("对象创建空间") cls.instance = super().__new__(cls) print(cls.instance) # return cls.instance #若未返回实例对象引用,实例化方法将报错:AttributeError: 'NoneType' object has no attribute 'Parser' def __init__(self,name,data): self.name = name self.data = data self.xml_load(self.data) def xml_load(self,data): print("初始化init", data) def Parser(self): print("解析完成finish",self.data) a = Solution("A111",10) a.Parser() print(id(a))
참고:
1) __init__() 메소드와 __new__() 메소드
__new__() 메소드의 차이점은 인스턴스와 클래스를 생성하는 데 사용됩니다. 인스턴스화 이전에 먼저 호출되는 클래스 메서드이자 정적 메서드입니다. __init__() 메서드는 인스턴스를 초기화하는 데 사용됩니다. 이 메서드는 인스턴스 개체가 생성된 후 호출되며 클래스 인스턴스 개체의 일부 초기 값을 설정하는 데 사용됩니다.
클래스에 __init__() 메서드와 __new__() 메서드가 동시에 나타나면 __new__() 메서드가 먼저 호출된 다음 __init__() 메서드가 호출됩니다. __new__() 메서드는 인스턴스 생성의 첫 번째 단계입니다. 실행 후 생성된 클래스의 인스턴스를 반환해야 합니다. 그렇지 않으면 오류가 보고되고 __init__() 메서드를 실행할 수 없습니다. 그 중 __init__() 메서드는 어떤 정보도 반환하지 않습니다.
2), __new__() 메소드
def __new__(cls, *args, **kwargs): print(cls)# cls 代表的是Solution这个类本身<class'__ main __.Solution'> cls.instance = super().__new__(cls)# object().__ new __() print(cls.instance) return cls.instance
super() 및 object.__new__(cls)를 다시 작성하면 상위 클래스의 새 메소드를 호출하여 열 수 있습니다. 따라서 return을 추가해야 합니다. 코드의 실행 순서는 다음과 같습니다. 먼저 새 메서드를 실행한 다음 init 메서드를 실행하고 마지막으로 다른 메서드를 실행합니다.
싱글턴 패턴의 원래 정의는 "디자인 패턴"에 나와 있습니다. "클래스에 인스턴스가 하나만 있는지 확인하고 이에 액세스할 수 있는 전역 액세스 지점을 제공합니다."
싱글턴의 주요 용도는 시스템 로그 출력, 운영 체제의 작업 관리자 등과 같이 세계의 단 하나의 인스턴스에만 액세스할 수 있도록 해야 하는 상황.
class Solution: # 1、记录第一个被创建对象的引用,代表着类的私有属性 _instance = None # 静态变量 存储在类的命名空间里的 def __init__(self,name,data): self.name = name self.data = data self.xml_load(self.data) def __new__(cls, *args, **kwargs): # 2.判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间 if cls._instance == None: # 3.把类属性中保存的对象引用返回给python的解释器 cls._instance = object.__new__(cls)# 3 return cls._instance # 如果cls._instance不为None,直接返回已经实例化了的实例对象 else: return cls._instance# 必须把地址返回给new方法,让它有存储空间 def xml_load(self,data): print("初始化init",self.name,data) def Parser(self): print("解析完成finish",self.name) a = Solution("A11",10)#第一次开辟一个对象空间地址,后面创建都是在该地址上进行的 a.Parser() b = Solution("A12",20)#b把a覆盖掉 b.Parser() print(id(a)) print(id(b)) # 内存地址,而且它们的内存地址都是一样的 print(a.name) print(b.name)
初始化init A11 10 解析完成finish A11 初始化init A12 10 解析完成finish A12 2465140199816 2465140199816 A12 A12
참고:
1) 싱글톤 모드에는 항상 공간이 하나만 있으며 이 공간은 항상 재사용됩니다.
먼저 생성된 객체의 참조를 기록하기 위해 클래스의 private 속성인 _instance를 정의합니다. cls._instance가 None이면 클래스가 아직 인스턴스화되지 않았음을 의미하며, 클래스를 인스턴스화하고 인스턴스 객체를 반환합니다.
다음 데이터 테스트를 통해 print(obj.name, obj.data)의 마지막 출력이 A12임을 알 수 있습니다. "A11"이 처음 인쇄되면 속성이 비어 있습니다. 이 속성을 저장할 공간을 엽니다. 두 번째 적중 이후 공간이 열렸습니다. else 문을 실행하고 "A12"를 원래 공간으로 직접 반환하여 이전 표지 데이터를 덮어씁니다.
def task(id,data): obj = Solution("{0}".format(id), "{0}".format(data)) print(obj.name, obj.data) import threading ID=["A11","A12","A13","A14","A12"] DATA=[10,20,30,40,20] for i in range(5): t = threading.Thread(target=task(ID[i],DATA[i]), args=[i, ]) t.start()
<__main__.Solution object at 0x00000221B2129148> 初始化init A11 10 A11 10 初始化init A12 20 A12 20 初始化init A13 30 A13 30 初始化init A14 40 A14 40 初始化init A12 20 A12 20
2), 싱글턴 모드의 또 다른 구현 방법
def __new__(cls,*args,**kwargs): # hasattr查询目标并判断有没有,not1==1返回的是False # if语句后面的 # not 条件整体为True时,执行cls.instance = object....代码 # if语句后面的 # not 条件整体为False时,执行return代码 if not hasattr(cls,"instance"): # hasattr查、判断的作用 cls.instance = object.__new__(cls) return cls.instance
위에서는 싱글톤 모드 개체 공간의 재사용을 실현하지만 때로는 시스템 리소스를 낭비하는 빈번한 요청(예: 데이터베이스 연결 요청 데이터)을 피하기 위해 초기화 프로세스를 한 번만 로드하고 싶을 때도 있습니다.
class Solution: #定义类变量 # 记录第一个被创建对象的引用,代表着类的私有属性 _instance = None #记录是否执行过初始化动作 init_flag = False def __init__(self,name,data): self.name = name self.data = data #使用类名调用类变量,不能直接访问。 if Solution.init_flag: return self.xml_load(self.data) # 修改类属性的标记 Solution.init_flag = True def __new__(cls, *args, **kwargs): # 判断该类的属性是否为空;对第一个对象没有被创建,我们应该调用父类的方法,为第一个对象分配空间 if cls._instance == None: # 把类属性中保存的对象引用返回给python的解释器 cls._instance = object.__new__(cls) return cls._instance #如果cls._instance不为None,直接返回已经实例化了的实例对象 else: return cls._instance def xml_load(self,data): print("初始化init",self.name,data) def Parser(self): print("解析完成finish",self.name) a = Solution("A11",10)#第一次实例化对象地址,后面创建都是在该地址上进行的 a.Parser() b = Solution("A12",20)#b把a覆盖掉 b.Parser() print(id(a)) print(id(b)) print(a.name) print(b.name)rrree
참고:
1) 싱글톤 모드에서는 초기화 프로세스를 한 번만 로드하세요.
这时候我们在类空间中再添加一个init_flag属性来记录是否已经执行过初始化操作即可实现加载一次初始化过程。从以上两次实例化过程结果来看,对象引用地址不变,结果被最后一次实例化数据覆盖且初始化init只被打印一次。
2)、单例模式下一次资源加载注意点
单例模式下控制类仅进行一次初始化过程适用于资源一次性加载进缓存的过程,对于多进程应用可采用多例模式实现。
多个实例对象空间引用地址完全独立,从而保持避免不同请求资源不被占用。将同一个对象请求归为同一个实例。
class Solution: ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用 _loaded = {} def __init__(self,name,data): self.name = name self.data = data self.xml_load(self.data) def __new__(cls, name,*args): if cls._loaded.get(name) is not None: client = cls._loaded.get(name) print(f"已经存在访问对象 {name}") print(client) return client # 把类属性中保存的对象引用返回给python的解释器 print(f"正在创建访问对象 {name}") client = super().__new__(cls) # 为该类实例name添加一个空间对象地址引用 print(client) cls._loaded[name] = client return client def xml_load(self,data): print("初始化init",self.name,data) def Parser(self): print("解析完成finish",self.name) if __name__ == '__main__': print("多例模式实例") a = Solution("A11",10) a.Parser() b = Solution("A11",10) b.Parser() c = Solution("A12", 20) c.Parser() print(f"{a is b}") print(a.name) print(b.name) print(c.name)
注:
1)、多例模式始终具有多个空间,不同空间完全独立。
我们在类空间中定义类实例化对象字典,即建立不同的实例对象和对象空间地址引用键值对,从而实现多例模式。通过类字典判断实例对象是否创建,节省创建的成本。
2)、多例模式测试过程
当创建相同的实例对象name="A11"时,程序首先在实例池中搜索cls._loaded.get(name),若存在则直接返回已创建的实例对象空间。多例模式完美的实现了不同访问对象具体不同的实例化对象地址。
多例模式实例 正在创建访问对象 A11 <__main__.Solution object at 0x000001C105AA5EC8> 初始化init A11 10 解析完成finish A11 已经存在访问对象 A11 <__main__.Solution object at 0x000001C105AA5EC8> 初始化init A11 10 解析完成finish A11 正在创建访问对象 A12 <__main__.Solution object at 0x000001C105AA5F88> 初始化init A12 20 解析完成finish A12 True A11 A11 A12
3)、多例模式下缓冲机制的实现
进一步优化多例模式初始化过程,比如读取文件或者数据库时仅进行一次初始化加载。
class Solution: ##定义类实例化对象字典,即不同的实例对象对应不同的对象空间地址引用 _loaded = {} def __new__(cls, name,data,*args): if cls._loaded.get(name) is not None: client = cls._loaded.get(name) print(f"已经存在访问对象 {name}") print(client) return client print(f"正在创建访问对象 {name}") # 把类属性中保存的对象引用返回给python的解释器 client = super().__new__(cls) print(client) # 为该类实例name添加一个空间对象地址引用 cls._loaded[name] = client client._init_db(name,data) return client def _init_db(self,name,data): self.name = name self.data = data self.xml_load(self.data) def xml_load(self,data): print("初始化init",self.name,data) def Parser(self): print("解析完成finish",self.name) if __name__ == '__main__': print("多例模式实例-缓存") a = Solution("A11",10) a.Parser() b = Solution("A11",10) b.Parser() c = Solution("A12", 20) c.Parser() print(f"{a is b}") print(a.name) print(b.name) print(c.name)
多例模式实例 正在创建访问对象 A11 <__main__.Solution object at 0x0000024198989148> 初始化init A11 10 解析完成finish A11 已经存在访问对象 A11 <__main__.Solution object at 0x0000024198989148> 解析完成finish A11 正在创建访问对象 A12 <__main__.Solution object at 0x00000241989891C8> 初始化init A12 20 解析完成finish A12 True A11 A11 A12
注:多例模式下多个实例化对象均只进行一次初始化过程。
重写__new__方法中每个实例对象创建后绑定初始化_init_db()方法执行一次,后面遇到同一个实例对象将不会发生什么,直接返回已创建的实例对象。从测试结果来看,创建相同的实例对象name="A11"时,第二次将略过初始化数据加载过程,很好的实现了缓存机制。
本文结合项目背景详细介绍了__new__方法实现单例模式和多例模式以及缓存机制的实现!
1、__new__ 方法是在类创建实例的时候自动调用的。
2、 实例是通过类里面的 __ new __ 方法是在类创建出来的。
3、 先调用__new__ 方法创建实例,再调用 __ init __方法初始化实例。
4、 __new__ 方法,后面的括号里面的cls代表的是类本身。
5、__new__ 方法,判断类属性为空就去开辟空间,否则复用原来的地址。
更多的特殊方法比如1、自我描述方法:__repr__2、析构方法:__del__ 3、列出对象所有属性(包括方法)名:__dir__4、__dict__属性:查看对象内部所有属性名和属性值组成的字典5、__getattr____setattr__等。
当然还有metaclass类的__new__方法,可以动态修改程序中的一批类,这个功能在开发一些基础性的框架时非常有用,可以使用metaclass为某一批需要通用功能的类添加方法,若有合适实例场景后续分享。
위 내용은 Python에서 __new__ 메소드의 역할을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!