2011-04-14 241 views
2

我已經搜索了一點點,試圖找出這一個,但沒有得到我正在尋找的解決方案。如何從函數外部獲取函數參數和值?

這是我的使用情況:

  • 我想從對F/M的參數和值的函數/方法的文檔字符串表達式求值,但是從功能外(當被調用但外面執行函數
  • 我無法靜態更改我正在評估的源代碼(不能寫入新功能),但可以動態更改(即在運行時包裝函數或添加屬性)
  • 我寧願堅持使用標準庫中的工具,但如果沒有,我們願意嘗試外部庫會使得任務變得輕而易舉

這裏是什麼,我試圖做一個簡單的例子:

def f1(a,b): 
    """a==b""" 
    pass 

def f2(f): 
    f_locals = "get f's args and values before f is executed" 
    return eval(f.__doc__,None,f_locals) 

>>> f2(f1(2,2)) 
+2

我完全不明白。 – 2011-04-14 19:19:42

+3

爲什麼?您不能將'f1(2,2)'作爲參數傳遞給另一個函數,而不對其進行評估; 'f1(2,2)'的值是用這些參數調用的f1的返回值。 – vicvicvic 2011-04-14 19:21:16

+0

奇怪的問題.. – 2011-04-14 19:28:21

回答

3

雖然我不知道你爲什麼會想這樣做,你所描述什麼可以通過inspect模塊來實現。這個例子與我可以想到的原始例子很接近。

from inspect import getcallargs 
def f1(a,b): 
    """a==b""" 
    pass 

def f2(f, *f_args, **f_kwargs): 
    f_callargs = getcallargs(f, *f_args, **f_kwargs) 
    return eval(f.__doc__, None, f_callargs) 

f2(f1, 2, 2) 

這應該輸出True

請記住,這裏假定有很多關於傳遞給f2的函數的參數和文檔字符串,其中最重要的是沒有一個被檢查的函數是惡意的或者是畸形的。你爲什麼不想正常地調用函數,爲什麼你不想改變函數呢?

編輯:作爲Pajton指出,getcallargs是比較合適的位置,並刪除這兩個dictzip呼叫。上面的代碼已經更新,以反映這一點。

+1

當然'getcallargs(func [,* args] [,** kwds])'會比'getargspec'更好。 – pajton 2011-04-14 20:36:12

1

我不確定這是不是您正在尋找的,但是這裏有一個沒有檢查模塊的替代方案。

#!/usr/bin/python 
# -*- coding: utf-8-unix -*- 
""" 
This is a sample implementation of Inline.pm (Perl) in Python. 

Using @inline decorator, it is now possible to write any code 
in any language in docstring, and let it compile down to executable 
Python code at runtime. 

For this specific example, it simply evals input docstring, so 
code in docstring must be in Python as well. 
""" 

# Language compiler for MyLang 
class MyLang: 
    @classmethod 
    def compile(self, docstring): 
     # For this example, this simply generates code that 
     # evals docstring. 
     def testfunc(*arg, **kw): 
      return eval(docstring, None, kw) 
     return testfunc 

# @inline decorator 
def inline(lang): 
    def decorate(func): 
     parm = func.__code__.co_varnames[0:func.__code__.co_argcount] 
     fgen = lang.compile(func.__doc__) 
     def wrap(*arg, **kw): 
      # turn all args into keyword-args 
      kw.update(dict(zip(parm, arg))) 
      return fgen(**kw) 
     return wrap 
    return decorate 

@inline(MyLang) 
def myadd(a, b): 
    """a + b""" 

print(myadd(1, 9)) 
print(myadd(b = 8, a = 2)) 
print(myadd(a = 3, b = 7))