2009-12-01 2 views
4

Хорошо, я соглашусь заранее, что это мега-клад, и я мог бы определенно реализовать это лучше. Это только болезненное любопытство заставляет меня узнать, как я могу это сделать.Перегрузка метода с функцией во время выполнения

Это возвращает ошибку параметра, потому что я поставляю overLoaded() только один аргумент вместо двух. Есть ли какая-то магия, чтобы сказать интерпретатору, что теперь это метод класса (я пытался украсить его с помощью @classmethod, я всегда понимал это как цель?)

+0

Оказывается, что 'classmethod' имеет особое значение в Python, так что я не думаю, что вы имеете в виду classmethod в заголовке. Возможно, повторная фраза, что-то вроде «Заменить метод с функцией, связанной во время выполнения»? –

+0

Done :) Спасибо, что указали это. – richo

ответ

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 уже в области видимости, вам не нужно передавать ее снова.

Модуль new Python не является решением, поскольку он был устаревшим с версии 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" 
+0

Это какая-то удивительная магия. Благодаря! – richo

+0

Что делать, если функция, которую вы хотите подключить, определяется вне класса init? В этом случае самость не входит в сферу действия. Я всегда забываю, как обрабатывать этот случай, потому что вы в основном присваиваете экземпляр несвязанного метода, он не заставляет его автоматически связываться ... –

+0

sj26 Покрыто это в его второй строфе с помощью частичного вызова(). – richo

3

Проблема в том, что вы пытаетесь добавить new метод экземпляра (не метод класса), и он не является обязательным. Python имеет функцию модуля для ручной привязки функций к экземплярам.

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

Редакция: По-видимому, модуль new устарел. Используйте вместо этого модуль types для метамагии.

import types 
self.method = types.MethodType(func, self, class) 
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