class A(object): def new(cls, *args,kwargs): print (cls) print (args ) print (kwargs ) print ("--------------") instance = object.__new__(cls, *args,kwargs) print (instance) def init(self,a,b): print ("init gets called" ) print ("self is", self ) self.a,self.b = a,b
class A(object):
def __new__(cls):
print (cls)
print (args )
print (kwargs )
print ("--------------")
instance = object.__new__(cls, *args,**kwargs)
print (instance)
def __init__(self,a,b):
print ("init gets called" )
print ("self is", self )
self.a,self.b = a,b
以下三段代码均会报错
# al=A(1,2)
# print (al.a)
# print (al.b)
原本期望的是能够正确输出 a
和 b
的值,可是运行却抛出了异常。
除了异常外还有来自对__new__()
方法调用所产生的输出,可是明明没有直接调用__new__()
方法,原因在哪里?
实际上并不是真正意义上的构造方法,方法所做的工作是 在类的对象创建好之后进行变量的初始化。
__new__()
方法才会真正创建实例,是类的构造 方法。
这两个方法都是 object
类中默认的方法,继承自 object
的新式类,
如果不覆盖这两个方法将会默认调用 object
中对应的方法。
上面的程序抛出异常是因__new__()
方法中并 没有显示返回对象,
因此实际上 a1
为 None
,
当去访问实例属性 a
时抛出 AttributeError: 'NoneType' object has no attribute 'a'
的错误也就不难理解了。
__new__()
方法和 __init__()
方法
看着 __new__()
方法和 __init__()
方法的定义:
object.__new__( cls [,args...])
:其中 els 代表类t args
为参数列表。object.__init__( self [,args...])
:其中self代表实例对象,args
为参数列表。
这两个方法之间有些不同点,总结如下:
根据 Python 文梢( http://dacs.pythmi.org/2/refereiice/datamodel.htm/#object.__new__ ) 可知,
__new__()
方法是静态方法,而__init__()
为实例方法。__new__()
方法一般需要返回类的对象,当返回类的对象时将会自动调用__init__()
方法进行初始化, 如果没有对象返回,则__init__()
方法不会被调用。方法不需要显示返回, 默认为None
,否则会在运行时拋出TypeError
。当需要控制实例创建的时候可使用
__new__()
方法,而控制实例初始化的时候使用__init__()
方法。一般情况下不需要覆盖
__new__()
方法,但当子类继承自不可变类型, 如str
、int
、unicode
或者tuple
的时候,往往需要覆盖该方法。当需要覆盖
__new__()
和__init__()
方法的时候这两个方法的参数必须保持一致,如果不一致将导致异常。示例如下:
class Test(object):
def __new__(cls, x):
return super(Test,cls) .__new__(cls)
def __init__(self,x,y):
self.x=x
self.y = y
Test(1)
# Test(2,3)
class UserSet(frozenset):
def __init__(self, arg=None):
if isinstance(arg, str):
arg = arg.split()
frozenset.__init__(arg)
print (UserSet("I am testing "))
UserSet({'e', 'a', ' ', 's', 'i', 'g', 'n', 'm', 't', 'I'})
print (frozenset("I am testing "))
frozenset({'e', 'a', ' ', 's', 'i', 'g', 'n', 'm', 't', 'I'})
上面的输出显然没有满足用户的需求,用户希望得到的输出是:
UserSet(['I', 'frozen', 'set', 'am', 'testing'])
实际上这些不可变类型的 __init__()
方法是个伪方法,
必须重新覆盖 __new__()
方法才能满足需求。方法实现的代码如下:
def __new__(cls,*args):
if args and isinstance (args[0],basestring):
args = (args[0].split(),) + args[l:]
return super (UserSet.cls).__new__(cls, *args)
2)用来实现工厂模式或者进行元类编程
(元类编程中常常需要使用__new__()
来控制对象创建。)的时候。
以简单工厂为例子, 它由一个工厂类根据传入的参数决定创建出哪一种产品类的实例,
属于类的创建型模式。其类的关系如图6-2所示。
工厂模式的实现代码如下:
class Shape(object):
def __init__(object):
pass
def draw(self):
pass
class Triangle(Shape):
def __init__(self):
print ("I am a triangle")
def draw(self):
print ("I am drawing triangle")
class Rectangle(Shape):
def __init__(self):
print ("I am a rectnagle")
def draw(self):
print ("I am drawing triangle1")
class Trapezoid(Shape):
def __init__(self):
print (" I am a trapezoid")
def draw(self):
print ("I am drawing triangle")
class Diamond(Shape):
def __init__(self):
print("I am a diamond")
def draw(self):
print ("I am drawing triangle")
class ShapeFactory(object):
shapes = {'triangle': Triangle, 'rectangle': Rectangle,'trapezoid': Trapezoid, 'diamond': Diamond}
def __new__(klass,name):
if name in ShapeFactory.shapes.keys():
print ("creating a new shape %s" % name)
return ShapeFactory.shapes[name]()
else:
print ("creating a new shape %s" % name)
return Shape()
在 ShapeFactory
类中重新覆盖了__new__()
方法,外界通过调用该方法来创建其所需的对象类型,
但如果所请求的类是系统所不支持的,则返回 Shape
对象。
在引入了工厂类之后,只需要使用如下形式就可以创建不同的图形对象:
ShapeFactory('rectangle').draw()
creating a new shape rectangle I am a rectnagle I am drawing triangle1
3)作为用来初始化的__init__()
方法在多继承的情况下,
子类的__init__()
方法如果不显示调用父类的__init__()
方法,
则父类的__init__()
方法不会被调用。
class A(object):
def __init__(self):
print ("I am A's __init__")
class B(A):
def __init__(self):
print ("I am B's __init__")
b = B()
I am B's __init__
程序输出为:I am B's __init__
。父类传入的 __init__()
方法并没有被调用,
所以要初始化父类中的变量需要在子类的 __init__()
方法中使用super ( B,self).__init()
。
对于多继承的情况,可以通过迭代子类的__bases__
属性中的内容来逐一调用父类的初始化方法。
注意:__new__()
方法才是类的构造方法,而 __init__()
不是。