【问题标题】:pythonic way for function with loads of parameters and return values带有大量参数和返回值的函数的pythonic方式
【发布时间】:2017-02-10 18:04:25
【问题描述】:

我正在尝试在 Python 中运行模拟。为此,我需要定义一些参数并从中派生一些其他参数。然后,我使用这些函数调用几个函数,这些函数作用于这些参数并返回某个结果。目前我的代码看起来像这样

common(ai, bi, ..., hi):
    # calculations
    return ao, bo, ..., fo
func1(ai, bi, ..., xi):
    ao, bo, ..., fo = common(ai, bi, ..., hi)
    # calculations specific to func1
    return y
func1(ai, bi, ..., xi):
    ao, bo, ..., fo = common(ai, bi, ..., hi)
    # calculations specific to func2
    return z

ai = 0
bi = 1
...
xi = 2

print(func1(ai, bi, ..., xi))
print(func2(ai, bi, ..., xi))

我用... 缩写参数列表等,并在# calculations 部分执行计算。

我更喜欢使用func1(di=2) 调用函数,并为所有其他函数使用默认值。然而,这意味着使用命名参数,在这种情况下,我必须指定两次默认值(在func1func2 中)。我认为**kwargs 不会在这里工作,因为这需要显式传递默认值。

我一直在尝试将字典传递给函数并在传递之前设置非标准参数。然而,这使得函数调用更难理解,所以我觉得应该有一种更简单的方法来做到这一点。

由于函数只包含相对较长的方程,我不想像将它们存储在字典中并从那里调用它们时所需要的那样在变量名称中添加字符。这使得阅读方程式变得更加困难。

我的问题是是否有解决这个问题的pythonic方法。

【问题讨论】:

  • 你尝试过抽象类吗?

标签: python parameter-passing


【解决方案1】:

你有两个方法的参数和返回值类型相同,唯一的区别是函数内部的过程,即它们共享相同的接口和功能。

您想要一种简单的方法来定义函数,而不必编写默认参数或多次传入字典。

最好使用装饰器或类继承。

Decorator 使您能够定义返回函数的工厂方法,例如 func1func2。接口只在装饰器中使用默认参数定义一次,返回的函数在处理方法或核心方法中有所不同。

Class继承的工作原理类似,但通过方法继承,它更加灵活和通用。

【讨论】:

  • 我最终将公共代码放入装饰器函数并从那里调用特定代码。使用 **kwargs 为装饰器设置参数,如果未提供参数,则默认为某个值。这几乎解决了我的所有问题,除了 func1func2 都明确提供了参数列表。这样,我可以使我的方程式保持可读性。我知道这可能不是一个好的编码实践,但对我来说,使用 python 就是为了减少开发时间并开始真正的工作。对我来说,这意味着可读的方程式。
  • 事实上,在大多数面向对象编程中,它是一种相当常见的设计模式。你会很擅长的。
【解决方案2】:

您可以创建一个类以在其属性中保存变量:

class SimClass(object):

    def __init__(self, **kw):
        self.ao = kw.get('aa', VA_DEFAULT)
        self.bo = kw.get('bb', VB_DEFAULT)
        # ...
        self.zo = kw.get('zz', VZ_DEFAULT)

    def common(self, ai, bi, ..., hi):
        # calculations set self.ao, self.bo, ..., self.fo
        return

    def func1(self, ai, bi, ..., xi):
        self.common(ai, bi, ..., hi)
        # calculations specific to func1 using self.ao, self.bo, ..., self.fo
        return y

    def func1(self, ai, bi, ..., xi):
        common(ai, bi, ..., hi)
        # calculations specific to func2 using self.ao, self.bo, ..., self.fo
        return z

ai = 0
bi = 1
...
xi = 2

sim = SimClass(bb='BB', cc='CC')

print(sim.func1(ai, bi, ..., xi))
print(sim.func2(ai, bi, ..., xi))

【讨论】:

    【解决方案3】:

    编程中的函数参数不必像在数学中那样使用。你可能有一个方程式:

    z = sqrt((x2 - x1)^2 + (y2 - y1)^2),

    仅当变量具有短名称时才易于阅读。你想保留它是可以理解的。但是编程语言可能不是这样工作的,它们使用自己的语法和工具。

    编程中重构的重要原则之一是减少函数中传递参数的数量。最简单的方法是将所有传递的参数封装在一个对象中,并且只在函数之间传递该对象:

    import math
    
    class Line:
        def __init__(self, x1, x2, y1, y2):
             self.x1 = x1
             self.x2 = x2
             self.y1 = y1
             self.y2 = y2
    
    def getLineLength(line):        
        return math.sqrt((line.x2 - line.x1)**2 + (line.y2 - line.y1)**2)
    
    line = Line(3, 5, -2, 7)
    print(getLineLength(line))
    

    当然,您必须使用对象名称,并且原始方程式变得不那么可读。并且 Python 代码不必与数学公式完全一样。但是,您现在可以在代码的其他部分重新使用新类 Line

    如果函数足够复杂,可以看到它的作用,则可以进一步重构:

    def getLineLength(line):
        dx = line.x2 - line.x1
        dy = line.y2 - line.y1      
        return math.sqrt(dx**2 + dy**2)
    

    这个函数可以移动到Line 类中:

    class Line:
    ...
    
    def getLength(self):
        dx = self.x2 - self.x1
        dy = self.y2 - self.y1      
        return math.sqrt(dx**2 + dy**2)
    
    line = Line(3, 5, -2, 7)
    print(line.getLength())
    

    ...甚至进一步重构:

    import math
    
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __sub__(self, other):
            return Point(other.x - self.x, other.y - self.y)
    
    class Line:
        def __init__(self, x1, x2, y1, y2):
             self.start = Point(x1, y1)
             self.end = Point(x2, y2)
    
        def getLength(self):
            d = self.start - self.end
            return math.sqrt(d.x**2 + d.y**2)
    
    line = Line(3, 5, -2, 7)
    print(line.getLength())
    

    新类Point可以在其他地方重复使用。

    因此,可以保持代码干净、简单和可重用。随着脚本的发展,可重用性变得非常重要。

    【讨论】:

      猜你喜欢
      • 2011-03-09
      • 2020-06-22
      • 2015-10-19
      • 2014-03-31
      • 2022-11-29
      • 1970-01-01
      • 2019-06-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多