为什么在课堂上定义'__new__'和'__init__'

时间:2010-01-07 02:41:09

标签: python class

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

我的代码:

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__将被调用的情况。

感谢

4 个答案:

答案 0 :(得分:19)

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

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

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

一般情况下,最好使用__init__来完成所有工作 - __new__,如果留下__init__无法做到的事情,则为“额外的东西”。

因此,您通常会定义两者是否可以在__init__中执行某些有用的操作,但不会定义在实例化类时您希望发生的所有事情。

例如,考虑一个子类int但也有foo个插槽的类 - 并且您希望它使用int的初始化程序和.foo的初始化程序进行实例化。 {1}}。由于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__,那么这个想法值得记住。

答案 1 :(得分:9)

__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风格表明除非必要,否则不会使用它(一如既往,更好的类设计是您首先使用的工具)。

答案 2 :(得分:2)

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

请注意,SortedDict会在其super()中调用__init__,这通常会转到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__实际上可以返回一个包装器的实例或一个与您认为实例化的完全不同的类。这用于提供类似元类的功能,而不需要实际需要元类。

答案 3 :(得分:0)

在我看来,您所描述的示例中不需要覆盖__new__。 实例和实际内存分配的创建发生在__new__中,__init____new__之后调用,用于初始化为经典OOP术语中的构造函数工作的实例。所以,如果你想要做的只是初始化变量,那么你应该去覆盖__init__。 当您使用Metaclasses时,__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'

同样,这只是出于教学目的,我并不是说应该做这样的事情。