Python 之 __new__() 方法与实例化

__new__() 是在新式类中新出现的方法,它作用在构造方法建造实例之前,可以这么理解,在 Python 中存在于类里面的构造方法 __init__() 负责将类的实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为__new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。

 

如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而__new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。

 

__new__() 方法的特性:

  • __new__() 方法是在类准备将自身实例化时调用。
  • __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
  • 类的实例化和它的构造方法通常都是这个样子:
复制代码
class MyClass(object):
    def __init__(self, *args, **kwargs):
        ...

# 实例化
myclass = MyClass(*args, **kwargs)
复制代码

  

正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 __init__() 方法之前,Python 首先调用 __new__() 方法:

def __new__(cls, *args, **kwargs):
    ...

  

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

  • 如果要得到当前类的实例,应当在当前类中的 __new__() 方法语句中调用当前类的父类的 __new__() 方法。

  例如,如果当前类是直接继承自 object,那当前类的 __new__() 方法返回的对象应该为:

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

  

注意:

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

 

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

 

复制代码
class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls, *args, **kwargs)    

# 以上return等同于
# return object.__new__(Foo, *args, **kwargs)
# return Stranger.__new__(cls, *args, **kwargs)
# return Child.__new__(cls, *args, **kwargs)
class Child(Foo): def __new__(cls, *args, **kwargs): return object.__new__(cls, *args, **kwargs)
# 如果Child中没有定义__new__()方法,那么会自动调用其父类的__new__()方法来制造实例,即 Foo.__new__(cls, *args, **kwargs)。
# 在任何新式类的__new__()方法,不能调用自身的__new__()来制造实例,因为这会造成死循环。因此必须避免类似以下的写法:
# 在Foo中避免:return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)。Child同理。
# 使用object或者没有血缘关系的新式类的__new__()是安全的,但是如果是在有继承关系的两个类之间,应避免互调造成死循环,例如:(Foo)return Child.__new__(cls), (Child)return Foo.__new__(cls)。
class Stranger(object):
    ...
# 在制造Stranger实例时,会自动调用 object.__new__(cls)
复制代码

 

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

 

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

 

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(Stranger, *args, **kwargs)  

class Stranger(object):
    ...

foo = Foo()
print type(foo)    

# 打印的结果显示foo其实是Stranger类的实例。

# 因此可以这么描述__new__()和__ini__()的区别,在新式类中__new__()才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法__init__()使其丰满。
# 如果以建房子做比喻,__new__()方法负责开发地皮,打下地基,并将原料存放在工地。而__init__()方法负责从工地取材料建造出地皮开发招标书中规定的大楼,__init__()负责大楼的细节设计,建造,装修使其可交付给客户。

 

我在看 Python in a Nutshell 中有这么一个疑问。
在类体(class body)外调用__new__()方法,cls 之外的参数是好像不会传递给__init__()方法。
像下面的代码:

1
2
3
4
5
6
7
class Myclass(object):
    def __init__(self, x):
        self.x = x
     
c1 = Myclass(11)
c2 = Myclass.__new__(Myclass, 12)
print c1.x, c2.x


而应该在在调用__new__()方法后显式调用 __init__()方法
就好像:

1
2
3
4
5
6
7
8
9
class Myclass(object):
    def __init__(self, x):
        self.x = x
     
c1 = Myclass(11)
c2 = Myclass.__new__(Myclass, 12)
if isinstance(c2, Myclass):
    type(c2).__init__(c2, 12)
print c1.x, c2.x


请问这是为什么呢?
#2楼[楼主] 2014-09-21 12:34 iFantasticMe  
@ 余星08

你好,你首先要知道在面向对象编程中,实例化基本遵循创建实例对象、初始化实例对象、最后返回实例对象这么一个过程。

Python 中的 __new__ 方法负责创建一个实例对象,__init__ 方法负责将该实例对象进行初始化。

当你进行 c1 = MyClass(11) 这样的操作的时候,其内部流程是首先调用 MyClass.__new__ 方法创建实例对象,紧接着就调用该对象的 __init__ 方法完成初始化,然后将该实例对象引用给 c1 变量。

好了,现在可以解释一下你的问题。

c1 = MyClass(11)

这条语句我已经解释过,它实际上是依次调用了 MyClass 的 __new__,__init__ 两个方法。

c2 = Myclass.__new__(Myclass, 12)

这条语句其实只是调用 __new__ 完成了创建实例对象,但没有调用 __init__ 完成对象的初始化,因此 print c2.x 是会提示没有该属性的错误的。

我下面给出三段代码可以帮助你了解 __new__, __init__ ,实例对象的参数传递,以及对继承的影响。

代码1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> class Foo(object):
    def __new__(cls, x, *args, **kwargs):
        self = object.__new__(cls)
        self.x = x
        return self
    def __init__(self*args, **kwargs):
        self.args = args
        self.kwargs = kwargs
 
         
>>> foo = Foo(10"args", name="bar")
>>> print foo.x, foo.args, foo.kwargs
10 (10'args') {'name''bar'}
 
>>> class Bar(Foo):
    def __init__(self*args, **kwargs):
        self.bar_args = args
        self.bar_kwargs = kwargs
 
         
>>> bar = Bar(10"args", name="bar")
>>> print bar.__dict__
{'x'10'bar_args': (10'args'), 'bar_kwargs': {'name''bar'}}


代码2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> class Foo(object):
    def __new__(cls*args, **kwargs):
        self = object.__new__(cls)
        self.x = args[0]
        self.args = args
        self.kwargs = kwargs
        return self
 
     
>>> foo = Foo(10"args", name="bar")
>>> print foo.x, foo.args, foo.kwargs
10 (10'args') {'name''bar'}
 
>>> class Bar(Foo):
    def __init__(self*args, **kwargs):
        self.bar_args = args
        self.bar_kwargs = kwargs
 
         
>>> bar = Bar(10"args", name="bar")
>>> print bar.__dict__
{'x'10'args': (10'args'), 'bar_args': (10'args'), 'bar_kwargs': {'name''bar'}, 'kwargs': {'name''bar'}}


代码3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
>>> class Foo(object):
    def __init__(self*args, **kwargs):
        self.x = args[0]
        self.args = args
        self.kwargs = kwargs
 
         
>>> foo = Foo(10"args", name="bar")
>>> print foo.x, foo.args, foo.kwargs
10 (10'args') {'name''bar'}
 
>>> class Bar(Foo):
    def __init__(self*args, **kwargs):
        self.bar_args = args
        self.bar_kwargs = kwargs
 
         
>>> bar = Bar(10"args", name="bar")
>>> print bar.__dict__
{'bar_args': (10'args'), 'bar_kwargs': {'name''bar'}}


以上三种写法看起来是等价的,但如果有一个类继承自 Foo 类,它们各自的表现就不一定相同了。因为实际编程中,一个子类往往会覆写父类的 __init__ 方法,但很少去覆写父类的 __new__ 方法。

第 3 种情况则是我们平常构建类实例的方法,除非你想在构建类对象时有特别的需求,否则一般不会去使用 __new__ 方法。
 
通过构造函数创建实例,会调依次用__new__()方法和__init__()方法来实例化和初始化,所以当__init__()方法中绑定了实例属性 x 时,通过构造函数传入的参数就被绑定到了该属性上。所以 c1 有属性 x 。
当在类体外调用__new__()方法,只是创建了一个实例,并没有传递到__init__()进行初始化,所以 'c2.x' 就会出现属性不存在的错误。但只要显式调用 __init__()方法,就能创建 x 属性了。

你写的三段代码我也认真对比了一下,也总结了一下。
三个 Foo 类的定义是等价的。实例属性既可以在__init__()中绑定,也可以在__new__()中绑定。
三个 Bar 类代码完全相同,但各自继承的父类写法不同,所以 Bar 的实例属性也略有不同。(主要是体现在在 __init__()方法里绑定了属性的 Foo 类中。因为 Bar 类定义了自己的 __init__()方法,而__new__()继承自 Foo/object,所以 __new__()中绑定的属性也会得到继承)
 
 
 

转载自:https://www.cnblogs.com/ifantastic/p/3175735.html

posted @ 2018-12-07 15:13  梁少华  阅读(113)  评论(0编辑  收藏  举报