【问题标题】:Overload a method with a function at runtime在运行时用函数重载方法
【发布时间】:2009-12-01 03:53:55
【问题描述】:

好的,我承认这是一个巨大的组合,我肯定可以更好地实现它。只是病态的好奇心驱使我去探索如何做到这一点。

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 装饰它,我一直认为这是它的目的??)

【问题讨论】:

  • 原来classmethod在Python中有特定的含义,所以我不认为你的意思是你标题中的classmethod。也许换个说法,比如“用运行时绑定的函数替换方法”?
  • 完成 :) 感谢您指出这一点。

标签: python metaprogramming


【解决方案1】:

不用担心 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"

【讨论】:

  • 如果要插入的函数定义在类init之外怎么办?在这种情况下, self 不在范围内。我总是忘记如何处理这种情况,因为您基本上是为实例分配了一个未绑定的方法,它不会使其自动绑定...
  • sj26 在他的第二节中用 partial() 调用涵盖了这一点。
【解决方案2】:

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

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

编辑:显然new 模块已被弃用。使用 types 模块代替 metamagic。

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

【讨论】:

    【解决方案3】:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-21
      • 1970-01-01
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多