• python的new与init


    基于文章:Why is init() always called after new()?

    特别说明:

    1. 这篇文章的灵感来源于stackoverflow的一个提问,准确说,我只是做了些知识梳理的工作,对其中的优秀回答进行了总结和提炼,旨在帮助自己和读者对此有个深刻的认识。

    2. 本文章节中的new是__new__的简写,init是__init__的简称,只是为了语言叙述的方便而做出的省略。

    Part I: 为什么new总是先于init?

    new是静态的类方法,static method。
    init是实例方法

    它们有不同的参数与返回值:

    1. new的参数总是:cls 返回值总是self
    2. init的参数总是:self 总是无返回值

    Part II: 三段代码的思考

    Block 1: 基于原文的代码

    class B(object):
        _dict = dict()
    
        def __new__(cls):
            if 'key' in B._dict:
                print("EXISTS:", B._dict['key'])
                return B._dict['key']
            else:
                print("NEW")
                return super(B, cls).__new__(cls)
    
        def __init__(self):
            print("INIT")
            B._dict['key'] = self
            print("")
    
    
    b1 = B()
    b2 = B()
    b3 = B()
    
    # 运行结果如下:
    NEW
    INIT
    
    EXISTS: <__main__.B object at 0x0000028F29820828>
    INIT
    
    EXISTS: <__main__.B object at 0x0000028F29820828>
    INIT
    
    

    关于原文中的代码解释:

    在每次实例化之前,也就是new的操作时,都查询了是否存在第一个实例。在第一次初始化时,把第一个实例self赋值给了类的公有属性:test_dict。之后的每次实例化之前,new都会查询到它(实例)已经存在,new并且总是返回了第一个实例给了init。然后init参与对该实例(总是第一个实例)进行构建工作。

    我只能揣测,原问题的作者似乎意外创建了一个单例的类。你可以在init中执行print(self),会发现打印出来的对象总是第一个实例。当然,如果确实是想要实现单例模式,应该使用装饰器。即:

    def singleton(cls):
        instances = {}
        def getinstance():
            if cls not in instances:
                instances[cls] = cls()
            return instances[cls]
        return getinstance
    
    @singleton
    class MyClass:
      ...
    

    为什么instances使用的是字典?因为Python总是用字典去存储一个新的实例,并且init中的属性也同样以字典的方法存储的。当然,如果你想节约内存,不使用字典而用列表,可以重载new方法,或者使用__slot__

    Block 2: 基于Block 1 的修改

    class A(object):
        test_dict = dict()                   
    
        def __new__(cls):                    
            if 'key' in A.test_dict:
                print("EXISTS:", A.test_dict['key'])
                return A.test_dict['key']
            else:
                print("NEW")
                return super(A, cls).__new__(cls)
    
        def __init__(self):                  
            print("INIT")
            if 'key' in A.test_dict:
                print('修改')
                A.test_dict['key'] += 1
            else:
                print('第一次创建')
                A.test_dict['key'] = 0
            print('')
    
    
    a1 = A()
    a2 = A()
    a3 = A()
    

    Block 3: 基于Block 2 的修改

    class A(object):
        test_dict = dict()                    1.类的公有属性
    
        def __new__(cls):                     2.执行__new__
            if 'key' in A.test_dict:
                print("EXISTS:", A.test_dict['key'])
                return super(A, cls).__new__(cls)
            else:
                print("NEW")
                return super(A, cls).__new__(cls)
    
        def __init__(self):                  
            print("INIT")
            if 'key' in A.test_dict:
                print('修改')
                A.test_dict['key'] += 1
            else:
                print('第一次创建')
                A.test_dict['key'] = 0
            print('')
    
    
    a1 = A()
    a2 = A()
    a3 = A()
    
    

    Q&A

    1. new 总是先于 int 执行,为什么?
      答:
      注意事项: new是类的静态方法,
      Use new when you need to control the creation of a new instance.
      Use init when you need to control initialization of a new instance.
      前者用于控制创建一个新实例,后者用于控制对该新实例进行初始化。

    精要:
    它们负责的工作不同:前者负责创建新实例,后者负责实例初始化
    它们服务的层级不同:前者是cls(类),后者是self(实例本身)
    __new__是秘书给领导打工,后者领导的一众小弟们打工。

    1. 为什么上述代码的__init__没有被执行,而下面的每次都正常执行了?
      答:
      用于我在上述代码中恶意使用了new, 没有对new进行返回实例。
      new__总是第一个被Call(调用),并且他的返回值:当且仅当是一个新的实例!!
      代码1中,并没有返回这个新的实例,第一次实例化对象,自动进行了__int
      ,后面的,
      都是只创建了新示例
  • 相关阅读:
    RuntimeError: cryptography is required for sha256_password or caching_sha2_p
    Django-C003-视图
    MySQL 实时监控日志
    ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061) : 第一次设置MySQL也适用
    Django-C002-深入模型,到底有多深
    ubuntu16.04下安装&配置anaconda+tensorflow新手教程
    人脸算法系列:MTCNN人脸检测详解
    YOLO系列:YOLOv1,YOLOv2,YOLOv3,YOLOv4,YOLOv5简介
    python __getitem__()方法理解
    启动scala的方法
  • 原文地址:https://www.cnblogs.com/crb912/p/9712017.html
Copyright © 2020-2023  润新知