在运行时使用函数重载方法

时间:2009-12-01 03:53:55

标签: python metaprogramming

好的,我会先前承认这是一个巨大的克服,我可以肯定地更好地实现这一点。只有病态的好奇心才能让我知道如何做到这一点。

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 :(得分: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__之后,iirc,但在__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"

答案 1 :(得分:3)

问题是您正在尝试添加新的实例方法(而不是类方法)并且它没有正确绑定。 Python有一个模块函数可以手动将函数绑定到实例。

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

编辑:显然不推荐使用new模块。使用types模块代替metamagic。

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

答案 2 :(得分: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