Python中构造方法和初始化方法

原文链接https://blog.csdn.net/qq_19707521/article/details/79359858

类的实例化

在python中创建一个新式类时,一般都会定义一个 __init__ 方法,用来对类的实例进行初始化。但是 __init__ 方法并不是类的构造方法,类中真正的构造方法是 __new__ 方法。

看看下面的例子:

class Test:
    def __init__(self):
        print('__init__ method in {}'.format(self.__class__))

    def __new__(cls, *args, **kwargs):
        print('__new__ method in {}'.format(cls))
        return super(Test, cls).__new__(cls, *args, **kwargs)


test = Test()

# 输出为:
# __new__ method in <class '__main__.Test'>
# __init__ method in <class '__main__.Test'>

从上例可以看出,当实例化类时,类的 __new__ 方法先被调用,然后是 __init__ 方法。

一般来说, 类的 __new__ 方法和 __init__ 方法都会是下面的形式:

def __new__(cls, *args, **kwargs):
    # do something
    obj = ...
    return obj
    
def __init__(self, *args, **kwargs):
    # do something

对于类的 __new__ 方法和 __init__ 方法可以概括为:

  • __new__ 方法是Python的新式类中真正的构造方法,负责创建并返回实例,因此,__new__ 方法必须要有返回值;

  • __init__ 方法是初始化方法,负责初始化 __new__ 方法返回的实例对象。


类的 __new__ 方法的特性

__new__ 方法是新式类中的方法,它具有以下特性:

  • 是进行类的实例化时第一个被调用的方法,将返回实例对象;

  • 始终都是类方法(即第一个参数为 cls ),即使没有加上 @classmethod 装饰器;

  • 该方法的第一个参数 cls 代表当前正在实例化的类,如果要得到当前类的实例,应当调用当前类的父类的 __new__ 方法;


重写 __new__ 方法

如果(新式)类中没有重写 __new__ 方法,默认是调用该类的直接父类的 __new__ 方法来创建类的实例。如果该类的父类也没有重写__new__ 方法,那么将一直按照继承链向上追溯至 object 类的 __new__ 方法。

如果(新式)类中重写了 __new__ 方法,那么可以选择任意一个其他新式类(必须是新式类,只有新式类有 __new__ 方法,因为 object 类是所有新式类的基类)的 __new__ 方法来创建实例,其中包括这个新式类的父类和子孙类,只要它们不会造成递归死循环。

看看下面的例子:

class Animal(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        """
        这里的object.__new__(cls, *args, **kwargs) 也可以写成如下形式:
        1. super(Animal, cls).__new__(cls, *args, **kwargs)
        2. object.__new__(Animal, *args, **kwargs)
        3. Cat.__new__(cls, *args, **kwargs)
        4. People.__new__(cls, *args, **kwargs) 即使People类和Animal类没有关系,也是可以调用的;

        在任何新式类中,不能调用自身的 ‘__new__’ 方法来创建实例,因为这样会造成死循环。
        所以要避免如下形式的调用:
        return Animal.__new__(cls, *args, **kwargs)
        return cls.__new__(cls, *args, **kwargs)
        """

        print('call __new__ method for {}'.format(obj.__class__))
        return object


class Cat(Animal):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        print('call __new__ method for {}'.format(obj.__class__))
        return obj


class People(object):
    # 该类没有 '__new__' 方法,那么会自动调用其父类,即object类的 '__new__' 方法来创建实例。
    pass


class Girl(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(Cat)
        print('call __new__ method for {}'.format(obj.__class__))
        return obj


dog = Animal()
cat = Cat()
tina = Girl()

# 输出为:
# call __new__ method for <class '__main__.Animal'>
# call __new__ method for <class '__main__.Cat'>
# call __new__ method for <class '__main__.Cat'>

类的 __init__ 方法的调用

类的 __new__ 方法决定是否使用该类的 __init__ 方法,因为类的 __new__ 方法可以调用其他类的构造方法或者直接返回别的类实例作为本类的实例。

通常来说,新式类进行实例化时,__new__ 方法会返回当前类的实例,然后调用该类的 __init__ 方法进行实例的初始化。但是,如果 __new__ 方法返回的不是当前类的实例,那么,当前类的 __init__ 方法就不会被调用。

请看下面的例子:

class A:
    def __init__(self, *args, **kwargs):
        print('call __init__ method from {}'.format(self.__class__))

    def __new__(cls, *args, **kwargs):
        obj = super(A, cls).__new__(cls, *args, **kwargs)
        print('call __new__ method for {}'.format(obj.__class__))
        return obj


class B:
    def __init__(self, *args, **kwargs):
        print('call __init__ method from {}'.format(self.__class))

    def __new__(cls, *args, **kwargs):
        obj = super(B, cls).__new__(A, *args, **kwargs)
        print('call __new__ for {}'.format(obj.__class__))
        return obj

b = B()
print(type(b))

# 输出为:
# call __new__ for <class '__main__.A'>
# <class '__main__.A'>

可以看到,类B在实例化时并未调用自身的的初始化方法 __init__ 。因为类B的构造方法返回的是类A的实例。

猜你喜欢

转载自blog.csdn.net/swinfans/article/details/84887219