2013-03-22 63 views
1

我已經編寫了一個python模塊mymod.py,它也可以用作命令行中的獨立程序。Python 2.7:如何傳遞獨立/模塊代碼的選項

在mymod.py中,我定義了幾個函數(其中默認值是使用關鍵字設置的), 和if __name__=="__main__"塊將模塊用作獨立程序。

我想覆蓋一些默認選項的可能性,因此在主程序I import argparse中並使用它來分析選項。我使用字典來存儲 的默認值,這樣如果有一天我需要更改默認值,我可以很容易地將其修改爲只在一個地方的值 。

它的工作原理,但我發現代碼不是「乾淨」,並認爲可能我沒有以適當的pythonic方式做它。

這是一個玩具爲例,說明我做的:

#!/usr/bin/env python 
#mymod.py 

__default_options__={ 
    "f1_x":10, 
    "f2_x":10 
} 

def f1(x=__default_options__["f1_x"]): 
    return x**2 

def f2(x=__default_options__["f2_x"]): 
    return x**4 

# this function is the "core" function which uses f1 and f2 
# to produce the main task of the program 
def f(x=__default_options__["f1_x"],y=__default_options__["f2_x"]): 
    return f1(x)+f2(y) 

if __name__=="__main__": 
    import argparse 
    parser = argparse.ArgumentParser(description = "A toy application") 
    parser.add_argument("--f1-x",help="the parameter passed to f1", 
    default=__default_options__["f1_x"], type = float,dest = "x") 
    parser.add_argument("--f2-x",help="the parameter passed to f2", 
    default=__default_options__["f2_x"], type = float, dest = "y") 

    options= parser.parse_args() 
    print f(options.x,options.y) 

像我這樣做是有點麻煩,可能針對的Python和argparse都精神傳遞的默認值。

這段代碼如何改進以使其變得更加pythonic,並且最好使用argparse?

+0

我想,你不應該使用帶有兩個下劃線的變量,因爲它可能會在未來的Python版本中破壞代碼。 – 2013-03-22 12:08:46

+1

此外,函數中的參數是在定義時評估的,而不是函數調用時的參數。因此,如果您在運行時更改'__default_options__',那麼'f1','f2'和'f'的默認值不會改變(這可能或可能不是您想要的) – 2013-03-22 12:12:06

+0

我使用了下劃線,因爲我想讓用戶不用更改默認選項..如果他們想覆蓋值,他們必須調用函數,如f(x = 3),例如... – lucacerone 2013-03-22 13:26:59

回答

1

可以使用`ArgumentParser.set_defaults方法,通過以下方式

default_options={ 
    "x":10, 
    "y":10 
} 

def f1(**kwargs): 
    x=kwargs.get('x', defalut_options['x']) 
    return x**2 

def f2(**kwargs): 
    y=kwargs.get('y', defalut_options['y']) 
    return x**4 

def f(**kwargs): 
    x=kwargs.get('x', defalut_options['x']) 
    y=kwargs.get('y', defalut_options['y']) 
    return f1(x=x, y=y) 

if __name__=="__main__": 
    import argparse 
    parser = argparse.ArgumentParser(description = "A toy application", formatter_class=argparse.ArgumentDefaultsHelpFormatter) 
    parser.add_argument("--f1-x",help="the parameter passed to f1", 
     type = float,dest = "x") 
    parser.add_argument("--f2-x",help="the parameter passed to f2", 
     type = float, dest = "y") 

    parser.set_defaults(**default_options) 

    options= parser.parse_args() 
    print f(options.x,options.y) 

我花了一段時間,使其工作,因爲我沒有注意到你正在使用destadd_argument(我從來沒有使用它)。如果未提供此關鍵字,argparse將默認值dest設置爲參數的長名稱(在此例中爲f1_xf2_x,因爲它用-替換爲_)。爲了說明問題:如果您想提供默認字典,則需要匹配dest(如果提供)。此外,請注意parser.set_defaults只是爲解析器添加參數,所以如果您的解析器中沒有某些條目,它將被添加到Namespace

--Edited仿製kwargs添加到functions--

+0

我不知道set_default方法,謝謝.. ,但我仍然有定義一個「全球」字典(包含所有功能的默認值,這對我來說似乎是一個不好的做法)..也許使用組有幫助嗎? – lucacerone 2013-03-22 13:32:03

+0

我不認爲這是一個不好的做法,使用字典設置默認值。如果函數的默認值多於'argparse'的默認值,您可以爲後者創建一個字典,並將其複製和/或擴展爲函數。 – 2013-03-22 13:39:55

+0

好的,但我認爲我用這個方法還是有一些缺陷的。在玩具例子中,函數只有一個參數..但是可能會有更多的情況(因爲「核心」函數使用許多功能)。沒有單獨編寫它們(也許忘了一個..),是不是有一種方法可以像f(** options_f1,** options_f2)那樣調用f [希望你有這種感覺]可能使得名稱衝突被避免? – lucacerone 2013-03-22 13:45:41

1

由於@Francesco在評論中寫道,您的默認詞典將無法正常工作,你可能預期:該功能將保留他們在加載默認值該模塊,而不管以後對字典的更改。以下是如何讓他們跟蹤字典的當前值:

_default_options = { 
    "f1_x":10, 
    "f2_x":10 
} 

def f1(x=None): 
    if x == None: 
     x = _default_options["f1_x"] 
    ... 

您可以然後通過ArgumentParser修改_default_options,或以任何其他方式,並f1()會使用它,如果使用任何參數調用。

這要求None永遠不會成爲x的有意義的值;如果情況並非如此,請選擇一個合適的不可能值。

+0

感謝您解釋我的評論。我認爲這個問題是關於在'argparse'中設置默認值的最好方法,而不是在函數參數中。 – 2013-03-22 13:07:35

+0

你可能是對的。無論如何,你很好地覆蓋了這一點。 – alexis 2013-03-22 13:08:54

+0

正如前面的評論中所解釋的,我不打算讓用戶能夠更改默認值。 我沒有想到的是,雖然這不是全局變量,但f1可以訪問_default_options事件嗎? – lucacerone 2013-03-22 13:37:17