IT科技类资讯

Python 中类的构造方法 __New__ 的妙用

时间:2010-12-5 17:23:32  作者:域名   来源:系统运维  查看:  评论:0
内容摘要:Python 的类中,所有以双下划线__包起来的方法,叫魔术方法,魔术方法在类或对象的某些事件发出后可以自动执行,让类具有神奇的魔力,比如常见的构造方法__new__、初始化方法__init__、析构

Python 的中类造方类中,所有以双下划线__包起来的构的妙方法,叫魔术方法,中类造方魔术方法在类或对象的构的妙某些事件发出后可以自动执行,让类具有神奇的中类造方魔力,比如常见的构的妙构造方法__new__、初始化方法__init__、中类造方析构方法__del__,构的妙今天来聊一聊__new__的中类造方妙用,主要分享以下几点:

__new__ 和 __init__ 的构的妙区别 应用1:改变内置的不可变类型 应用2:实现一个单例 应用3:客户端缓存 应用4:不同文件不同的解密方法 应用5:Metaclasses

__new__ 和 __init__ 的区别

1、调用时机不同:new 是中类造方真正创建实例的方法,init 用于实例的构的妙初始化,new 先于 init 运行。中类造方

2、网站模板构的妙返回值不同,中类造方new 返回一个类的实例,而 init 不返回任何信息。

3、new 是 class 的方法,而 init 是对象的方法。

示例代码:

class A:     def __new__(cls, *args, **kwargs):         print("new", cls, args, kwargs)         return super().__new__(cls)     def __init__(self, *args, **kwargs):         print("init", self, args, kwargs) def how_object_construction_works():     x = A(1, 2, 3, x=4)     print(x)         print("===================")     x = A.__new__(A, 1, 2, 3, x=4)     if isinstance(x, A):         type(x).__init__(x, 1, 2, 3, x=4)     print(x) if __name__ == "__main__":     how_object_construction_works() 

上述代码定义了一个类 A,在调用 A(1, 2, 3, x=4) 时先执行 new,再执行 init,等价于:

x = A.__new__(A, 1, 2, 3, x=4) if isinstance(x, A):     type(x).__init__(x, 1, 2, 3, x=4) 

代码的运行结果如下:

new <class __main__.A> (1, 2, 3) { x: 4} init <__main__.A object at 0x7fccaec97610> (1, 2, 3) { x: 4} <__main__.A object at 0x7fccaec97610> =================== new <class __main__.A> (1, 2, 3) { x: 4} init <__main__.A object at 0x7fccaec97310> (1, 2, 3) { x: 4} <__main__.A object at 0x7fccaec97310> 

new 的主要作用就是让程序员可以自定义类的创建行为,以下是其主要应用场景:

应用1:改变内置的不可变类型

我们知道,元组是不可变类型,但是我们继承 tuple ,然后可以在 new 中,对其元组的元素进行修改,因为 new 返回之前,元组还不是亿华云计算元组,这在 init 函数中是无法实现的。比如说,实现一个大写的元组,代码如下:

class UppercaseTuple(tuple):     def __new__(cls, iterable):         upper_iterable = (s.upper() for s in iterable)         return super().__new__(cls, upper_iterable)     # 以下代码会报错,初始化时是无法修改的     # def __init__(self, iterable):     #     print(finit { iterable})     #     for i, arg in enumerate(iterable):     #         self[i] = arg.upper() if __name__ == __main__:     print("UPPERCASE TUPLE EXAMPLE")     print(UppercaseTuple(["hello", "world"])) # UPPERCASE TUPLE EXAMPLE # (HELLO, WORLD) 

应用2:实现一个单例

class Singleton:     _instance = None     def __new__(cls, *args, **kwargs):         if cls._instance is None:             cls._instance = super().__new__(cls, *args, **kwargs)         return cls._instance if __name__ == "__main__":     print("SINGLETON EXAMPLE")     x = Singleton()     y = Singleton()     print(f"{ x is y=}") # SINGLETON EXAMPLE # x is y=True 

应用3:客户端缓存

当客户端的创建成本比较高时,比如读取文件或者数据库,可以采用以下方法,同一个客户端属于同一个实例,节省创建对象的成本,这本质就是多例模式。

class Client:     _loaded = { }     _db_file = "file.db"     def __new__(cls, client_id):         if (client := cls._loaded.get(client_id)) is not None:             print(f"returning existing client { client_id} from cache")             return client         client = super().__new__(cls)         cls._loaded[client_id] = client         client._init_from_file(client_id, cls._db_file)         return client     def _init_from_file(self, client_id, file):         # lookup client in file and read properties         print(f"reading client { client_id} data from file, db, etc.")         name = ...         email = ...         self.name = name         self.email = email         self.id = client_id if __name__ == __main__:     print("CLIENT CACHE EXAMPLE")     x = Client(0)     y = Client(0)     print(f"{ x is y=}")     z = Client(1) # CLIENT CACHE EXAMPLE # reading client 0 data from file, db, etc. # returning existing client 0 from cache # x is y=True # reading client 1 data from file, db, etc. 

应用4:不同文件不同的解密方法

先在脚本所在目录创建三个文件:plaintext_hello.txt、rot13_hello.txt、otp_hello.txt,程序会根据不同的文件选择不同的解密算法

import codecs import itertools class EncryptedFile:     _registry = { }  # rot13 -> ROT13Text     def __init_subclass__(cls, prefix, **kwargs):         super().__init_subclass__(**kwargs)         cls._registry[prefix] = cls     def __new__(cls, path: str, key=None):         prefix, sep, suffix = path.partition(":///")         if sep:             file = suffix         else:             file = prefix             prefix = "file"         subclass = cls._registry[prefix]         obj = object.__new__(subclass)         obj.file = file         obj.key = key         return obj     def read(self) -> str:         raise NotImplementedError class Plaintext(EncryptedFile, prefix="file"):     def read(self):         with open(self.file, "r") as f:             return f.read() class ROT13Text(EncryptedFile, prefix="rot13"):     def read(self):         with open(self.file, "r") as f:             text = f.read()         return codecs.decode(text, "rot_13") class OneTimePadXorText(EncryptedFile, prefix="otp"):     def __init__(self, path, key):         if isinstance(self.key, str):             self.key = self.key.encode()     def xor_bytes_with_key(self, b: bytes) -> bytes:         return bytes(b1 ^ b2 for b1, b2 in zip(b, itertools.cycle(self.key)))     def read(self):         with open(self.file, "rb") as f:             btext = f.read()         text = self.xor_bytes_with_key(btext).decode()         return text if __name__ == "__main__":     print("ENCRYPTED FILE EXAMPLE")     print(EncryptedFile("plaintext_hello.txt").read())     print(EncryptedFile("rot13:///rot13_hello.txt").read())     print(EncryptedFile("otp:///otp_hello.txt", key="1234").read()) # ENCRYPTED FILE EXAMPLE # plaintext_hello.txt # ebg13_uryyb.gkg # ^FCkYW_X^GLE 

应用5:Metaclasses

metaclass 可以像装饰器那样定制和修改继承它的子类,前文Python黑魔法之metaclass

本文转载自微信公众号「Python七号」,可以通过以下二维码关注。转载本文请联系Python七号公众号。

copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap