2013-05-02 93 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