替代构造函数是类方法的最常见用例.那么,您的“实际” __init__通常是各种类方法的最低公分母.
class Triangle(object):
def __init__(self, v1, v2, v3):
self.v1 = v1
self.v2 = v2
self.v3 = v3
# This is just here to demonstrate, since it is just
# wrapping the built-in __new__ for no good reason.
@classmethod
def by_vertices(cls, vertices):
# Make sure there are exactly three vertices, though :)
return cls(*vertices)
@staticmethod
def sidesToVertices(sides):
# converts sides to vertices
return v1, v2, v3
@classmethod
def by_sides(cls, sides):
return cls(*sides_to_vertices(sides))
def area(self):
# calculate the are using vertices
...
return result
要获取Triangle的实例,您可以编写以下任意内容:
t = Triangle(p1, p2, p3)
t = Triangle.by_vertices(p1, p2, p3) # Same as the "direct" method
t = Triangle.by_sides(s1, s2, s3)
唯一的区别是Triangle(p1,p2,p3)隐藏对Triangle .__ new__的隐式调用,这是一个类方法,就像by_vertices和by_sides一样. (实际上,您可以简单地定义by_vertices = __new__.)在所有三种情况下,Python都会在cls返回时隐式调用Triangle .__ init__.
(请注意,by_vertices会生成一个特定的三角形,而by_sides可以生成仅在相对于原点的位置和旋转方面不同的任意数量的“等效”三角形.相反,by_sides可以被视为生成“真实”三角形,by_vertices指定一个三角形的特定位置.所有这些都与眼前的问题无关.)
相切的背景.
t = Triangle(v1,v2,v3)是“常规”方法,但这意味着什么?三角形是一个类,而不是一个函数.要回答这个问题,您需要了解元类和__call__方法.
__call__用于使实例可调用. my_obj(args)成为my_obj .__ call __(args)的语法糖,它本身像所有实例方法一样,是类型(my_obj).__ call __(my_obj,args)的语法糖(近似近似值).
您已经听说Python中的所有内容都是一个对象.对于类也是如此.每个类对象都是其元类的实例,
类型的类型.默认值为type,因此Triangle(v1,v2,v3)会将糖去糖为Triangle .__ call __(v1,v2,v3)或type .__ call __(Triangle,v1,v2,v3).
这样一来,type .__ call__会做什么?不多.它只是为其第一个参数调用适当的类方法__new__.也就是说,键入.__ call __(Triangle,v1,v2,v3)== Triangle .__ new __(v1,v2,v3).然后,Python实际上对Triangle的返回值隐式调用__init__.__new__实际上是Triangle的实例.因此,您可以将type .__ call__定义为
def __call__(cls, *args, **kwargs):
obj = cls.__new__(*args, **kwargs)
if isinstance(obj, cls):
cls.__init__(obj, *args, **kwargs)
return obj