在运行时用函数重载方法

4 投票
3 回答
2217 浏览
提问于 2025-04-15 16:33

好的,我先承认这真的是个大杂烩,我肯定可以做得更好。只是出于好奇,我想知道我该怎么做。

class SomeClass(object):
    def __init__(self):
        def __(self, arg):
            self.doStuff(arg)
        self.overLoaded = __
    def doStuff(self, string):
        print string

SomeClass().overLoaded("test string")

这段代码返回了一个参数错误,因为我只给overLoaded()提供了一个参数,而不是两个。有没有什么特别的方法可以告诉解释器,这现在是一个类的方法呢?(我试过用@classmethod装饰它,我一直以为这就是它的目的?)

3 个回答

0

sj26的解决方案很好。还有另一种选择,如果你想设置一个可以被任何用户提供的函数或者对象的其他方法重载的方法,可以创建一个自定义描述符。这个描述符可以用作装饰器(类似于@classmethod或@staticmethod);它允许你在一个实例的字典中存储一个函数,并将其作为方法返回:

import types

class overloadable(object):
    def __init__(self, func):
        self._default_func = func
        self._name = func.__name__

    def __get__(self, obj, type=None):
        func = obj.__dict__.get(self._name, self._default_func)
        return types.MethodType(func, obj, type)

    def __set__(self, obj, value):
        if hasattr(value, 'im_func'): value = value.im_func
        obj.__dict__[self._name] = value

    def __delete__(self, obj):
        del obj.__dict__[self._name]

现在我们可以简单地用“@overloadable”来装饰一个函数:

class SomeClass(object):
    def doStuff(self, string):
        print 'do stuff:', string
    @overloadable
    def overLoaded(self, arg):
        print 'default behavior:', arg

当我们为某个特定实例重载它时,它会自动执行正确的操作:

>>> sc = SomeClass()
>>> sc.overLoaded("test string")    # Before customization
default behavior: test string

>>> sc.overLoaded = sc.doStuff      # Customize
>>> sc.overLoaded("test string")
do stuff: test string

>>> del sc.overLoaded               # Revert to default behavior
>>> sc.overLoaded("test string")
default behavior: test string
3

问题在于你试图添加一个新的实例方法(而不是类方法),但是它没有正确绑定。Python有一个模块功能,可以手动将函数绑定到实例上。

import new
self.method = new.instancemethod(func, self, class)

补充说明:显然,new模块已经不再推荐使用了。可以改用types模块来进行一些高级操作。

import types
self.method = types.MethodType(func, self, class)
8

别担心那个self参数,函数已经从本地环境中获取到了。

class SomeClass(object):
    def __init__(self):
        def __(arg):
            self.bar(arg)
        self.foo = __
    def foo(self, arg):
        print "foo", arg
    def bar(self, arg):
        print "bar", arg

SomeClass().foo("thing") # prints "bar thing"

当你创建一个实例的时候(我记得是在__new__之后,但在__init__之前),Python会自动把所有的方法绑定上去,这样实例就会作为第一个参数传入。如果你是后来添加一个方法,那你就需要手动传入这个实例。因为你在定义函数的时候,self已经在环境中了,所以不需要再传一次。

Python的new模块并不是一个解决方案,因为它从2.6版本开始就不再推荐使用了。如果你想创建一个“真正的”实例方法,可以用部分装饰器来实现,像这样:

import functools

class SomeClass(object):
    def __init__(self):
        def __(self, arg):
            self.bar(arg)
        self.foo = functools.partial(__, self)
    def foo(self, arg):
        print "foo", arg
    def bar(self, arg):
        print "bar", arg

SomeClass().foo("thing") # prints "bar thing"

撰写回答