經(jīng)常在程序中看到有tf.app.flags
和tf.app.run
字樣的代碼台汇,這兩段代碼究竟是什么作用譬胎,就讓我們從分析源碼的角度來加深理解充甚!
tf.app.flags
tf.app.flags主要用于處理命令行參數(shù)的解析工作券盅,其實可以理解為一個封裝好了的argparse包(argparse是一種結(jié)構(gòu)化的數(shù)據(jù)存儲格式,類似于Json项郊、XML)∠诳郏現(xiàn)在我們就從源碼來分析它究竟是怎么解析命令行參數(shù)的,應(yīng)該怎么使用它着降!
源碼如下:
不出意外的首先導(dǎo)入了argparse
包
使用argparse
的第一步就是創(chuàng)建一個解析器對象差油,告訴它將會有些什么參數(shù)。當程序運行時任洞,該解析器可以用于處理命令行參數(shù)(只能解析參數(shù)蓄喇、獲取參數(shù)、設(shè)置已有參數(shù)的默認值等操作)侈咕。argparse
中的解析器類是ArgumentParser
公罕。
定義了_FlagValues
類,如我們前面所說耀销,要處理命令行參數(shù)楼眷,就要用解析器類_global_parser里的方法來解析,這里使用了parse_known_args()
這個函數(shù)熊尉,其實同parse_args
函數(shù)差不多(注:這里說的parse_args()
函數(shù)和此處_FlagValues
類中定義的_parse_args()
函數(shù)不一樣罐柳,前者也是argparse
中一種解析參數(shù)的函數(shù)),只是這個函數(shù)在接受到多余的命令行參數(shù)時不會報錯狰住,會原封不動的以一個list形式將其返回张吉。所以此函數(shù)返回的”result“是參數(shù)解析完的數(shù)據(jù),而”unparsed“是那些未被解析的參數(shù)list催植。將命令行傳入的命令和數(shù)據(jù)解析出來以字典的形式放到__dict__
的['_flags']
這個字典中肮蛹,這么做也是為了方便我們后續(xù)直接訪問命令行輸入的命令,因為可以直接通過字典調(diào)用(在tensorflow中其實是通過tf.app.flag.Flags
來實現(xiàn)實例化這個類创南,然后再調(diào)用里面解析得到的參數(shù)即可)伦忠。
初始化完了之后,可以看到源碼里是一些setattr/getattr的
方法稿辙,也就是一些設(shè)置和獲得解析的命令行參數(shù)的方法昆码。要注意的是,在獲得參數(shù)的時候(getattr
)邻储,首先要通過解析字典中的’parsed’來檢驗參數(shù)是否已經(jīng)被解析過赋咽,因為在_parse_flags方法中,只要解析過參數(shù)(也即是運行過該函數(shù))吨娜,那么self.__dict__[‘__parsed’]
就會為True(表明解析過參數(shù))脓匿。因為這里是獲取參數(shù),所以出了要判斷參數(shù)是否在字典里的基本要求外萌壳,還要判斷有沒有解析過參數(shù)亦镶,沒有就運行_parse_flags
解析參數(shù)日月。其它就比較簡單,這里就不介紹了缤骨!
后面將上述整個_FlagValues
類的實例化阀溶,這樣就方便了我們的訪問操作妓羊。因為我們要訪問命令行輸入的命令時芯丧,就可以直接從這個實例里操作蝴簇。
注意從這里開始都是在類外定義的方法,所以要調(diào)用就只能通過tf.app.flags.XXX來實現(xiàn)了虱歪。
下面的_define_helper
函數(shù)中調(diào)用了_global_parser.add_argument
完成對命令行參數(shù)的添加(傳入flag_name蜂绎,default_value,docstring笋鄙,flagtype參數(shù))师枣,可以看到添加參數(shù)使用的是解析器類_global_parser
的方法。仔細看這個函數(shù)的參數(shù)萧落,第一個參數(shù)是‘--’+flag_name這個表示我們定義的命令行參數(shù)使用時必須以‘--’開頭践美,比如--flag_int9
(具體看后面例子),而第二個參數(shù)default_value
是參數(shù)的默認值找岖,第三個參數(shù)docstring
保存幫助信息(命令行中輸入 -h激活該參數(shù))陨倡,第四個參數(shù)表示限定了賦予命令行參數(shù)數(shù)據(jù)的類型。
上面我們已經(jīng)看到了使用_define_helper
參數(shù)即可以添加命令行參數(shù)许布,這里源碼中又將其封裝為針對string/int/float/bool類型參數(shù)的特定添加方法**兴革。
看DEFINE_string()
,這里則由于_define_helper()
最后一個type參數(shù)是str蜜唾,上面我們關(guān)于_define_helper
參數(shù)的解釋杂曲,說明DEFINE_string()
限定了可選參數(shù)輸入必須是string,這也就是為什么這個函數(shù)定義為DEFINE_string()
袁余,同理解阅,DEFINE_interger()
限定可選參數(shù)必須是int,DEFINE_float()
限定可選參數(shù)必須是float泌霍,DEFINE_boolean()
限定可選參數(shù)必須是bool。
源碼中最后介紹的方法是在程序運行前先將某些命令行參數(shù)加入到”必備參數(shù)“(__required_flags
)的字典中述召,以判斷解析完的參數(shù)是否滿足這些必備要求朱转!因為mark_flags_as_required
方法會調(diào)用mark_flag_as_required
方法,來將當前傳入的參數(shù)加入到__required_flags
字典中(_add_required_flag
方法)积暖,在最上面解析參數(shù)的方法_parse_flags
中藤为,解析完參數(shù)會通過_assert_all_required
方法判斷解析到的參數(shù)是否都在_required_flags
字典中。
講了這么多夺刑,具體在tensorflow中我們該怎么使用呢缅疟?
首先我們通過tf.app.flags
來調(diào)用這個flags.py
文件分别,這樣我們就可以用flags.DEFINE_interger/float()
來添加命令行參數(shù),而FLAGS=flags.FLAGS
可以實例化這個解析參數(shù)的類從對應(yīng)的命令行參數(shù)取出參數(shù)存淫。
新建test.py文件耘斩,并輸入如下代碼,代碼的功能是創(chuàng)建幾個命令行參數(shù)桅咆,然后把命令行參數(shù)輸出顯示
import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_string('data_dir', '/tmp/mnist', 'Directory with the MNIST data.')
flags.DEFINE_integer('batch_size', 5, 'Batch size.')
flags.DEFINE_integer('num_evals', 1000, 'Number of batches to evaluate.')
FLAGS = flags.FLAGS
print(FLAGS.data_dir, FLAGS.batch_size, FLAGS.num_evals)
- 在命令行中輸入
test.py -h
就可以查看幫助信息括授,也就是Directory with the MNIST data.
,Batch size
和Number of batches to evaluate
這樣的消息岩饼。 - 在命令行中輸入
test.py --batchsize 10
就可以將batch_size的值修改為10荚虚!
tf.app.run()
該函數(shù)一般都是出現(xiàn)在這種代碼中:
if __name__ == '__main__':
tf.app.run()
上述第一行代碼表示如果當前是從其它模塊調(diào)用的該模塊程序,則不會運行main函數(shù)籍茧!而如果就是直接運行的該模塊程序版述,則會運行main函數(shù)。
具體第二行的功能從源碼開始分析寞冯,源碼如下:
flags_passthrough=f._parse_flags(args=args)
這里的parse_flags
就是我們tf.app.flags
源碼中用來解析命令行參數(shù)的函數(shù)渴析。所以這一行就是解析參數(shù)的功能;
下面兩行代碼也就是tf.app.run
的核心意思:執(zhí)行程序中main函數(shù)简十,并解析命令行參數(shù)檬某!