2012-11-01 94 views
27

如果已經提出並回答了此問題,請道歉。 我需要做的是非常簡單的概念,但不幸的是,我一直無法在網上找到答案。使用自定義名稱創建Python動態函數

我需要在運行時用自定義名稱在Python(Python2.7)中創建動態函數。每個函數的主體也需要在運行時構造,但對於所有函數來說(幾乎)是相同的。

我從名字列表開始。

func_names = ["func1", "func2", "func3"] 

注意,FUNC_NAME列表可以保存任意名稱的列表,所以名稱將不僅僅是FUNC1,FUNC2,FUNC3,....

我想要的結果是:

def func1(*args): 
     ... 

    def func2(*args): 
     ... 

    def func3(*args): 
     ... 

我需要這樣做的原因是,每個函數名稱都對應於一個測試用例,然後從外部調用該測試用例。

更新:沒有用戶輸入。我正在綁定一個更大的模塊的兩端。一端確定測試用例是什麼,另一方面是填充測試用例名稱列表。另一端是函數本身,它們必須與測試用例的名稱進行1:1映射。所以我有測試用例的名稱,我知道我想對每個測試用例做什麼,我只需要創建具有測試用例名稱的函數。由於測試用例的名稱是在運行時確定的,所以基於這些測試用例的功能創建也必須在運行時。

更新:我也可以將這個自定義命名函數包裝在類中,如果這樣會讓事情變得更容易。

我可以硬編碼函數的內容(因爲它們幾乎是相同的)在一個字符串中,或​​者我可以將它從之前定義的基類中取出。只需要知道如何用這個內容填充函數。

例如:

func_content = """ 
        for arg in args: 
         print arg 
        """ 

由於提前,

馬赫迪

回答

1

您可能需要使用eval;你將構建包含每個函數的Python定義的字符串(即以def func1 ....開頭的多行字符串),然後你會得到它eval它。

但我會爲每個這樣的函數生成一個唯一的名稱(例如genfun345)。不要對這些名稱使用一些未經檢查的用戶輸入。因爲如果名稱與Python中的某個已知名稱相同,則很難調試出災難。

你相信這些功能的輸入嗎?你關心惡意軟件還是濫用?

閱讀關於hygenic macros上的維基百科。

+0

沒有用戶輸入。我正在綁定一個更大的模塊的兩端。一端確定測試用例是什麼,另一方面是填充測試用例名稱列表。另一端是函數本身,它必須與測試用例的名稱進行1:1映射。所以我有測試用例的名稱,我知道我想對每個測試用例做什麼,我只需要創建具有測試用例名稱的函數。由於測試用例的名稱是在運行時確定的,所以基於這些測試用例的功能創建也必須在運行時。 – mahdiolfat

33

對於你所描述的,我不認爲你需要下降到eval或宏 - 通過閉包創建函數實例應該工作得很好。例如:

def bindFunction1(name): 
    def func1(*args): 
     for arg in args: 
      print arg 
     return 42 # ... 
    func1.__name__ = name 
    return func1 

def bindFunction2(name): 
    def func2(*args): 
     for arg in args: 
      print arg 
     return 2142 # ... 
    func2.__name__ = name 
    return func2 

但是,您可能希望將這些函數按名稱添加到某個範圍,以便您可以按名稱訪問它們。

>>> print bindFunction1('neat') 
<function neat at 0x00000000629099E8> 
>>> print bindFunction2('keen') 
<function keen at 0x0000000072C93DD8> 
+0

簡單而有效。 –

6

有可能是一種內省做這種事情的,但我不認爲這將是Python的解決問題的方法。

我認爲你應該利用python中的函數性質作爲一級公民。按照Shane Holloway指出的那樣使用閉包,根據需要自定義功能內容。然後對於動態名稱綁定,使用一個字典,其中的鍵是動態定義的名稱,並且這些值將是函數本身。

def function_builder(args): 
    def function(more_args): 
     #do stuff based on the values of args 
    return function 

my_dynamic_functions = {} 
my_dynamic_functions[dynamic_name] = function_builder(some_dynamic_args) 

#then use it somewhere else 
my_dynamic_functions[dynamic_name](the_args) 

希望它對您的使用情況有意義。

1

如果我正確理解你的要求,這聽起來像你只是想動態地分配現有功能的新的或替代的名字 - 在這種情況下的東西沿着以下行應該做的工作:

import sys 

testcases = [] 
def testcase(f): 
    """ testcase function decorator """ 
    testcases.append(f) 
    return f 

@testcase 
def testcase0(*args): 
    print 'testcase0 called, args:', args 

@testcase 
def testcase1(*args): 
    print 'testcase1 called, args:', args 

@testcase 
def testcase2(*args): 
    print 'testcase2 called, args:', args 

def assign_function_names(func_names, namespace=None): 
    if namespace is None: 
     namespace = sys._getframe(1).f_globals # default to caller's globals 
    for name, func in zip(func_names, testcases): 
     func.__name__ = name # optional 
     namespace[name] = func 

assign_function_names(["funcA", "funcB", "funcC"]) 

funcA(1, 2, 3) 
funcB(4, 5) 
funcC(42) 
+0

感謝您的回覆。但事實並非如此。它們不是現有的函數,此外,要動態創建的函數的數量也是未知的(僅在運行時才知道)。 – mahdiolfat

+0

如果你可以「硬編碼函數的內容」,那麼你可以在.py文件中使用'def xxx(yyy):'作爲內容前的內容,並將其作爲一個存在的函數 - 你認爲你「通過將其放入字符串並動態創建一個函數來獲得它? – martineau

+0

我想你已經誤解了我在這裏要做的事情,並沒有真正回答我的問題,而是繼續告訴我做別的事情。儘管如此。我已經解決了我的問題。 – mahdiolfat

10

擴展到Shane的答案,因爲在尋找類似問題的解決方案時我剛發現這個問題。注意變量的範圍。您可以通過使用生成器函數來定義範圍來避免範圍問題。這裏是一個定義一個類方法的一個例子:

class A(object): 
    pass 

def make_method(name): 
    def _method(self): 
     print("method {0} in {1}".format(name, self)) 
    return _method 

for name in ('one', 'two', 'three'): 
    _method = make_method(name) 
    setattr(A, name, _method) 

在使用中:

In [4]: o = A() 

In [5]: o.one() 
method one in <__main__.A object at 0x1c0ac90> 

In [6]: o1 = A() 

In [7]: o1.one() 
method one in <__main__.A object at 0x1c0ad10> 

In [8]: o.two() 
method two in <__main__.A object at 0x1c0ac90> 

In [9]: o1.two() 
method two in <__main__.A object at 0x1c0ad10>