2013-05-02 60 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 2013-05-02 12:36:23

+0

Sooo,你希望有一个可以在运行时配置的函数的“管道”,以便数据通过? – 2013-05-02 12:38:01

+0

@mgilson,对,仅针对当前实例 – 2013-05-02 12:38:03

回答

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() 

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
  • 我觉得它太复杂了(因此不符合Python),和个人来说,我没有使用它
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谢谢:-P – 2013-05-02 12:30:50

+0

,为什么要复制?为什么不使用'kwargs.update(self.run_defaults)'?现在只需像往常一样使用'kwargs'。 – mgilson 2013-05-02 12:30:59

+0

这是错误的方式:这种方式默认会覆盖kwargs,这似乎并不需要。 – 2013-05-02 12:31:41

2

kwargs是一本字典,而我们应该需要做的是保存以备后用。然后用户可以用它们的值覆盖它。

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

编辑

你或许在寻找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 2013-05-02 12:30:06

+0

@mgilson因为那么默认值会覆盖特定的传入选项。 – 2013-05-02 12:31:38

+0

我的观点并不是在run方法中获取默认参数,而是让run方法API适用于函数包装工具 – 2013-05-02 12:31:43

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仍然不正确。包装方案很有趣。 – 2013-05-02 13:26:48

+0

@Juh_ - 你的意思是“API不完全正确”。你想要什么? – mgilson 2013-05-02 13:28:07

+0

该方法的函数对象的co_argcount,co_varnames等。 ipython的简单测试是做“m.run?”并显示预期的API:m.run(self,foo = 3,bar ='baz',** kargs) – 2013-05-02 13:30:29