进阶学Python:Python类的__new__()! | 八月更文挑战

585 阅读5分钟

这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战

前言


三毛曾经感慨:

“心之何如,有似万丈迷津,横亘千里,其中并无舟子可以渡人,除了自渡,他人爱莫能助。” ——生命的洪流中,终其一生,我们都在学会怎么做自己的摆渡人。

关于安装和汉化可以观看博主的这篇文章《VsCode下载安装及汉化 》以及Python系列:windows10配置Python3.0开发环境!,安装完毕重启VsCode!

这次介绍一下关于类的魔法方法 __new__()方法Python3 中关于 object基类的__new__() 方法:

@staticmethod # known case of __new__
    def __new__(cls, *more): # known special case of object.__new__
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

__new__()创建和返回一个新的对象。通俗说:该魔法属性是用来创建实例对象的。接下来我们看一下它的是如何创建对象的。


Come on!理解

  1. __new__() 是在新式类中新出现的方法,它作用在构造方法建造实例之前。
  • 可以这样理解:Python 中存在于类中的构造方法 __init__()负责将类实例化,而在 __init__() 执行之前,__new__()负责制造这样的一个实例对象,以便 __init__() 去让该实例对象更加的丰富(为其添加属性等)。
  • 同时:__new__() 方法还决定是否要使用该 __init__() 方法,因为 __new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。
  1. __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
    如果将类比喻为工厂,那么 __init__() 方法则是该工厂的生产工人,__init__() 方法接受的初始化参 数则是生产所需原料,__init__() 方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 __new__() 则是生产部经理,__new__() 方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。

结构

Python 中,若不重写类中的 __new__() 方法,其默认的结构时这样的:

class Foo(object):
    def __new__(cls,*args,**kwagrs):
        return super().__new__(cls,*args,**kwagrs)

1. 其第一个参数 cls 是当前正在实例化的类。

如果要得到当前类的实例,应当在当前类中的 __new__() 方法语句中调用当前类的父类 的 __new__() 方法。例如,如果当前类是直接继承自object,那当前类的__new__()方法返回的对象应该为:

class Foo(object):
    def __new__(cls,*args,**kwagrs):
        return object.__new__(cls,*args,**kwagrs)

注:①、如果(新式)类中没有重写 __new__() 方法,即在定义新式类时没有重新定义 __new__() 时 ,Python默认是调用该类的直接父类的 __new__() 方法来构造该类的实例,如果该类的父类也没有重写 __new__(),那么将一直按此规矩追溯至 object的__new__() 方法,因为 object 是所有新式类的基类。

②、如果新式类中重写了 __new__() 方法,那么你可以自由选择任意一个的其他的新式类(必定要是 新式类,只有新式类必定都有 __new__() ,因为所有新式类都是object的后代,而经典类则没有 __new__() 方法)的 __new__() 方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死 循环。

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-17 14:15:18
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-17 14:15:45
'''


class A(object):
    pass


class B(A):
    pass


class C(B):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

        # 等同于:
        # return B.__new__(cls, *args,**kwargs)
        # return A.__new__(cls, *args,**kwargs)
        # return object.__new__(cls, *args,**kwargs)

2. 在任何新式类的 __new__() 方法,不能调用自身的 __new__() 来制造实例,因为这会造成死循环。

class Bar(object):
    def __new__(cls,*agrs,**kwagrs):
        return Bar.__new__(Foo,*agrs,**kwagrs)

这样的话会造成 当 Bar 调用其自身的 __new__() 方法制造实例时,又会跳到第二行去执行 __new__(),当到第三行时,又跳到第二行去调用 __new__() ,这样不断的就会陷入死循环。

3. Bar.__new__(Foo,*agrs,**kwagrs),指:使用哪个类 (bar)__new__() 方法去制造谁的(Foo)实例对象。


通常来说,新式类开始实例化时,__new__() 方法会返回cls(cls指代当前类)的实例,然后该类的 __init__() 方法作为构造方法会接收这个实例(即 self )作为自己的第一个参数,然后依次传入 __new__ () 方法中接收的位置参数和命名参数。

注:如果 __new__() 没有返回 cls (即当前类)的实例,那么当前类的 __init__() 方法是不会被调用 的。如果 __new__() 返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方法。

'''
@Descripttion: 这是一个文件头注释!
@version: 1.0.0
@Company: DCIT-SH
@Author: Sunny Chen
@Date: 2019-10-17 15:56:23
@LastEditors: Sunny Chen
@LastEditTime: 2019-10-17 15:56:23
'''


class Foo(object):
    def __new__(cls, *args, **kwagrs):
        return object.__new__(cls, *args, **kwagrs)

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


class Bar(object):
    def __new__(cls, *agrs, **kwagrs):
        return object.__new__(Foo, *agrs, **kwagrs)


bar = Bar()
print(type(bar))  #foo其实是Stranger类的实例。

# 输出为:<class '__main__.Foo'>

会发现上例:制造的是 Foo 的实例对象。

总结:因此可以这么描述 __new__()和__ini__() 的区别,在新式类中 __new__() 才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法 __init__() 使其丰满。

如果以建房子做比喻,__new__() 方法负责开发地皮,打下地基,并将原料存放在工地。而 __init__() 方法负责从工地取材料建造出地皮开发招标书中规定的大楼,__init__() 负责大楼的细节设计,建造,装修使其可交付给客户。

快去动手试试吧!


🎉最后

  • 更多参考精彩博文请看这里:陈永佳的博客

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!