Python---类的"__new__"和"__init__"方法

本文介绍一下类的构造函数和初始化函数,以及如何通过”魔术方法”定制一个功能强的类

1 . 类构造和初始化

先通过一段代码看看这两个方法的调用顺序:
class A(object):
    """"""
    #----------------------------------------------------------------------
    def __init__(self, *args, **kwargs):
        print("init_for:",self.__class__)
    def __new__(cls, *args, **kwargs):
        print("new_for:",cls)
        return object.__new__(cls, *args, **kwargs)

a = A()

上面代码输出为如下,可以看到,当通过类实例化一个对象的时候,”new“方法首先被调用,然后是”init“方法。

new_for: <class '__main__.A'>
init_for: <class '__main__.A'>

对于”new“和”init“可以概括为:

  • new”方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个”cls”对应的实例对象,所以说”new”方法一定要有返回值!
  • 对于”init”方法,是一个初始化的方法,”self”代表由类产生出来的实例对象,”init”将对这个对象进行相应的初始化操作。

    2 . _new_”是在新式类中新出现的方法,它有以下行为特性:

  • new” 方法是在类实例化对象时第一个调用的方法,将返回实例对象

  • new” 方法始终都是类的静态方法(即第一个参数为cls),即使没有被加上静态方法装饰器
  • 第一个参数cls是当前正在实例化的类,如果要得到当前类的实例,应当在当前类中的 “new” 方法语句中调用当前类的父类的” new” 方法

对于上面的第三点,如果当前类是直接继承自 object,那当前类的 “new” 方法返回的对象应该如下,new方法的递归调用总该有个终止对象,那就是object(所有新式类直接或间接的基类),

def __new__(cls, *args, **kwargs):
    # func_suite
return object.__new__(cls, *args, **kwargs)

3 . 下面我们进行” _new_”方法的重写,探索下它的一些特性,

如果(新式)类中没有重写”new”方法,Python默认是调用该类的直接父类的”new”方法来构造该类的实例,如果该类的父类也没有重写”new”,那么将一直按照同样的规则追溯至object的”new”方法。

而如果新式类中重写了”new”方法,那么可以选择任意一个其他的新式类(必须是新式类,只有新式类有”new”,因为所有新式类都是从object派生)的”new”方法来创建实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。

看一段例子代码:

class Foo(object):
    """
    Foo class
    """
    #----------------------------------------------------------------------
    def __new__(cls, *args, **kwargs):
        # obj = object.__new__(cls, *args, **kwargs)
        obj = Bar.__new__(cls, *args, **kwargs)
        # obj = super(Foo, cls).__new__(cls, *args, **kwargs)
        print("Call_new_Foo:", obj.__class__)
        return obj
    def __init__(self):
        print("Call_init_for_Foo:", self.__class__)

class Bar(Foo):
    """
    Bar class
    """
    #----------------------------------------------------------------------
    def __new__(cls, *args, **kwargs):
        obj = Car.__new__(cls, *args, **kwargs)
        print("Call_new_Bar:", obj.__class__)
        return obj
    def __init__(self):
        print("Call_init_Bar:", self.__class__)

class Car(object):
    """
    Car class
    """
    #----------------------------------------------------------------------
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        print("Call_new_Car:", obj.__class__)
        return obj
    def __init__(self):
       print("Call_init_Car:", self.__class__)

foo = Foo()
bar = Bar()
car = Car()

代码输出为:

Call_new_Car: <class '__main__.Foo'>
Call_new_Bar: <class '__main__.Foo'>
Call_new_Foo: <class '__main__.Foo'>
Call_init_for_Foo: <class '__main__.Foo'>
Call_new_Car: <class '__main__.Bar'>
Call_new_Bar: <class '__main__.Bar'>
Call_init_Bar: <class '__main__.Bar'>
Call_new_Car: <class '__main__.Car'>
Call_init_Car: <class '__main__.Car'>

对于上述代码的解释为: new方法是用来生成类实例的,而init方法则会在拿到类实例后进行一些初始化操作,对于是否调用类的init方法则是由new方法决定的啦,因为”new” 可以调用其他类的构造方法或者直接返回别的类创建的对象来作为本类的实例。

通常来说,新式类开始实例化时,”new”方法会返回cls(cls指代当前类)的实例,然后调用该类的”init”方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入”new”方法中接收的位置参数和命名参数。但是,如果”new”没有返回cls(即当前类)的实例,那么当前类的”init”方法是不会被调用的。看下面的例子:

class X(object):
    """
    X class
    """
    flag = 521
    #----------------------------------------------------------------------
    def __init__(self, *args, **kwargs):
        print("Call_init_from:", self.__class__)
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls, *args, **kwargs)
        print("Call_new_for:", cls.__class__)
        return obj

class Y(object):
    """
    Y class
    """
    #----------------------------------------------------------------------
    def __init__(self, *args, **kwargs):
        print("Call_init_from:", self.__class__)
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(X, *args, **kwargs)
        print("Call_new_for:", cls.__class__)
        return obj

y = Y()
print(type(y))
print(y.flag)

输出为:

Call_new_for: <class 'type'>
<class '__main__.X'>
521

可以看出,class Y 的init方法没有执行,原因是Y的new方法返回了一个class X类型的对象呀

4 . 使用new方法派生不可变类型,比如从float派生出一个子类,通过实现”_new_“方法:

class RoundFloat(float):
    """

    """
    #----------------------------------------------------------------------
    def __new__(cls, num):
        num = round(num, 2)
        print(num)
        return float.__new__(RoundFloat, num)
    def __init__(self, num):
        print(num)
        pass

输出为:

9.21
9.2143464
9.21

5 . 定制我们自己的类,比如实现一个迭代器,关于更多的object类的魔法方法可以参考:
object_model:iter

class Fibs(object):
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self): # 这里的环境是python3.x,如果是2.x的环境,那就是"next"方法名称啦
        self.a, self.b = self.b, self.a + self.b
        if self.a > 100000:
            raise StopIteration()
        return self.a

fib = Fibs()
for f in fib:
    print(f)
    if f > 20:
        break

输出为:

1   1   2   3   5   8   13

关于更多的object所含方法的解释可以去查看python原文档,哈哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值