zoukankan      html  css  js  c++  java
  • 类的实例化(过程)

    一、call

    对象后面加括号时,触发执行。
    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,
    即:对象() 调用它的类的__call__
    或者 类() 调用它的元类的__call__

    class BBJ(type):#自定义元类必须继承type
        def __call__(self, *args, **kwargs):
            print(111)
    class Foo(metaclass=BBJ):#metaclass指定元类
        def __init__(self):
            print('__init__触发了')
        def __call__(self, *args, **kwargs):
            print('__call__触发了')
    Foo()  # 执行 __init__
    
    class Foo:
        def __init__(self):
            print('__init__触发了')
    
        def __call__(self, *args, **kwargs):
    
            print('__call__触发了')
    obj = Foo()  # 执行 __init__
    ===>__init__触发了
    obj()  # 执行 __call__
    ===>__call__
    

    二、init和new

    曾经我幼稚的以为认识了python的init()方法就相当于认识了类构造器,结果,new()方法突然出现在我眼前,让我突然认识到原来new才是老大。为什么这么说呢?

    我们首先得从new(cls[,…])的参数说说起,new方法的第一个参数是这个类,而其余的参数会在调用成功后全部传递给init方法初始化,这一下子就看出了谁是老子谁是小子的关系。

    所以,new方法(第一个执行)先于init方法执行:

    class A:
        pass
    
    
    class B(A):
        def __new__(cls):
            print("__new__方法被执行")
            return super().__new__(cls)
    
        def __init__(self):
            print("__init__方法被执行")
    
    
    b = B()
    ====>__new__方法被执行
    ====>__init__方法被执行
    
    

    我们比较两个方法的参数,可以发现new方法是传入类(cls),而init方法传入类的实例化对象(self),而有意思的是,new方法返回的值就是一个实例化对象(ps:如果new方法返回None,则init方法不会被执行,并且返回值只能调用父类中的new方法,而不能调用毫无关系的类的new方法)。我们可以这么理解它们之间的关系,new是开辟疆域的大将军,而init是在这片疆域上辛勤劳作的小老百姓,只有new执行完后,开辟好疆域后,init才能工作。

    绝大多数情况下,我们都不需要自己重写new方法,但在当继承一个不可变的类型(例如str类,int类等)时,它的特性就尤显重要了。我们举下面这个例子:

    class CapStr(str):
        def __init__(self, string):
            string = string.upper()
    
    
    a = CapStr("I love China!")
    print(a)
    ====>I love China!
    
    class CapStr(str):
        def __new__(cls, string):
            string = string.upper()
            return super().__new__(cls, string)
    
    
    a = CapStr("I love China!")
    print(a)
    =====>I LOVE CHINA!
    

    我们可以根据上面的理论可以这样分析,我们知道字符串是不可改变的,所以第一个例子中,传入的字符串相当于已经被打下的疆域,而这块疆域除了将军其他谁也无法改变,init只能在这块领地上干瞪眼,此时这块疆域就是”I love China!“。而第二个例子中,new大将军重新去开辟了一块疆域,所以疆域上的内容也发生了变化,此时这块疆域变成了”I LOVE CHINA!“。
    总结:

    在我们实例化一个类时,其实第一个调用的是类的元类的__call__,在这个__call__里面再调用类的__new__的到实例化的对象,然后调用类的__init__对对象进行初始化,最后将对象retrun出去,就得到我们的对象了
    
  • 相关阅读:
    使用HttpClient短信网关接口实现手机号验证码注册
    Linux安装nginx、redis(在线安装)
    Linux安装Tomcat(本地安装)
    Linux搭建java运行环境(本地安装)
    Linux的安装注意事项
    Linux的Shell常用命令
    applicationContext.xml的复用(import resource)
    Redis解决Session共享问题(如果开启nginx,实现负载均衡)
    Redis缓存商品查询信息(SpringMVC)
    Spring整合Redis
  • 原文地址:https://www.cnblogs.com/qiukangle/p/14248135.html
Copyright © 2011-2022 走看看