iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Python 中类的构造方法 __New__的妙用
  • 672
分享到

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

2024-04-02 19:04:59 672人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

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

1、概述

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

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

2、__new__ 和 __init__ 的区别

  • 调用时机不同:new 是真正创建实例的方法,init 用于实例的初始化,new 先于 init 运行。
  • 返回值不同,new 返回一个类的实例,而 init 不返回任何信息。
  • 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 的主要作用就是让程序员可以自定义类的创建行为,以下是其主要应用场景:

3、应用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(f'init {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') 

4、应用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 

5、应用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. 

6、应用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 

到此这篇关于Python 中类的构造方法 New的妙用的文章就介绍到这了,更多相关Python 中类的构造方法 New内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python 中类的构造方法 __New__的妙用

本文链接: https://www.lsjlt.com/news/154920.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作