2012-11-14 36 views
3

我有一堆模塊中的回調函數。他們都呼籲some_func()與第一個,比方說,兩個參數總是相同的,並從他們自己的論據中派生出來,而其他論點各不相同。像這樣:一種自動將參數傳遞給函數的方法?

from outer.space import some_func 
def callback_A(param1, param2, param3): 
    ... 
    some_func(param2+param1, param3, ..., ...) 
    ... 

def callback_B(param1, param2, param3, param4): 
    ... 
    some_func(param2+param1, param3, ..., ...) 
    ... 

而且它遍佈整個代碼。而且比param2 + param1更醜陋。

在C/C++,我只是做了宏觀

#define S_F(a,b) some_func(param2+param1, param3, a, b) 

,並開始使用S_F內的回調,而不是some_func。我可以在Python中做什麼?

+0

我拉的東西從我的答覆到目前爲止起來這裏,所以這一切都在一個地方:我不能改變'some_func'的定義。問題似乎是這樣的:如果事情要在一次掃描中得到幫助,它必須在回調函數之外完成,但param1等僅在*之內變得可見。 C預處理器通過很愚蠢的做法:它不關心param2 + param1是什麼,它只是文本。 –

回答

2

您可以使用functools.partial

>>> from functools import partial 
>>> def my_function(a,b,c,d,e): 
...  print (a,b,c,d,e) 
... 
>>> func_with_defaults = partial(my_function, 1, 2, e=5) 
>>> func_with_defaults(3, 4) 
(1, 2, 3, 4, 5) 

編輯:

既然你不提前擁有這些價值,你不能使用partiallambda。 你可能會想這樣做:

>>> A = lambda x: x + y 
>>> def do_something(y): 
...  return A(2) # hope it uses the `y` parameter... 
... 
>>> do_something(1) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 2, in do_something 
    File "<stdin>", line 1, in <lambda> 
NameError: global name 'y' is not defined 

但你可以看到這是行不通的。爲什麼?因爲當你定義一個函數時,python會保存你定義它的範圍,並用它來解決變量。

如果你有機會訪問some_func將有可能給你想要做什麼用「黑客」利用inspect解釋堆棧但這不是做一個強大的也不優雅的事情,所以做做到這一點。

我想你的情況做的就是重寫聲明。

如果你真的想避免這種情況,你可以使用exec試一下:

>>> def some_function(a,b,c): 
...  print(a,b,c) 
... 
>>> code = 'some_function(a+b,c,%s)' 
>>> 
>>> def func_one(a,b, c): 
...  exec code % 1 
... 
>>> def func_two(a,b,c): 
...  exec code % 2 
... 
>>> func_one(1,2,3) 
(3, 3, 1) 
>>> func_two(1,2,3) 
(3, 3, 2) 

但這醜陋

如果你只使用位置參數給你的函數,你可以做一些更優雅如:

>>> def compute_values(a,b,c): 
...  return (a+b, c) 
... 
>>> def func_one(a,b,c): 
...  some_function(*(compute_values(a,b,c) + (1,))) 
... 
>>> def func_two(a,b,c): 
...  some_function(*(compute_values(a,b,c) + (2,))) 
... 
>>> func_one(1,2,3) 
(3, 3, 1) 
>>> func_two(1,2,3) 
(3, 3, 2) 

但在這一點上你只是重複一個不同的文本,而你失去了很多的可讀性。 如果你想在Python中有預處理功能,你可以嘗試Python Preprocessing,儘管在你的情況下,我寧願重複函數調用。

+0

我不知道我可以在'partial'中預先替換的值:param1等是回調的參數,而不是1,2或5.在每個param1等變爲可見的回調中執行'partial'失敗整個幹嘗試。 –

+0

@AlexanderPavlov現在我明白你想要什麼了。不,沒有辦法做到這一點。至少不是一個乾淨,優雅和健壯的方式,因爲python沒有預處理器。根據確切的簽名,可能會有一些方法可以使用,但我認爲只需複製粘貼呼叫會更簡單。無論如何,我認爲這也是C中的一個醜陋的黑客攻擊,因爲宏沒有清楚地表明它使用了一些隱藏的參數,應該在正確類型的範圍內。 – Bakuriu

0
S_F = lambda a, b: some_func(param2+params1, param3, a, b) 

記住param1param2param3必須在lambda語句的範圍內都有效,就像some_func

+0

「param_i必須在lambda範圍內可用」是指我必須在每個callbackA,callbackB,...內部聲明它?有沒有辦法在模塊範圍內做到這一點? –

0

如何與keyword arguments而不是函數簽名的位置參數:

some_func(**kwargs): 
    Instead of position arguments, just use the keys that some_func can respond to. 
    You can defaults to the ones not included... 
    if key1 in kwargs: 
     #use key1... 
    #... 

def callback_A(param1, param2, param3): 
    some_func(key1=param1+parma2,key2=param3, other_key=...) 


def callback_B(param1, param2, param3, param4): 
    some_func(key1=param2+param1, key2=param3, other_keys=..., ...) 

編輯

如果你不能改變函數簽名some_func那麼你可以把它包裝:

def some_func(a,b,c,d,e): 
    print 'some_funct({}, {}, {}, {}, {})'.format(a,b,c,d,e) 

def some_func_wrap(**kwargs): 
    params={'p1':'default a','p2':'default b', 
       'p3':'default c','p4':'default d','p5':'default e'} 
    for p,arg in kwargs.items(): 
     try: 
      del params[p] # to raise an error if bad key 
      params[p]=arg 
     except KeyError: 
      print 'bad param to some_func_wrap()\n' 
      raise 

    some_func(*[params[k] for k in sorted(params.keys())]) 

some_func_wrap(p1=1,p2=1+3) 

打印:some_funct(1, 4, default c, default d, default e)

+0

我不能自由修改'outer.space',尤其是'some_func'。它只有位置參數。問題不在於忽略理由,而恰恰相反:我試圖通過它們,只是*默默地*。 –

+0

@Alexander Pavlov:你可以包裝'some_func'(相當於C宏...)或者使用函數裝飾器,然後...... – 2012-11-14 14:36:38

2

你可以將它基於裝飾:

import functools 
from outer.space import some_func 

def with_some(f): 
    @functools.wraps(f) 
    def wrapper(param1, param2, param3, *args): 
     new_args = f(*args) 
     return some_func(param1+param2, param3, *new_args) 

    return wrapper 

@with_some 
def callback_A(): 
    return() # Will call some_func(param1 + param2, param3) 

... 

@with_some 
def callback_B(param4): 
    return param4, # some_func(param1 + param2, param3, param4) 

包裹的功能都將有簽名f(param1, param2, param3, *args)