在Python中,假设我有一些继承自Shape的类Circle。形状需要x坐标和y坐标,此外,圆需要半径。我希望能够通过做类似的事来初始化Circle
c = Circle(x=1., y=5., r=3.)
Circle继承自shape,因此我需要使用命名参数__init__
,因为不同的类需要不同的构造函数。我可以手动设置x,y和r。
class Shape(object):
def __init__(self, **kwargs):
self.x = kwargs['x']
self.y = kwargs['y']
class Circle(Shape):
def __init__(self, **kwargs):
super(Circle, self).__init__(**kwargs)
self.r = kwargs['r']
或者,我可以使用self.__dict__.update(kwargs)
class Shape(object):
def __init__(self, **kwargs):
self.__dict__.update(**kwargs)
class Circle(Shape):
def __init__(self, **kwargs):
super(Circle, self).__init__(**kwargs)
这样做的好处是代码更少,我不需要像self.foo = kwargs['foo']
那样维护样板。缺点是Circle不需要哪些参数。这被认为是作弊还是这种好风格(只要Circle的界面有详细记录)?
谢谢大家,感谢您的深思熟虑。 self.__dict__.update(**kwargs)
hack在尝试组织我的代码时对我很有用,但我会确保通过明确地正确传递参数并在生产代码中进行明确的错误检查来替换它。
答案 0 :(得分:25)
class Shape(object):
def __init__(self, x=None, y=None):
self.x = x
self.y = y
class Circle(Shape):
def __init__(self, r=None, **kwargs):
super(Circle, self).__init__(**kwargs)
self.r = r
就是这样。当你真的不需要时,不要使用**kwargs
。
这被认为是作弊还是这种好风格(只要是 与Circle的接口有详细记录)?
当您在编写简单易懂的代码和令人头疼的代码+漂亮的文档字符串之间做出选择时,您实际上没有任何选择,您只需编写简单,自我记录的代码:)
答案 1 :(得分:17)
我想说第一种方法绝对是可取的,因为explicit is better than implicit。
考虑一下如果你在初始化一个圆圈时发生错误会发生什么,例如Circle(x=1., y=5., rr=3.)
。您希望立即看到此错误,__dict__.update(kwargs)
不会发生此错误。
答案 2 :(得分:10)
如果您想自动分配,我建议采用以下方法:
def __init__(self, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
,就风格而言,它介于两者之间,明确地使用self.__dict__
自行编写它。
答案 3 :(得分:3)
如果你希望它更明显,你可以让Circle.__init__
对参数进行一些健全性检查。大概你会检查以确保所有参数都在那里,并且可能会为无意义的参数引发错误。
您甚至可以在Shape
中制作装饰器或辅助函数来为您执行此操作。像这样:
class Circle(Shape):
def __init__(self, **kwargs):
self.check(kwargs, 'x', 'y', 'r')
super(Circle, self).__init__(**kwargs)
.check
将在Shape
中实现,并且基本上只是验证所有参数都在kwargs
中,并且可能没有额外的参数(抱歉,没有任何代码 - 你可以自己解决)。您甚至可以让子类重载它以检查可选参数,您可能希望以不同于其他参数的方式处理它们(即为它们提供一个默认值,否则将在Shape.__init__
中分配。
否则,如果您记录您的界面,并且它的工作方式与文档一致,那么它总是没问题。你做的任何事情都是为了使它按照我们“期望”的方式工作(抛出不正确参数的例外)是一种奖励。