使用* args和** kwargs而不是显式列出预期参数

时间:2018-08-14 10:03:06

标签: python args kwargs

当我在一个有很多嵌套函数调用和对象层次结构的项目中工作时,我想知道将所有必需的参数传递到最高层然后逐步传递到较低层的最佳实践是什么。

示例:3个类及其所需的(构造函数)参数:

A级:a,b

B级:c,d,e

C级:f

类A持有B的一个实例,而B持有C的一个实例。它们在各自父级的构造函数中初始化。因此,这意味着使用我的代码的人将只初始化A的一个新对象,并且必须将所有参数a,b,c,d,e,f传递给A,然后在该参数上对其进行处理或进一步分发。

示例代码:

class A(object):
    def __init__(self, a, b, c, d, e, f):
        b = B(c, d, e, f)

class B(object):
    def __init__(self, c, d, e, f):
        c = C(f)

class C(object):
    def __init__(self, f):
        pass

我的第一个尝试是直接在构造函数中列出类及其所有子级需要的所有参数,但这很容易出错,并且具有很高的代码冗余性,尤其是在每个提供默认值的情况下。层。所以我在考虑使用** kwargs和* args代替。

这样的好处是我可以将那些参数传递给孩子,并且每一层都可以从那里获取所需的参数。

但是,缺点是,想要使用该层次结构的类的人无法使用IDE的自动完成功能来查看期望的参数。

什么被认为是Python 3的最佳实践?

2 个答案:

答案 0 :(得分:0)

编码不应该满足任何IDE要求,您在谈论not use the IDE's auto-completion to see which parameters are expected...不应编写使您的IDE满意的代码,而应易于其他编码者使用。这是什么意思?您公开的公共界面应该直观易懂,而不是IDE易于学习。我想说的是,良好的代码可以满足的许多先决条件之一是,您是否甚至不需要读取任何文档,但公共接口是不言自明的。

这里有一个有趣的编程语录供您考虑:

  

函数的理想参数个数为零(尼拉度)。下一个   来一个(单声道),紧接着是两个(双声道)。三个论点   (triadic)应尽可能避免。超过三个(复数)   需要非常特殊的理由-然后不应该使用   无论如何。

通过查看不相关的示例,我可以直截了当地说这不是一个好的OOP设计,不仅在公开的公共接口方面,而且在关系方面(层次结构都应使用适当的推理来创建) 。也许如果您提出了一个真实的案例,我可以进一步建议进行适当的重构,但是使用无用的虚构代码,没有用例和虚构的无用参数,我看不到重点。

无论如何,我能为您提供的最佳建议是尝试提出具有适当逻辑关系和精简的自解释公共接口的良好封装对象。否则,将您的代码“吸引人”和“令人愉悦”以供他人使用,就可以了:)

答案 1 :(得分:0)

不要将依赖注入视为需要特殊支持的东西。这只是一种将逻辑从函数内部移动到该函数外部的技术。也就是说,代替

class A(object):
    def __init__(self, a, b, c, d, e, f):
        b = B(c, d, e, f)

class B(object):
    def __init__(self, c, d, e, f):
        c = C(f)

class C(object):
    def __init__(self, f):
        pass

a = A(1, 2, 3, 4, 5, 6)

您写

class A(object):
    def __init__(self, a, b, bobj):
        b = bobj

class B(object):
    def __init__(self, c, d, e, cobj):
        c = cobj

class C(object):
    def __init__(self, f):
        pass

a = A(1, 2, B(3, 4, 5, C(6)))

这提供了更好的分离效果,例如A不再需要知道如何创建B的实例(或者,甚至可以得到 B的实例)。