当参数恒定时,是否适合在python类中使用闭包?

时间:2019-05-16 16:48:34

标签: python

这是关于编码约定以及某些内容是否“ pythonic”或良好实践的问题。

说我有一个像这样的python类:

class ClassName:
    def __init__(self, arg1, arg2, arg3, arg4, arg5):
        self.arg1 = arg1
        self.arg1 = arg2
        self.arg1 = arg3
        self.arg1 = arg4
        self.arg1 = arg5

        self.some_var = 0


    def some_function1(self):
        make use of self.arg1, self.arg2
        modify self.some_var

    def some_function2(self):
        make use of self.arg3, self.arg4, self.arg5
        modify self.some_var

将所有arg都视为常量,以便在整个类中用于计算和方法。

除了这样做,我有时使用以下模式:

class ClassName:
    def __init__(self, arg1, arg2, arg3, arg4, arg5):
        self.some_var = 0

        self.some_function1 = self.some_function1(arg1, arg2)
        self.some_function2 = self.some_function2(arg3, arg4, arg5)

    def some_function1(self, arg1, arg2):
        def inner():
            make use of arg1, arg2
            modify self.some_var
        return inner

    def some_function2(self, arg3, arg4, arg5):
        def inner():
            make use of arg3, arg4, arg5
            modify self.some_var
        return inner

由于我们传递给__init__方法的变量仅在某些计算中用作常量,因此有意义的是,而不是使它们成为成员变量,而是将它们绑定到函数闭包(从而为它们提供更合适的作用域) )。但是,我认为这样做虽然很有意义,但对某些人来说,这很不寻常,而且可能并不明显。

这种格式不好吗?

3 个答案:

答案 0 :(得分:1)

接下来要出现的维护者绝对会讨厌您。这种方法从哪里来?为什么基于我正在查看的类还是实例,它具有不同的签名?我该如何继承这个东西?

不要那样做。请记住,“明确胜于隐含”。如果您在方法中使用self.foo,请实际将其称为self.foo,以便下一个人可以一目了然地知道其定义位置。老实说,很可能下一个人会是您,从现在起六个月后的凌晨2点,当某件事发生故障并且您要对其进行故障排除时。您保存的想法可能是您自己的。

答案 1 :(得分:0)

恐怕这将是非常主观的,在这里我不能要求太多权限,所以请带点盐。

我不认为使用闭包本质上是非Python的,但是我认为在实例上绑定闭包的类上的方法被掩盖的模式正逐渐进入非Python领域。这让我想知道,一个包含some_function1some_function2定义的类是否真的是这里最自然的抽象。即使他们将self作为参数接收,看起来也不像在使用sheet.setDefaultColumnWidth(numChars); 吗?也许它们在逻辑上是更独立的实体(类/函数),其实例可能由该类的实例拥有?

答案 2 :(得分:0)

我不一定会像您那样做,并且觉得读起来很奇怪。

对于您的问题,我有一些可能的解决方案-一个非常丑陋,但无论如何我都包括在内。

from functools import partial

class A():
    def __init__(self, arg1, arg2, arg3):
        self.B = self.B(self, arg1, arg2)
        self.arg3 = arg3

    class SomeFunction():
        def __init__(self, parent, arg1, arg2):
            self.parent = parent
            self.arg1 = arg1
            self.arg2 = arg2

        def __call__(self):
            self.parent.arg3 = 5
            return self.arg1 + self.arg2

class B():
    def __init__(self, arg1, arg2, arg3):
        self.some_function = partial(self.some_function, arg1=arg1, arg2=arg2)
        self.arg3 = arg3

    def some_function(self, arg1, arg2):
        self.arg3 *= 5
        return arg1 + arg2

在类A中,您将函数定义为可调用类,然后在__init__中将其重新定义为A实例上的实例。这是丑陋的。

在类B中,我利用functools.partial将方法替换为另一种方法,但将参数固定为arg1arg2。从技术上讲,这可能类似于A中的方法,但可读性更高,不需要对函数本身进行任何更改,这是一个好主意。

另一种方式是这样的:

class C():
    def __init__(self, arg1, arg2, arg3):
        self.some_function = lambda: self._some_function(arg1, arg2)
        self.arg3 = arg3

    def _some_function(self, arg1, arg2):
        self.arg3 *= 5
        return arg1 + arg2

因此,实际上是通过将实际操作委托给_some_function的lambda来定义函数的,这再次使函数实现保持不变。由于您使用“ closure”作为术语,因此您可能熟悉从rust(或从...获得语言rust的语言)窃取closure的概念。 我也尝试过使用函数/类装饰器,但由于它们无法在调用时访问参数,因此我认为它们无法在此处使用。

使用BC的Imo很好,最好使用C