关于python:self.__dict__.update(**kwargs)这样的语法是合适的吗

Is self.__dict__.update(**kwargs) good or poor style?

在python中,假设我有一些类,circle,它继承自shape。形状需要x和y坐标,此外,圆需要半径。我想通过这样做来初始化圆,

1
c = Circle(x=1., y=5., r=3.)

圆继承自形状,所以我需要对__init__使用命名参数,因为不同的类需要不同的构造函数。我可以手动设置x、y和r。

1
2
3
4
5
6
7
8
9
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)自动设置圆的属性。

1
2
3
4
5
6
7
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']那样维护样板文件。缺点是,圆圈需要哪些参数并不明显。这被认为是一个骗局还是一个好的风格(只要循环的接口有良好的文档记录)?

谢谢大家的周到回答。self.__dict__.update(**kwargs)黑客在组织代码方面对我很有用,但我会确保用正确的显式传递参数和在生产代码中执行明确的错误检查来代替它。


1
2
3
4
5
6
7
8
9
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

Is this considered a cheat or is this good style (as long as the
interface to Circle is well-documented)?

当您在编写简单、易懂的代码和头疼的代码+好的docstrings之间做出选择时,实际上您没有任何选择,您只需编写简单、自我记录的代码:)


我想说,第一种方法肯定更可取,因为显式比隐式好。

考虑一下,如果在初始化一个圆时输入了错别字,会发生什么情况,比如Circle(x=1., y=5., rr=3.)。您希望立即看到这个错误,这在__dict__.update(kwargs)中不会发生。


如果您希望自动分配,我建议采用以下方法:

1
2
3
def __init__(self, **kwargs):
    for key, value in kwargs.iteritems():
        setattr(self, key, value)

就风格而言,它介于明确地编写和使用self.__dict__自己进行黑客攻击之间。


如果你想让它更明显,你可以让Circle.__init__对论点进行一些健全的检查。大概你会检查确保所有的论点都在那里,并且可能会对毫无意义的论点产生错误。

您甚至可以在Shape中创建一个装饰或助手函数来为您完成这项工作。像这样:

1
2
3
4
class Circle(Shape):
    def __init__(self, **kwargs):
        self.check(kwargs, 'x', 'y', 'r')
        super(Circle, self).__init__(**kwargs)

.check将在Shape中实现,基本上只是验证所有参数都在kwargs中,并且可能没有其他参数(对不起,没有该参数的代码-您可以自己解决)。您甚至可以让子类重载它以检查可选参数,您可能希望以不同于其他参数的方式处理这些参数(即,为它们提供一个默认值,否则不会在Shape.__init__中指定该值)。

否则,如果您记录了您的接口,并且它按照文档的方式工作,那么它总是可以的。你做的任何其他事情都能像我们"期望"的那样工作(为错误的论点抛出异常)是一个额外的收获。