如何使用条件定义类?

时间:2019-05-05 12:14:26

标签: python

我想生成一个class,其中输入元素的数量由TrueFalse的条件确定。我尝试过这样的事情

class Test:
    def __init__(self, cond):
        self.cond = cond
        if cond == True:
            def __call__(self, a, b, c):
                d1 = a + b + c
                return d1
        elif cond == False:
            def __call__(self, a, b):
                d2 = a + b
                return d2

result1 = Test(cond=True)(a, b, c)
result2 = Test(cond=False)(a, b)

但是它显然不起作用,并且提供以下错误:

TypeError: 'Test' object is not callable

我怀疑我使用了错误的def类型,因为__init__在这种情况下可能不合适。

  • 以这种方式使用class的最Python方式是什么?
  • 甚至可以使用class来做到这一点吗?

我知道使用def函数而不是class会很容易。

7 个答案:

答案 0 :(得分:5)

我将重组您的代码。在__init__()中添加仅用于初始化变量的条件或逻辑不是一个好主意。

相反,应该将dunder __call__()分开,以便可以在类实例化时调用它。

class Test:
    def __init__(self, cond):
        self.cond = cond

    def __call__(self, a, b, c=0):
        if self.cond:
            return a + b + c
        else:
            return a + b

a, b, c = 1, 2, 3

result1 = Test(cond=True)(a, b, c)
result2 = Test(cond=False)(a, b)

print(result1)  # 6
print(result2)  # 3

答案 1 :(得分:3)

首先避免这种情况的常见方法是定义一个子类。

uint64_t
从表面上看,这几乎没有改善;但是在您可能会在多个地方使用有条件的情况下,这种重组可以相当快地收回投资。

答案 2 :(得分:2)

不要执行条件函数定义。

class Test:
    def __init__(self, cond):
        self.cond = cond

    def __call__(self, a, b, c=0):
        if self.cond:
           return a + b + c
        else:
           return a + b

a, b, c = 1, 2, 3

print(Test(cond=True)(a, b, c))
# => 6

print(Test(cond=False)(a, b))
# => 3

此外,请勿进行类似if cond == True:elif cond == False:的比较,这也是一种反模式。如果假设cond是布尔值,则if cond:else:很好。

答案 3 :(得分:2)

不涉及元类的事情,最简单的实现方法是将__call__定义为接受可变数量的参数。

class Test:
    def __init__(self, cond):
        self.cond = cond

    def __call__(self, *args):
        if self.cond:
            if len(args) != 3:
                raise ValueError('When cond=True, requires 3 arguments')
        else:
            if len(args) != 2:
                raise ValueError('When cond=False, requires 2 arguments')

        return sum(args)

答案 4 :(得分:2)

在这种情况下,您可以通过将参数作为参数的集合来处理,从而在python上获得更好的Python效果(因此,请跳过cond的初始配置,因为Python不会强迫您使用固定数量的他们),或者:

  1. 使用列表作为参数:
class Test:
    def __call__(self, args):
        return sum(args)

my_test = Test()
print(my_test([1, 2, 3]))
# output: 6
  1. 使用“ varargs”接受任意数量的位置参数:
class Test:
    def __call__(self, *args):
        return sum(args)

my_test = Test()
print(my_test(1, 2, 3))
# output: 6

请注意,*args实际上是一个列表,主要区别在于可调用对象的调用方式。

这两个参数均可使用任意数量的参数。如果您具有与特定数量的参数相对应的特定行为,则可以像对待任何列表一样检查args的长度,并以相同的方式(例如,使用args[0])访问其元素。

如果需要初始配置,则可以强制执行某种行为,同时仍可以灵活地处理参数。例如,该类的以下版本拒绝输入长度不等于2 if cond is True的输入,如果输入长度为False则拒绝输入3的输入,并且都使用相同的方法:

class Test:
    def __init__(self, cond):
        self.cond = cond

    def __call__(self, *args):
        if (self.cond is True and len(args) != 3) \
        or (self.cond is False and len(args) != 2):
            raise ValueError

        return sum(args)

如果希望在实例化类时可以忽略它,则可以在cond的{​​{1}}中添加默认值。

答案 5 :(得分:1)

您的代码不起作用,因为__call__()__init__()方法的局部函数,因此在其中进行定义不会影响class'的定义。我不知道它是什么“ pythonic”,但是以下似乎是完成您想要的事情的最简单方法-本质上是使__init__()充当类装饰器。

class Test:
    def __init__(self, cond):
        self.cond = cond
        if cond:
            def __call__(self, a, b, c):
                d1 = a + b + c
                return d1
        else:
            def __call__(self, a, b):
                d2 = a + b
                return d2

        setattr(self.__class__, '__call__', __call__)  # Assign to class.


if __name__ == '__main__':

    a, b, c = 10, 30, 2
    result1 = Test(cond=True)(a, b, c)
    result2 = Test(cond=False)(a, b)
    print(f'{result1}, {result2}')  # -> 42, 40

答案 6 :(得分:1)

虽然我同意也许其他方法更好,但如果您还记得函数是Python中的一类对象,那么最直接的方法就是已经完成的工作以这种方式重新定义__call __:

class Test:
    def method1(self, a, b, c):
        d1 = a + b + c
        return d1
    def method2(self, a, b):
        d2 = a + b
        return d2

    def __init__(self, cond):
        self.cond = cond
        if cond:
            self.__call__=self.method1
        else:
            self.__call__=self.method2


Test(cond=True)(1, 2, 3)
6
Test(cond=False)(1, 2)
3