简体   繁体   English

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

[英]Using *args and **kwargs instead of explicitly listing expected parameters

When working on a project where I have a lot of nested function calls and object hierarchies I was wondering what is the best practice to pass all required parameters to the highest layer and from there step-wise to the lower layers. 当我在一个有很多嵌套函数调用和对象层次结构的项目中工作时,我想知道什么是将所有必需的参数传递到最高层然后逐步传递到较低层的最佳实践。

Example: 3 classes with their needed (constructor) parameters: 示例:3个类及其所需的(构造函数)参数:

Class A: a, b A级:a,b

Class B: c, d, e B类:c,d,e

Class C: f C级:f

Class A holds an instance of B and B holds an instance of C. They are initialized in the constructor of the respective parent. 类A持有B的一个实例,而B持有C的一个实例。它们在各自父级的构造函数中初始化。 So this means that the one who is using my code will only initialize a new object of A and have to pass all parameters a,b,c,d,e,f to A where they are processed or further distributed. 因此,这意味着使用我的代码的人将只初始化A的新对象,并且必须将所有参数a,b,c,d,e,f传递给A,然后在该参数处对其进行处理或进一步分发。

Example code: 示例代码:

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

My first attempt was to list all parameters that a class and all their children needs directly in the constructor, but this is very prone to errors and also has a very high code redundancy, especially when it comes to providing default values at every layer. 我的第一个尝试是直接在构造函数中列出类及其所有子级需要的所有参数,但这很容易出错,并且具有很高的代码冗余性,尤其是在每一层都提供默认值时。 So I am thinking of using **kwargs and *args instead. 所以我在考虑使用** kwargs和* args代替。

This would have the advantage that I could just pass those to the children and every layer could just take the arguments it needs from there. 这样做的好处是,我可以将那些参数传递给孩子,并且每一层都可以从那里获取所需的参数。

However, the disadvantage would be that someone who wants to use a class of this hierarchy could not use the IDE's auto-completion to see which parameters are expected. 但是,缺点是希望使用此层次结构的类的人无法使用IDE的自动完成功能来查看期望的参数。

What is considered the best practice in Python 3 for this? 在Python 3中,什么被认为是最佳实践?

Coding shouldn't be a mean to satisfy any IDE requirements, you're talking about not use the IDE's auto-completion to see which parameters are expected... Your code shouldn't be written to make your IDE happy, instead it should be easy to be uses by other coders. 编码不应该满足任何IDE要求,而是在谈论not use the IDE's auto-completion to see which parameters are expected...补全功能not use the IDE's auto-completion to see which parameters are expected...不应编写使您的IDE满意的代码,而应该易于被其他编码人员使用。 What does this mean? 这是什么意思? Your exposed public interfaces should be intuitive and easy to learn by people, not IDEs. 您公开的公共界面应该直观易懂,而不是IDE易于学习。 I'd say one of the many requisites that good code satisfies is whether you don't even need to read any doc at all but the public interface are self-explanatory. 我要说的是,良好的代码可以满足的许多先决条件之一是,您是否甚至不需要阅读任何文档,但公共接口是不言自明的。

Here's an interesting programming quote for you to consider: 这是一个有趣的编程报价供您考虑:

The ideal numbers of arguments for a function is zero (niladic). 函数的理想参数个数为零(尼拉度)。 Next comes one (monadic), followed closely by two (dyadic). 接下来是一个(单声道),紧接着是两个(双声道)。 Three arguments (triadic) should be avoided where possible. 在可能的情况下,应避免使用三个参数(三重性)。 More than three (polyadic) requires very special justification ‐ and then shouldn't be used anyway. 超过三个(polyadic)需要非常特殊的理由-因此无论如何都不应使用。

By looking at the non-relevant example you've proposed I can say straight-away that's not good OOP design, not just in terms of exposed public interfaces but also in terms of relationships (hierarchies should be created with proper reasoning). 通过查看您提出的不相关的示例,我可以直截了当地说这不是好的OOP设计,不仅在公开的公共接口方面,而且在关系方面(层次结构都应使用适当的推理来创建)。 Maybe if you presented a real world-case I could advice further to make proper refactoring but with meaningless imaginary code with no use-cases and fictional meaningless parameters I don't see the point. 也许如果您提出了一个真实的案例,我可以进一步建议进行适当的重构,但是使用无用的虚构代码,没有用例和虚构的无用参数,我看不到重点。

In any case, the best advice I can give you is about trying to come up with good encapsulated objects with proper logical relationships and thin self-explanatory public interfaces. 无论如何,我能为您提供的最佳建议是尝试提出具有适当逻辑关系和精简的不言自明的公共接口的良好封装对象。 Said otherwise, make your code "attractive" and "pleasant" to be used by others and you'll be fine :) 否则,使您的代码“有吸引力”和“令人愉悦”以供他人使用,那么您会很好的:)

Don't think of dependency injection as something you need special support for. 不要将依赖注入视为需要特殊支持的东西。 It's just a technique where you move logic from inside a function to outside that function. 这只是一种将逻辑从函数内部移动到该函数外部的技术。 That is, instead of 也就是说,代替

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)

you write 你写

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)))

This provides better separation, as (for example) A no longer needs to know how to create an instance of B (or, for that matter, that it even gets an instance of B ). 这提供了更好的分离,因为(例如) A不再需要知道如何创建B的实例(或者就此而言,它甚至可以获取 B的实例)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM