动态调用重写的函数

2024-10-02 08:18:16 发布

您现在位置:Python中文网/ 问答频道 /正文

我遍历了一个列表,并希望对每个项调用一个函数,但是这个函数应该是可替换的。你知道吗

例如,我有以下脚本:

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的多个子类呢?你知道吗


Tags: textselfchildformathelloworldreturndef
2条回答

你想要这样的东西吗?你知道吗

exec_all(lambda x: x.a())

或:

def call_a(obj):
    return obj.a()
exec_all(call_a)

传递方法名,而不是方法。使用^{}获取方法:

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不同,unbound方法检查第一个参数是否是本例中正确类的实例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) 

但是故意传递错误的方法并不是一个好的设计。如果希望调用ChildB.a,则不应传递ChildA.a。Python并没有使这变得简单,因为这不是OOP的工作方式。 传递正确的方法或将方法名作为字符串传递(如上所示)都是更好的选择。你知道吗

相关问题 更多 >

    热门问题