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

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

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内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python 用__new__方法实现单例的操作

    介绍 init 方法通常用在初始化一个类实例时候,但其实它不是实例化一个类的时候第一个被调用 的方法.当使用 Student(id, name) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 new 方法. new方法接受的参数虽然也是和init一样,但init是在类实例创建之后调用,而 new方法正是创建这个类实例的方法. new为对象分配空间,是内置的静态方法,new在内存中为对象分配了空间也返回了对象的引用,init获得了这个引用才初始化这个实例. 示例 一个非常简单的单例 cl

  • 用Python实现Newton插值法

    1. n阶差商实现 def diff(xi,yi,n): """ param xi:插值节点xi param yi:插值节点yi param n: 求几阶差商 return: n阶差商 """ if len(xi) != len(yi): #xi和yi必须保证长度一致 return else: diff_quot = [[] for i in range(n)] for j in range(1,n+1): if j == 1: for i in

  • Python中__new__和__init__的区别与联系

    __new__ 和 __init__ 的区别主要表现在: __new__ 负责对象的创建而 __init__ 负责对象的初始化. __new__:创建对象时调用,会返回当前对象的一个实例 __init__:创建完对象后调用,对当前对象的一些实例初始化,无返回值 1. 在类中,如果__new__和__init__同时存在,会优先调用__new__ class ClsTest(object): def __init__(self): print("init") def __new__(cl

  • python不同版本的_new_不同点总结

    我们都知道python的版本不同,在使用的时候就有所区别.鉴于我们推荐小伙伴们选择python3版本,所以这方面的区别了解的不是很多.就拿_new_来说,在python2和3中的写法是不一样的,之前有接触_new_的小伙伴想必没有注意到这个问题.接下来讲讲new的基本用法,然后就python不同版本中_new_的区别带来详解. new方法接受的参数虽然也是和init一样,但init是在类实例创建之后调用,而 new方法正是创建这个类实例的方法. class Person(object): "&q

  • Python函数__new__及__init__作用及区别解析

    [同] 二者均是Python面向对象语言中的函数,__new__比较少用,__init__则用的比较多. [异] __new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是个静态方法.__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常用在初始化一个类实例的时候.是一个实例方法. 也就是:__new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例

  • python__new__内置静态方法使用解析

    这篇文章主要介绍了python__new__内置静态方法使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用类名()创建对象时,python解释器会调用__new__方法来为对象分配空间.__new__是一个由object基类提供的内置静态方法,主要作用有两个: (1)在内存中为对象分配空间 (2)返回对象的引用 python的解释器在获得对象的引用之后,将引用作为第一个参数,传递给__intit__方法. 重写__new__方法的代码

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

    目录 1.概述 2.__new__ 和 __init__ 的区别 3.应用1:改变内置的不可变类型 4.应用2:实现一个单例 5.应用3:客户端缓存 6.应用4:不同文件不同的解密方法 1.概述 python 的类中,所有以双下划线__包起来的方法,叫魔术方法,魔术方法在类或对象的某些事件发出后可以自动执行,让类具有神奇的魔力,比如常见的构造方法__new__ .初始化方法__init__ .析构方法__del__ ,今天来聊一聊__new__的妙用,主要分享以下几点: __new__ 和 __

  • Python中类的定义、继承及使用对象实例详解

    本文实例讲述了Python中类的定义.继承及使用对象的方法.分享给大家供大家参考.具体分析如下: Python编程中类的概念可以比作是某种类型集合的描述,如"人类"可以被看作一个类,然后用人类这个类定义出每个具体的人--你.我.他等作为其对象.类还拥有属性和功能,属性即类本身的一些特性,如人类有名字.身高和体重等属性,而具体值则会根据每个人的不同:功能则是类所能实现的行为,如人类拥有吃饭.走路和睡觉等功能.具体的形式如下: 例:类的概念: class 人类:             

  • python中类的一些方法分析

    本文实例分析了python中类的一些方法,分享给大家供大家参考.具体分析如下: 先来看看下面这段代码: class Super: def delegate(self): self.action() class Provider(Super): def action(self): print 'in Provider.action' x = Provider() x.delegate() 本文实例运行环境为Python2.7.6 运行结果如下: in Provider.action 在Super类

  • 对python中类的继承与方法重写介绍

    1.单继承 父类也叫基类 子类也叫派生类 如下所示,继承的关系: 继承的书写格式: class 子类(父类): 方法 实例: class Animal: def eat(self): print("-----吃-------") def drink(self): print("-----喝--------") class Dog(Animal): def drak(self): print("汪汪叫") a=Animal() a.eat() 孙类

  • 浅谈python 中类属性共享的问题

    感觉这种理解有问题,举个例子来说. class Dog(object): name = 'dog' def init(self): self.age = 18 d1 = Dog() d2 = Dog() 这里有两个实例 d1,d2 吧. d1.name # 输出 dogd2.name # 输出 dogd1.name = 'abc' d1.name # 输出 abcd2.name # 输出 dogDog.name # 输出 dog 原因是 d1.name 输出 dog 不是因为这个实例共享了类属性

  • python中类的输出或类的实例输出为<__main__类名 object at xxxx>这种形式的原因

    原因: __str__()这个特殊方法将对象转换为字符串的结果 效果图: 代码: # 定义一个Person类 class Person(object): """人类""" def __init__(self, name , age): self.name = name self.age = age p = Person('小黑',18) print(p) print('\n\n\n\n\n') # 定义一个Person类 class Person

  • Python中类的创建和实例化操作示例

    本文实例讲述了Python中类的创建和实例化操作.分享给大家供大家参考,具体如下: python中同样使用关键字class创建一个类,类名称第一个字母大写,可以带括号也可以不带括号: python中实例化类不需要使用关键字new(也没有这个关键字),类的实例化类似函数调用方式: # coding: utf-8 # 创建一个类,类名称第一个字母大写,可以带括号也可以不带括号 class Student(): student_count = 0 def __init__(self, name, sa

  • python中类与对象之间的关系详解

    在搜索平台上关于类以及对象都已经被霸屏了,主要的问题无非就是两个,一个是理解二者,另一个就是理解二者之间的使用关系,对于小编来说,两者统一跟大家讲清,相信也很难被大家消化,这不,给大家想出来比较好理解的方式,用最简单的话,快速交大家上手,可别不信,简单易懂内容如下. 二者关系: 女生口红是一种类,但是mac.完美日记是口红里的个体,被称作是对象.这就是二者之间的关系,有人理解成包含情况也可以. 定义类/对象: class 类名(父类): class Human(object): pass man

  • Python中类变量和实例变量的区别

    目录 一.前言 二.解决过程 三.总结 一.前言 有人提问了一个关于Python类变量和实例变量的问题,这里拿出来给大家分享下,一起学习. 是用英文提问的,当然了英文看上去也不难,有点二级英文基础,也看得懂,实在不行,在线翻译一下也问题不大了. 二.解决过程 这里主要涉及到三个部分,类属性,实例属性,及实例属性的引用对象指向性问题.在该例中counter为类属性,__first为实例属性,print函数中的counter为实例属性的引用对象指向性问题.当ExampleClass类实例化时,__i

  • python列表的构造方法list()

    前言: 在很多语言中都有这种情况,需要把一组数集中存储起来方便后面的使用,而且还要求这组数能够随意的去查询,取出,排序,删除等等.这里大家可能想到了数组这个概念,也就是其他语言中的array,但是在python中没有数组这个概念,与之相应的是列表,本篇文章就来说说列表这个语法. 首先看看列表的构造: list = [element1, element2, element3, ..., elementn] 列表可以存储整数.小数.字符串.列表.元组等任何类型的数据,并且同一个列表中元素的类型也可以

随机推荐