今天在寫代碼的時候遇到了一個關於parser的一些小坑,記錄在此備用。
我們知道在python中可以用argprase來傳遞一些參數給代碼執行,來看下面的例子,假設現在有一個test文件夾,下面有3個python文件,分別用a.py;b.py;c.py來表示,目錄樹如下。
每一個的初始代碼為一個簡單的print函數。
1 #a.py 2 def out_a(): 3 print("I am a.py") 4 5 6 if __name__ == '__main__': 7 out_a()
1 #b.py 2 def out_b(): 3 print("I am b.py") 4 5 6 if __name__ == '__main__': 7 out_b()
1 #c.py 2 def out_c(): 3 print("I am c.py") 4 5 6 if __name__ == '__main__': 7 out_c()
現在在a.py中引入模塊argprase,並定義一些簡單的參數,代碼如下
1 import argparse 2 parser = argparse.ArgumentParser() 3 parser.add_argument('--first_parameter', default='first') 4 parser.add_argument('--second_parameter', default='second') 5 parser.add_argument('--third_flag', action='store_true') 6 args = parser.parse_args() 7 8 9 def out_a(): 10 print("I am a.py") 11 12 13 if __name__ == '__main__': 14 out_a()
這里面簡單說一下第3個參數,這也是我今天想記錄文章的原因,這個參數是argparse里面提供的開關布爾選項,actions記錄的是一個動作,意味着在調用這個函數的時候,如果在命令行添加這個參數,則該參數為True,如果不添加這個參數,則該參數為False,歸納起來為如下的兩個圖。
這個是沒有指定第3個參數的情況
這個是指定第3個參數的情況
對於這種開關布爾選項更為詳細的介紹,可以參考知乎問題:Argparse中action的可選參數store_true,store_false到底是什么意思?
到目前為止沒有出現問題,接下來,我希望b.py也使用參數,並且還希望使用a.py里面的函數,因此我對b.py進行如下修改。
1 from a import out_a 2 import argparse 3 parser = argparse.ArgumentParser() 4 parser.add_argument('--fourth_parameter', default='fourth') 5 parser.add_argument('--fifth_parameter', default='fifth') 6 parser.add_argument('--sixth_flag', action='store_true') 7 args = parser.parse_args() 8 9 10 def out_b(): 11 print("I am b.py") 12 13 14 if __name__ == '__main__': 15 out_b() 16 out_a()
然后同樣的,我們分別用兩種方式來測試b.py,效果如下。
這個是不使用參數的情況
這個是使用參數的情況
可以看到報錯了,當時我看到這里的時候想了很久,排除了拼寫錯誤的情況以后,觀察這里面的輸出,發現看到的是a.py當中的3個參數,而不是b.py當中設置的參數,於是我將a.py和b.py的參數表打印出來,看到這樣子的結果。
輸出兩個python文件的參數表
可以發現盡管我使用的是 from a import out_a ,但依然引入了a.py當中的參數表,並且后引入的b.py的參數表沒有辦法覆蓋掉。下面在c.py中同樣引入3個參數,然后引入b.py的方法,代碼如下:
1 from b import out_b 2 import argparse 3 parser = argparse.ArgumentParser() 4 parser.add_argument('--seventh_parameter', default='seventh') 5 parser.add_argument('--eighth_parameter', default='eighth') 6 parser.add_argument('--ninth_flag', action='store_true') 7 args = parser.parse_args() 8 print(args) 9 10 def out_c(): 11 print("I am c.py") 12 13 14 if __name__ == '__main__': 15 out_c() 16 out_b()
效果如下
不使用任何參數調用c.py
看到有3個參數列表輸出就知道c.py的參數也是無效的了,驗證一下。
使用參數調用c.py
解決方案:
其實只需要將所有的參數表放到同一個文件里面就可以了,比如utils.py,由於這里是同一個文件夾下的3個文件,在import調用的時候就只需要初始化一次所有參數就可以使用了,有點類似於C語言當中的全局變量,因為這個東西排查了一個下午,也是有點惱火了。