为什么在一个类中定义'__new__'和'__init__'

问题描述:

我认为你可以在一个类中定义'__init__'或'__new__',但为什么都在django.utils.datastructures.py中定义。为什么在一个类中定义'__new__'和'__init__'

我的代码:

class a(object): 
    def __init__(self): 
     print 'aaa' 
    def __new__(self): 
     print 'sss' 

a()#print 'sss' 

class b: 
    def __init__(self): 
     print 'aaa' 
    def __new__(self): 
     print 'sss' 
b()#print 'aaa' 

datastructures.py:

class SortedDict(dict): 
    """ 
    A dictionary that keeps its keys in the order in which they're inserted. 
    """ 
    def __new__(cls, *args, **kwargs): 
     instance = super(SortedDict, cls).__new__(cls, *args, **kwargs) 
     instance.keyOrder = [] 
     return instance 

    def __init__(self, data=None): 
     if data is None: 
      data = {} 
     super(SortedDict, self).__init__(data) 
     if isinstance(data, dict): 
      self.keyOrder = data.keys() 
     else: 
      self.keyOrder = [] 
      for key, value in data: 
       if key not in self.keyOrder: 
        self.keyOrder.append(key) 

SortedDict.__init__会是怎样的情况下调用。

感谢

+3

** do **阅读文档,他们是详尽的! – SilentGhost 2010-01-07 12:58:34

您可以定义__new____init__中的一个或两个。

__new__必须返回一个对象 - 可能是一个新对象(通常该任务被委托给type.__new__),现有对象(实现单例,从池中“回收”实例等等),甚至是一个是而不是该类的一个实例。如果__new__返回该类的一个实例(新的或现有的),则__init__会被调用;如果__new__返回的对象是而不是该类的一个实例,则__init__而不是被调用。

__init__传递一个类实例作为它的第一个项目(在__new__返回它的状态,即通常为“空”),并且必须根据需要更改它以使其可以使用(通常通过添加属性)。

一般来说,最好使用__init__作为它的全部功能 - 如果__init__无法完成,则为__new__,因为这个“额外的东西”。

所以,如果在__init__中有一些有用的东西,但是不是你想在类实例化时发生的所有事情,你通常会定义它们。

例如,考虑到子类int而且还具有foo槽类 - 你希望它与为int,一个用于.foo一个初始化实例化。由于int是不变的,这部分在__new__发生,那么迂腐一个能代码:

>>> class x(int): 
... def __new__(cls, i, foo): 
...  self = int.__new__(cls, i) 
...  return self 
... def __init__(self, i, foo): 
...  self.foo = foo 
... __slots__ = 'foo', 
... 
>>> a = x(23, 'bah') 
>>> print a 
23 
>>> print a.foo 
bah 
>>> 

在实践中,为的情况下,如果你失去了__init__和刚移动的self.foo = foo__new__这个简单,没有人会介意。但是,如果初始化足够复杂,最好放在__init__中,这个想法值得记住。

+0

昨天我问了一个关于动态继承多个类的问题。基于你目前的例子,我做到了这一点:http://gist.github.com/271098 什么错了? – 2010-01-07 09:10:31

+0

Alex,请解释为什么'__new__'在SortedDict实现的特定情况下被重写?初始化'__init__'中的'keyOrder'属性有什么不好?我能想到的唯一真正原因是,它确保即使SortedDict被子类化并且子类不调用基类“__init__”,也确保该属性被创建。但是,再次,你可以用'__new__'做同样的事,对吧? – shylent 2010-01-07 12:43:35

+0

@becomingGuru,该代码中存在许多错误,但第一个是您使用的是旧式类:不要 - 总是使用新式(从'object'继承)。在这种情况下,这将揭示第二个错误,一旦修复,您将拥有第三个错误,等等。为什么不打开一个SO问题并要求调试代码,您的代码中存在太多错误来解释它们全部评论! – 2010-01-07 15:34:18

__new____init__做完全不同的事情。方法__init__启动一个类的新实例 - 它是一个构造函数。 __new__是一个非常微妙的事情 - 它可以改变参数,实际上,它是启动对象的类。例如,下面的代码:

class Meters(object): 
    def __new__(cls, value): 
     return int(value/3.28083) 

如果你打电话给你Meters(6)不会实际创建的Meters一个实例,但int一个实例。你可能想知道为什么这很有用。元类实际上至关重要,这是一个公认的模糊(但功能强大)的特性。

您会注意到,在Python 2.x中,只有继承自object的类可以利用__new__,如同上面的代码所示。

您在django中显示的__new__的使用似乎试图在SortedDict对象上保留一个合理的方法解析顺序。但我承认,通常很难说清为什么__new__是必要的。标准的Python风格表明,除非必要,否则不会使用它(与往常一样,更好的类设计是您首先转向的工具)。

+6

'__new__'是构造函数,'__init__'是...只是初始化 - ialization – 2010-01-07 04:27:02

+2

@Auurag:迂回地说,你是对的。不过,如果你是来自Python以外的语言,最好被告知'__init__'是构造函数 - 人们在没有我的帮助的情况下擅长滥用功能。所以,我将'__init__'作为构造函数保存在我的响应中,以便为其他人带来好处。 – pavpanchekha 2010-01-08 03:16:00

我唯一的猜测就是,在这种情况下,他们(这个类的作者)希望在调用SortedDict.__init__之前,在该类上存在keyOrder列表。

注意SortedDict在其__init__调用super(),这通常会去dict.__init__,这可能会叫__setitem__等以开始添加物品。 SortedDict.__setitem__预计.keyOrder属性存在,并且其中存在问题(因为.keyOrder通常不会在调用super()之后才创建。)可能这仅仅是子类化dict的一个问题,因为我的正常肠道本能仅仅是初始化.keyOrder在致电super()之前。

__new__的代码也可以被用来允许SortedDict在菱形继承结构作为子类,它在哪里可以SortedDict.__init__第一__setitem__之前不叫和等叫。 Django必须在2.3以上的版本中支持各种Python版本,有可能这个代码在某些版本中是完全没有必要的,并且在其他版本中是需要的。


有用于限定两个__new____init__一个共同的使用:访问可以由它们的实例版本黯然失色类的属性,而不必做type(self)self.__class__(在元类的存在,可能甚至不正确的事情)。

例如:

class MyClass(object): 
    creation_counter = 0 

    def __new__(cls, *args, **kwargs): 
     cls.creation_counter += 1 
     return super(MyClass, cls).__new__(cls) 

    def __init__(self): 
     print "I am the %dth myclass to be created!" % self.creation_counter 

最后,__new__实际上可以从你以为你是实例返回的包装或一个完全不同的类的实例。这用于提供类元类特性,而实际上不需要元类。

在我看来,在你描述的例子中没有必要重写__new__。 实例和实际内存分配的创建发生在__new__,__init____new__之后调用,并且意味着用传统的OOP术语来初始化服务于构造函数的实例。所以,如果你想要做的就是初始化变量,那么你应该重写__init__。 当您使用元类时,__new__的真正角色就会出现。如果你想做类似改变属性或添加属性的东西,那必须在创建类之前进行,你应该重写__new__

想一想,一个完全假设的情况下,你想使私人类的某些属性,即使他们没有定义(我不是说应该做的)。

class PrivateMetaClass(type): 
     def __new__(metaclass, classname, bases, attrs): 
      private_attributes = ['name', 'age'] 

      for private_attribute in private_attributes: 
       if attrs.get(private_attribute): 
       attrs['_' + private_attribute] = attrs[private_attribute] 
       attrs.pop(private_attribute) 

      return super(PrivateMetaClass, metaclass).__new__(metaclass, classname, bases, attrs) 


class Person(object): 

     __metaclass__ = PrivateMetaClass 

     name = 'Someone' 
     age = 19 

person = Person() 
>>> hasattr(person, 'name') 
False 
>>> person._name 
'Someone' 

再一次,它只是为了教学目的,我不是说我应该做任何这样的事情。