Python类的内置成员方法 __init__, __new__ 和 __call__

发布时间 2023-10-03 18:36:20作者: Gensokyo_Alice

__init__ 和 __new__

这个方法想必大家平常也用的很多,这个方法负责对象的初始化。
什么是初始化呢?就是对已经存在的东西赋若干个初始值。
所以我们可以知道当我们调用 __init__() 方法的时候,这个类已经被实例化了。

我们可以运行一下如下代码

class A():
    def __new__(cls, *args, **kwargs):
        print("run the new of A")
        # return super().__new__(cls) 两句等价
        return object.__new__(cls)

    def __init__(self):
        print("run the init of A")

a = A()

然后我们就会发现,一个类的创建遵循如下步骤

  1. 调用 __new__() 方法,如果自身没有定义,就去父类中查找,直到 object 类
  2. __init__() 方法对实例化的对象本身进行初始化。

也就是实际上我们会先调用 __new__() 来实例化这个类,然后再去调用 __init__() 去对这个对象进行初始化。

然后我们看到 __new__() 传入了一个变量 cls 那么这个是什么呢?
答案是这个是这个类本身

不过我们可能会疑惑,为什么不是 self
答案是 self 代表的是实例对象本身,而不是类本身。

一般情况下,我们不会去重写 __new__ 方法,除非你明确知道自己在做什么。但是在进行一些设计模式的编写的,比如单例模式和工厂模式。

然后我们就可以发现,既然我可以传入自己的类,那么我是不是也可以传入别的类,答案是肯定的。不过这样并不会调用任何人的 __init__() 函数。所以需要你自己手动对对象进行初始化,一般情况下也不会有什么作用。

需要注意的事情是 __new__ 必须要有返回值,返回实例化出来的实例。

__call__

然后我们来看一下另一个内置成员方法 __call__
这个成员方法实际上就是让一个实例对象可以当作一个方法来调用
代码如下

class C:
    def __init__(self, name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print("I'm {}".format(self.name))
c = C("Gensokyo_Alice")
c()

那么这个除了当作方法调用还有什么用呢,众所周知python还有一个方法叫做 hasattr,不过它无法判断指定的名称是类方法还是类属性。
那么我们可以借助 __call__ 来判断。

class C:
    def __init__(self, name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print("I'm {}".format(self.name))

    def dosth(self):
        print("I'm {}".format(self.name))
c = C("Gensokyo_Alice")
if hasattr(c, "name"):
    print(hasattr(c.name, "__call__"))
if hasattr(c, "dosth"):
    print(hasattr(c.dosth, "__call__"))

因为类方法本质是一个可调用对象,所以实现了 __call__ 方法。
而类属性则没有。