我遍历列表并希望在每个项目上调用一个函数,但此函数应该是可替换的。
例如,我有以下脚本:
class Parent(object):
def a(self, text):
raise NotImplementedError("called Parent.a")
def b(self, text):
raise NotImplementedError("called Parent.b")
class ChildA(Parent):
def a(self, text):
return "A.a: {}".format(text)
def b(self, text):
return "A.b: {}".format(text)
class ChildB(Parent):
def a(self, text):
return "B.a: {}".format(text)
def b(self, text):
return "B.b: {}".format(text)
# the separation is ONLY so that the first exec_all doesn't fail
# in my production code it's a list of mixed instances
children = [
ChildA(), # obviously here might be several different ChildA instances
]
childrenMixed = children + [
ChildB(), # obviously here might be several different ChildB instances
]
def exec_all(method, children):
for child in children:
try:
print(method(child, "Hello world"))
except Exception as e:
print("Unable to call method for child '{}': {}".format(child, e.message))
exec_all(ChildA.a, children) # works
exec_all(ChildA.b, children) # works
exec_all(ChildA.a, childrenMixed) # TypeError
exec_all(ChildA.b, childrenMixed) # TypeError
exec_all(Parent.a, childrenMixed) # NotImplementError
exec_all(Parent.b, childrenMixed) # NotImplementError
前两个exec_all
确实可以正常工作,但接下来的两个不能正常工作,因为它会尝试拨打ChildA.a
中不存在的ChildB
。最后两个提升了NotImplementedError
。
看起来应该是这样的:
A.a: Hello world # 1st exec_all
A.b: Hello world # 2nd exec_all
A.a: Hello world # 3rd exec_all
B.a: Hello world # but TypeError
A.b: Hello world # 4th exec_all
B.b: Hello world # but TypeError
A.a: Hello world # 5th exec_all
B.a: Hello world # but NotImplementError
A.b: Hello world # 6th exec_all
B.b: Hello world # but NotImplementError
那么我如何支持Parent
的多个子类?
答案 0 :(得分:2)
你是否经历过这样的事情?
exec_all(lambda x: x.a())
或:
def call_a(obj):
return obj.a()
exec_all(call_a)
答案 1 :(得分:0)
传递方法名称,而不是方法。使用getattr(child, methodname)
获取方法:
class Parent(object):
def a(self, text):
raise NotImplementedError("called Parent.a")
def b(self, text):
raise NotImplementedError("called Parent.b")
class ChildA(Parent):
def a(self, text):
return "A.a: {}".format(text)
def b(self, text):
return "A.b: {}".format(text)
class ChildB(Parent):
def a(self, text):
return "B.a: {}".format(text)
def b(self, text):
return "B.b: {}".format(text)
children = [ ChildA(), ]
childrenMixed = children + [ ChildB(), ]
def exec_all(methodname, children):
for child in children:
method = getattr(child, methodname)
print(method("Hello world"))
print
exec_all('a', children)
exec_all('b', children)
exec_all('a', childrenMixed)
exec_all('b', childrenMixed)
exec_all('a', childrenMixed)
exec_all('b', childrenMixed)
产量
A.a: Hello world
A.b: Hello world
A.a: Hello world
B.a: Hello world
A.b: Hello world
B.b: Hello world
A.a: Hello world
B.a: Hello world
A.b: Hello world
B.b: Hello world
在Python2中,ChildA.a
是未绑定的方法。与Python3不同,未绑定方法检查第一个参数是否是正确类的实例 - 在本例中为ChildA
。这就是调用
ChildA.a(ChildB(), text)
提出TypeError
:
TypeError: unbound method a() must be called with ChildA instance as first argument (got ChildB instance instead)
在Python3中,这样的调用是可以的,但是如果你这样做,那么将所有这些方法设置为普通函数而不是方法可能会更好。
听起来你真的想在发布它们时保留函数调用的形式:
exec_all(ChildA.a, children)
exec_all(ChildA.b, children)
exec_all(ChildA.a, childrenMixed)
exec_all(ChildA.b, childrenMixed)
exec_all(Parent.a, childrenMixed)
exec_all(Parent.b, childrenMixed)
如果我们将此作为固定要求,那么您可以通过定义exec_all
来获得所需的行为,如下所示:
class Parent(object):
def a(self, text):
raise NotImplementedError("called Parent.a")
def b(self, text):
raise NotImplementedError("called Parent.b")
class ChildA(Parent):
def a(self, text):
return "A.a: {}".format(text)
def b(self, text):
return "A.b: {}".format(text)
class ChildB(Parent):
def a(self, text):
return "B.a: {}".format(text)
def b(self, text):
return "B.b: {}".format(text)
children = [
ChildA(),
]
childrenMixed = children + [
ChildB(),
]
def exec_all(method, children):
methodname = method.__name__
for child in children:
method = getattr(child, methodname)
print(method("Hello world"))
exec_all(ChildA.a, children)
exec_all(ChildA.b, children)
exec_all(ChildA.a, childrenMixed)
exec_all(ChildA.b, childrenMixed)
exec_all(Parent.a, childrenMixed)
exec_all(Parent.b, childrenMixed)
但故意传递错误的方法并不是一个好的设计。如果希望调用ChildA.a
,则不应传递ChildB.a
。 Python并不简单,因为这不是OOP的工作方式。
传递正确的方法或将方法名称作为字符串传递(如上所示)是更好的选择。