我想生成一个class
,其中输入元素的数量由True
或False
的条件确定。我尝试过这样的事情
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
会很容易。
答案 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不会强迫您使用固定数量的他们),或者:
class Test:
def __call__(self, args):
return sum(args)
my_test = Test()
print(my_test([1, 2, 3]))
# output: 6
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