2013-05-02 2 views
4

Я хотел бы задать динамические аргументы ключа-аргумента по умолчанию. Например, сДинамическое добавление ключей-аргументов к методу

class Module(object): 
    def __init__(self, **kargs): 
     set-default-key-args-of-method(self.run, kargs) # change run arguments 
    def run(self, **kargs): 
     print kargs 

Мы имеем:

m = Module(ans=42) 

m.run.im_func.func_code.co_argcount # => 2 
m.run.im_func.func_code.co_varnames # => ('self','ans','kargs') 
m.run.im_func.func_defaults   # => (42,) 
m.run()        # print {'ans':42} 

Я пытался что-то с types.CodeType (который я не очень понимаю) для функции (а не метод) и получил его (нуль не-к-неудачу), но добавленные ключевые аргументы не показывались в словаре kargs функции (он только печатает {})

Это изменение должно быть выполнено только для текущего экземпляра , На самом деле, я использую класс прямо сейчас (я OO в моем сознании), поэтому я хотел бы сделать это с помощью метода класса, но функция может быть лучше. Что-то вроде:

def wrapped_run(**kargs): 
    def run(**key_args): 
     print key_args 

    return wrap-the-run-function(run, kargs) 

run = wrapped_run(ans=42) 

run.func_code.co_argcount # => 1 
run.func_code.co_varnames # => ('ans','key_args') ## keep the 'key_args' or not 
run.func_defaults   # => (42,) 
run()      # print {'ans':42} 

Любые советы или идеи приветствуются.

Немного о контексте:

Класс модуля является своего рода функция упаковщик, который можно использовать, чтобы включить нижний конец функции в системе автоматически, но потока данных добавить промежуточные процедуры. Я бы хотел, чтобы функция запуска модуля (на самом деле, вероятно, это была функция __call___) имела правильный API, чтобы система потока данных могла легко генерировать вход модуля корректно.

Я использую Python 2.7

+0

Стоит спросить, если вы хотите добавлять функции к методу привязки или к несвязавшемуся методу (например, Должны ли они влиять только на текущий экземпляр или на каждый экземпляр класса?) – mgilson

+0

Итак, вы хотите иметь «конвейер» функций, которые можно настроить во время выполнения для перемещения данных? –

+0

@mgilson, справа, это только для текущего экземпляра –

ответ

0

Ради закрытия, я дам единственное решение, которое было найдено: использование exec (предложенный mgilson)

import os, new 

class DynamicKargs(object): 
    """ 
    Class that makes a run method with same arguments 
    as those given to the constructor 
    """ 
    def __init__(self, **kargs): 
     karg_repr = ','.join([str(key)+'='+repr(value) \ 
           for key,value in kargs.iteritems()]) 
     exec 'def run(self,' + karg_repr + ',**kargs):\n return self._run(' + karg_repr + ',**kargs)' 

     self.run = new.instancemethod(run, self) 

    def _run(self, **kargs): 
     print kargs 

# this can also be done with a function 
def _run(**kargs): 
    print kargs 

def dynamic_kargs(**kargs): 
    karg_repr = ','.join([str(key)+'='+repr(value) for key,value in kargs.iteritems()]) 
    exec 'def run(' + karg_repr + ',**kargs):\n return _run(' + karg_repr + ',**kargs)' 
    return run 


# example of use 
# -------------- 
def example(): 
    dyn_kargs = DynamicKargs(question='ultimate', answer=42) 
    print 'Class example \n-------------' 
    print 'var number:', dyn_kargs.run.im_func.func_code.co_argcount 
    print 'var names: ', dyn_kargs.run.im_func.func_code.co_varnames 
    print 'defaults: ', dyn_kargs.run.im_func.func_defaults 
    print 'run print: ', 
    dyn_kargs.run() 
    print '' 

    dyn_kargs = dynamic_kargs(question='foo', answer='bar') 
    print 'Function example \n----------------' 
    print 'var number:', dyn_kargs.func_code.co_argcount 
    print 'var names: ', dyn_kargs.func_code.co_varnames 
    print 'defaults: ', dyn_kargs.func_defaults 
    print 'run print: ', 
    dyn_kargs() 

The example функция печатает:

Class example 
------------- 
var number: 3 
var names: ('self', 'answer', 'question', 'kargs') 
defaults: (42, 'ultimate') 
run print: {'answer': 42, 'question': 'ultimate'} 

Function example 
---------------- 
var number: 2 
var names: ('answer', 'question', 'kargs') 
defaults: ('bar', 'foo') 
run print: {'answer': 'bar', 'question': 'foo'} 

Однако:

  • там могут быть проблемы, если значение аргумента недостаточно хорошо представлены их repr
  • Я думаю, что это слишком сложно (при этом не вещий), и лично я не использовал его
0

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

class Module(object): 
    def __init__(self, **kwargs): 
     self.run_defaults = kwargs 
    def run(self, **kwargs): 
     actual_values = self.run_defaults.copy() 
     actual_values.update(kwargs) 
     print actual_values 
+0

@mgilson thanks :-P –

+0

Также , почему копия? Почему бы просто не использовать 'kwargs.update (self.run_defaults)'? теперь просто используйте 'kwargs', как обычно. – mgilson

+0

Это неправильный путь: в этом случае значения по умолчанию будут отменять kwargs, что кажется нежелательным. –

2

kwargs является словарем, и все, что нам нужно нужно сделать, это сохраните его позже. Тогда пользователь может переопределить его своими значениями.

class Module(object): 
    def __init__(self, **kwargs): 
     self.defaults = kwargs 
    def run(**kwargs): 
     values = dict(self.defaults.items() + kwargs.items()) 
     print values 

EDIT

Вы, возможно, ищете lambda поколения функции?

def wrapfunc(**kwargs): 
    def run(kwargs): 
     print kwargs 
    return lambda x: run(dict(kwargs.items() + x.items())) 

run = wrapfunc(ans=42) 
run({}) 
run({'ans':12}) 
+0

Почему бы просто не использовать 'kwargs.update (self.defaults)'? – mgilson

+0

@mgilson Потому что тогда значения по умолчанию переопределяют специально переданные в параметрах. –

+0

Моя точка зрения заключается не в том, чтобы получить аргумент по умолчанию в методе run, но для того, чтобы API-интерфейс запуска был подходящим для инструментов для обертывания объектов –

2

Вы можете искать что-то вроде этого:

class Module(object): 
    def __init__(self, **kargs): 
     old_run = self.run.im_func 
     def run(self,**kwargs): 
      kargs_local = kargs.copy() 
      kargs.update(kwargs) 
      return old_run(self,**kargs) 
     self.run = run.__get__(self,Module) 

    def run(self,**kargs): 
     print kargs 

m1 = Module(foo=3,bar='baz') 
m1.run() 
print type(m1.run) 

m2 = Module(foo=4,qux='bazooka') 
m2.run() 
print type(m2.run) 

Я только что создал оболочку instancemethod вокруг предыдущей функции. (частично вдохновленный this post).

В качестве альтернативы:

from functools import partial 
from types import MethodType 

class Module(object): 
    def __init__(self, **kargs): 
     self.run = MethodType(partial(self.run.im_func,**kargs),self,Module) 

    def run(self,**kargs): 
     print kargs 

, но это еще не дает API, который вы ищете ...

+0

Этот ответ хорошо с моим ограничением на метод, но API по-прежнему неверен. Интересна схема упаковки. –

+0

@Juh_ - Что именно вы подразумеваете под «API не совсем корректно». Чего вы хотите? – mgilson

+0

co_argcount, co_varnames и т. Д. Объекта функции метода. Простой тест с ipython делает «m.run»? и отображение ожидаемого API: m.run (self, foo = 3, bar = 'baz', ** kargs) –

Смежные вопросы