如何在python中实现多个构造函数?
How to implement multiple constructors in python?
在 python 中,不可能多次定义 init 函数,了解语言的工作原理,这是相当公平的。当一个对象被创建时, init 被调用,所以,有两个会产生不确定性。然而,在某些设计中,这样的 属性 是可取的。例如:
class Triangle(object):
def __init__(self,vertices):
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def area(self):
# calculate the are using vertices
...
return result
class Triangle(object):
def __init__(self,sides):
self.v1 = sides[0]
self.v2 = sides[1]
self.v3 = sides[2]
def area(self):
# calculate the are using sides
...
return result
在这种情况下,我们有相同数量的属性要初始化,而且它们是相关的,因此您可以从一个中获得 other.True,在这个特定示例中,可以使用顶点这一事实是元组,而边可能是浮点数(或字符串或其他东西),但当然情况并非总是如此。
一个可能的解决方案是将初始化过程委托给其他函数,例如:
class Triangle(object):
def __init__(self):
self.v1 = None
self.v2 = None
self.v3 = None
def byVertices(self,vertices):
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def sidesToVertices(self,sides):
# converts sides to vertices
...
return vertices
def bySides(self,sides):
vertices = sidesToVertices(sides)
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def area(self):
# calculate the are using vertices
...
return result
但它看起来不是很干净,像 "area" 这样的所有功能都必须检查属性是否正确实例化(或采用 try/catch),这是很多代码,加上它破坏了项目的可读性。总的来说,这看起来像是一个廉价的把戏。
另一种选择是告诉实例您要初始化什么类型的属性:
class Triangle(object):
def __init__(self, data, type = "vertices"):
if type == "sides":
data = sideToVertices(self,sides)
else if type == "vertices":
pass
else:
raise(Exception)
self.v1 = data[0]
self.v2 = data[1]
self.v3 = data[3]
def sidesToVertices(self,sides):
# converts sides to vertices
...
return vertices
def area(self):
# calculate the are using vertices
这种其他方法似乎更可取,但是我不确定 "pythonic" 需要在 init 中引入多少逻辑。你对这件事有什么看法?有没有更好的方法来协调这种情况?
备用构造函数是 class 方法最常见的用例。您的 "real" __init__
通常是各种 class 方法的最低公分母。
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
The,要获取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__
的隐式调用,这是一个 class 方法,就像 by_vertices
和 by_sides
一样。 (事实上,您可以简单地定义 by_vertices = __new__
。)在所有三种情况下,Python 隐式调用 Triangle.__init__
任何 cls
returns.
(请注意,by_vertices
生成一个特定的三角形,而 by_sides
可以生成任意数量的 "equivalent" 个三角形,它们仅在位置和相对于原点的旋转方面有所不同。相反,by_sides
可以被认为生成 "real" 三角形,其中 by_vertices
指定特定位置的三角形。None 这与手头的问题特别相关。)
切线背景。
t = Triangle(v1, v2, v3)
是"normal"方法,但是这是什么意思呢? Triangle
是一个 class,不是函数。要回答这个问题,您需要了解 metaclasses 和 __call__
方法。
__call__
用于使实例可调用。 my_obj(args)
成为 my_obj.__call__(args)
的语法糖,它本身与所有实例方法一样,是 type(my_obj).__call__(my_obj, args)
.
的语法糖(一级近似值)
您听说 Python 中的一切都是对象。 classes 也是如此;每个 class 对象都是其元 class 的一个实例,
类型的类型。默认为 type
,因此 Triangle(v1, v2, v3)
将脱糖为 Triangle.__call__(v1, v2, v3)
或 type.__call__(Triangle, v1, v2, v3)
.
除此之外,type.__call__
做什么?不多。它只是为其第一个参数调用适当的 class 方法 __new__
。即type.__call__(Triangle, v1, v2, v3) == Triangle.__new__(v1, v2, v3)
。 Python 然后在 Triangle.__new__
的 return 值上隐式调用 __init__
,如果它实际上是 Triangle
的一个实例。因此,您可以认为 type.__call__
的定义类似于
def __call__(cls, *args, **kwargs):
obj = cls.__new__(*args, **kwargs)
if isinstance(obj, cls):
cls.__init__(obj, *args, **kwargs)
return obj
可以使用可选参数,使用更方便的参数调用构造函数
class Triangle(object):
def __init__(self, vertices=None, sides=None):
if vertices is not None:
# Initialize with vertices
elif sides is not None:
# Initialize with sides
else:
# Error: neither parameter was passed!
my_sides = ...
my_vertices = ...
triangle_sides = Triangle(sides=my_sides)
triangle_vertices = Triangle(vertices=my_vertices)
class Employee:
def __init__(self, fname, lname, salary):
self.fname = fname
self.lname = lname
self.salary = salary
def __init__(self, *employee):
self.fname = employee[0]
self.lname = employee[1]
abdullah = Employee("Abdullah", "Aqib", 10000)
mustafa = Employee("Mustafa", "Ahmad")
print(abdullah.__dict__)
print(mustafa.__dict__)
在 python 中,不可能多次定义 init 函数,了解语言的工作原理,这是相当公平的。当一个对象被创建时, init 被调用,所以,有两个会产生不确定性。然而,在某些设计中,这样的 属性 是可取的。例如:
class Triangle(object):
def __init__(self,vertices):
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def area(self):
# calculate the are using vertices
...
return result
class Triangle(object):
def __init__(self,sides):
self.v1 = sides[0]
self.v2 = sides[1]
self.v3 = sides[2]
def area(self):
# calculate the are using sides
...
return result
在这种情况下,我们有相同数量的属性要初始化,而且它们是相关的,因此您可以从一个中获得 other.True,在这个特定示例中,可以使用顶点这一事实是元组,而边可能是浮点数(或字符串或其他东西),但当然情况并非总是如此。
一个可能的解决方案是将初始化过程委托给其他函数,例如:
class Triangle(object):
def __init__(self):
self.v1 = None
self.v2 = None
self.v3 = None
def byVertices(self,vertices):
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def sidesToVertices(self,sides):
# converts sides to vertices
...
return vertices
def bySides(self,sides):
vertices = sidesToVertices(sides)
self.v1 = vertices[0]
self.v2 = vertices[1]
self.v3 = vertices[2]
def area(self):
# calculate the are using vertices
...
return result
但它看起来不是很干净,像 "area" 这样的所有功能都必须检查属性是否正确实例化(或采用 try/catch),这是很多代码,加上它破坏了项目的可读性。总的来说,这看起来像是一个廉价的把戏。
另一种选择是告诉实例您要初始化什么类型的属性:
class Triangle(object):
def __init__(self, data, type = "vertices"):
if type == "sides":
data = sideToVertices(self,sides)
else if type == "vertices":
pass
else:
raise(Exception)
self.v1 = data[0]
self.v2 = data[1]
self.v3 = data[3]
def sidesToVertices(self,sides):
# converts sides to vertices
...
return vertices
def area(self):
# calculate the are using vertices
这种其他方法似乎更可取,但是我不确定 "pythonic" 需要在 init 中引入多少逻辑。你对这件事有什么看法?有没有更好的方法来协调这种情况?
备用构造函数是 class 方法最常见的用例。您的 "real" __init__
通常是各种 class 方法的最低公分母。
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
The,要获取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__
的隐式调用,这是一个 class 方法,就像 by_vertices
和 by_sides
一样。 (事实上,您可以简单地定义 by_vertices = __new__
。)在所有三种情况下,Python 隐式调用 Triangle.__init__
任何 cls
returns.
(请注意,by_vertices
生成一个特定的三角形,而 by_sides
可以生成任意数量的 "equivalent" 个三角形,它们仅在位置和相对于原点的旋转方面有所不同。相反,by_sides
可以被认为生成 "real" 三角形,其中 by_vertices
指定特定位置的三角形。None 这与手头的问题特别相关。)
切线背景。
t = Triangle(v1, v2, v3)
是"normal"方法,但是这是什么意思呢? Triangle
是一个 class,不是函数。要回答这个问题,您需要了解 metaclasses 和 __call__
方法。
__call__
用于使实例可调用。 my_obj(args)
成为 my_obj.__call__(args)
的语法糖,它本身与所有实例方法一样,是 type(my_obj).__call__(my_obj, args)
.
您听说 Python 中的一切都是对象。 classes 也是如此;每个 class 对象都是其元 class 的一个实例,
类型的类型。默认为 type
,因此 Triangle(v1, v2, v3)
将脱糖为 Triangle.__call__(v1, v2, v3)
或 type.__call__(Triangle, v1, v2, v3)
.
除此之外,type.__call__
做什么?不多。它只是为其第一个参数调用适当的 class 方法 __new__
。即type.__call__(Triangle, v1, v2, v3) == Triangle.__new__(v1, v2, v3)
。 Python 然后在 Triangle.__new__
的 return 值上隐式调用 __init__
,如果它实际上是 Triangle
的一个实例。因此,您可以认为 type.__call__
的定义类似于
def __call__(cls, *args, **kwargs):
obj = cls.__new__(*args, **kwargs)
if isinstance(obj, cls):
cls.__init__(obj, *args, **kwargs)
return obj
可以使用可选参数,使用更方便的参数调用构造函数
class Triangle(object):
def __init__(self, vertices=None, sides=None):
if vertices is not None:
# Initialize with vertices
elif sides is not None:
# Initialize with sides
else:
# Error: neither parameter was passed!
my_sides = ...
my_vertices = ...
triangle_sides = Triangle(sides=my_sides)
triangle_vertices = Triangle(vertices=my_vertices)
class Employee:
def __init__(self, fname, lname, salary):
self.fname = fname
self.lname = lname
self.salary = salary
def __init__(self, *employee):
self.fname = employee[0]
self.lname = employee[1]
abdullah = Employee("Abdullah", "Aqib", 10000)
mustafa = Employee("Mustafa", "Ahmad")
print(abdullah.__dict__)
print(mustafa.__dict__)