用Python编写一个接受回调的类?

问题描述 投票:0回答:7

我需要编写一个类,允许子类使用函数名称设置属性。然后该函数必须可以从类的实例中调用。

例如,我说我需要编写一个Fruit类,其中子类可以传入欢迎消息。 Fruit 类必须公开一个可以设置的属性 print_callback。

class Fruit(object):
    print_callback = None

    def __init__(self, *args, **kwargs):
        super(Fruit, self).__init__(*args, **kwargs)
        self.print_callback("Message from Fruit: ")

我需要公开一个可由此代码使用的 API(需要明确的是,此代码不能更改,假设它是第 3 方代码):

def apple_print(f):
    print "%sI am an Apple!" % f

class Apple(Fruit):
    print_callback = apple_print

如果我跑步:

mac = Apple()

我想得到:

来自水果的讯息:我是苹果!

相反,我得到:

TypeError:apple_print() 恰好需要 1 个参数(给定 2 个)

我认为这是因为 self 作为第一个参数传入。

那么我该如何编写Fruit类呢?谢谢!

python callback
7个回答
11
投票

Python 假定任何绑定在类作用域内的函数都是方法。如果您想将它们视为函数,则必须深入研究它们的属性以检索原始函数对象:

def __init__(self, *args, **kwargs):
    super(Fruit, self).__init__(*args, **kwargs)

    # The attribute name was changed in Python 3; pick whichever line matches
    # your Python version.
    callback = self.print_callback.im_func  # Python 2
    callback = self.print_callback.__func__ # Python 3

    callback("Message from Fruit: ")

6
投票

可以直接使用:

class Apple(Fruit):
    print_callback = staticmethod(apple_print)

或:

class Apple(Fruit):
    print_callback = classmethod(apple_print)

在第一种情况下,您只会获得一个参数(原始参数)。在第二个中,您将收到两个参数,其中第一个参数是调用它的类。

希望这会有所帮助,并且更短且更简单。


3
投票

更新:合并abourget的建议来使用

staticmethod

试试这个:

def __init__(self, *args, **kwargs):
    super(Fruit, self).__init__(*args, **kwargs)

    # Wrap function back into a proper static method
    self.print_callback = staticmethod(self.print_callback)

    # And now you can do:
    self.print_callback("Message from Fruit: ")

3
投票

当我发现这个问题时,我正在寻找更像这样的东西:

class Something:
    def my_callback(self, arg_a):
        print arg_a

class SomethingElse:
    def __init__(self, callback):
        self.callback = callback

something = Something()
something_else = SomethingElse(something.my_callback)
something_else.callback("It works...")

1
投票

元类还有一个更脏的解决方案:

def apple_print(f):
    print "Apple " + f

class FruitMeta(type):
    def __new__(cls, name, bases, dct):
        func = dct["print_callback"]
        dct["print_callback"]=lambda x,f,func=func: func(f)
        return type.__new__(cls,name,bases,dct)

class Fruit(object):
    __metaclass__ = FruitMeta
    print_callback = None
    def __init__(self):
        super(Fruit,self).__init__()
        self.print_callback("Msg ")

class Apple(Fruit):
    print_callback = apple_print

mac = Apple()here

它在创建类之前对其进行操作!


0
投票

您可以使用Python 3的

__func__
或Python 2
im_func
来获取函数对象并直接调用它。 对于全局函数,它可以正常工作。但是如果你想调用特定类实例的函数, 您需要提供类实例作为回调函数的第一个参数。

这是示例:

class Test1:
    def __init__(self):
        self.name = 'Test1 instance'

    def OnPrint(self, value):
        assert isinstance(self, Test1), 'Wrong self argument type'
        print(f'{self.name}: Test1.OnPrint with value {value}')

t1 = Test1()
f1 = t1.OnPrint.__func__

print('1')
f1(t1, 5)

# Will print:
#     Test1 instance: Test1.OnPrint with value 5
print('2')

#f1(1,2)
#AssertionError: Wrong self argument type

#f1(2)
#TypeError: Test1.OnPrint() missing 1 required positional argument: 'value'

请注意,如果您确信该调用是正确的,则可以省略

assert isinstance


0
投票

我最近遇到了这个问题(不敢相信我以前没有见过它,但无论如何)。 如果将回调函数存储为属性不起作用(因为正如 John Millikin 指出的那样,Python 假设类范围内绑定的函数是一个方法(因此将 self 传递给它)),那么您可以通过存储来绕过它在容器(例如列表或元组)中回调,然后 Python 不会假设它是一个方法...

def foo(x):
    print(x)

class A():
    def addCallback(self, new_callback):
        self.callback = [new_callback,]

    def doit(self,x):
        self.callback[0](x)

a = A()
a.addCallback(foo)
a.doit(42)
© www.soinside.com 2019 - 2024. All rights reserved.