淺談使用xml作為配置檔案初始化自己的專案
當一個專案的程式碼不斷增加,其中很多的內容諸如全域性變數、提示語言等等都有必要放在一個獨立的檔案,方便變更。這個獨立的檔案有很多種,可以是init檔案、conf檔案、xml檔案,為了通用性,我選擇了xml檔案作為自己的配置檔案。對於《字元級的CNN文字分類器》一文中,我的xml檔案是這樣的:
<?xml version="1.0" encoding="UTF-8" ?> <config> <directories> <directory name="train_text_home">TrainText</directory>xml是一種非常開放的語言,所有的標籤、屬性命名都沒有相關的規定,什麼時候使用子標籤,什麼時候使用屬性全憑作者的習慣,這裡我給出一種判斷的標準:對於資料的儲存,儘量使用子標籤,例如上述中hyperparameter(超參)屬於我要存的資料,所以我用標籤來存。但name和type屬於這個資料的一些屬性,我使用屬性去儲存。但其實這樣的分類法有時候並不會很清晰。例如電影,一部電影有標題、簡介、演員等內容,這些屬於這部電影的屬性,但從另外一個角度來說,這些又是屬於我們要存起來的資料。所以有另外一種標準,我們可以把需要直接使用的內容看成資料,例如這個超參直接賦給程式,電影的標題、簡介和演員直接輸出到前端頁面,而name和type屬於對這些資料的描述,我們不會直接使用它們,所以作為屬性。在上述檔案中,大家還可以看到我在部分內容裡面使用了格式字串“%s%d%f”等,關於這點的意義之一,我在另一篇文章中提到,就是為了可以儲存整條文字,方便語言包的製作。具體怎樣指定相關的變數,後面會介紹。<directory name="test_text_home">TestText</directory> <directory name="checkpoint_home">CheckPoints</directory> <directory name="summary_home">Summaries</directory> <directory name="log_home">Logs</directory> </directories><files> <file name="model_file">ENCharCNNTextClassification_%s</file> <file name="summary_file">ENCharCNNTextClassification_%s</file> <file name="log_file">Logs_of_%s.log</file> </files> <messages> <modulename="pre_train.py"> <message name="encode_success">One-Hot encoding done! Totally %d words have been skipped.</message> <message name="start_train_file">Start to train from file: %s.</message> <message name="done_train_file">Finish to train from file: %s.</message> <message name="open_dir">Open directory: %s.</message> </module> <module name="char_cnn.py"> <message name="checkpoint_restore">Checkpoint: %s has been restored.</message> <message name="checkpoint_restore_fail">No checkpoints being restored.</message> <message name="display_steps">Total steps: %d, batch cost: %.4f, batch accuracy: %.2f%%, time to use: %d seconds.</message> <message name="display_test">Accuracy: %.2f%%.</message> </module> <module name="main.py"> <message name="done_train">The train has been done!</message> <message name="done_validation">The validation has been done!</message> </module> </messages> <options> <option name="train_name" type="str">ag_news</option> <option name="max_to_keep" type="int">10</option> <option name="epochs" type="int">55</option> <option name="display_steps" type="int">2000</option> <option name="save_steps" type="int">2000</option> </options> <hyperparameters> <hyperparameter name="length0" type="int">1014</hyperparameter> <hyperparameter name="n_class" type="int">4</hyperparameter> <hyperparameter name="batch_size" type="int">128</hyperparameter> <hyperparameter name="learning_rate" type="float">0.01</hyperparameter> <hyperparameter name="decay_steps" type="int">1000</hyperparameter> <hyperparameter name="decay_rate" type="float">0.8</hyperparameter> <hyperparameter name="keep_prob" type="float">0.5</hyperparameter> <hyperparameter name="grad_clip" type="int">5</hyperparameter> </hyperparameters> </config>
有了xml檔案,就需要有程式碼去讀,同樣為了全域性性考慮,我專門寫了一個類來讀取配置檔案,做我程式的初始化工作:
import os import logging import xml.etree.ElementTree class ProjectInitializer: config = None work_dir = os.getcwd() @classmethod def init_my_project(cls, config_file='config.xml'): assert os.path.isfile(config_file) assert (os.path.splitext(config_file)[-1]) == '.xml' cls.config = xml.etree.ElementTree.parse(config_file).getroot() cls._check_dirs() cls._init_logging() @classmethod def get_dir_path(cls, home_name): path = cls.config.find("./directories/directory[@name=\'%s\']" % home_name).text if os.path.isabs(path): return path else: return os.path.join(cls.work_dir, path) @classmethod def get_file_path(cls, home_name, file_name, *args): real_file_name = cls.config.find("./files/file[@name=\'%s\']" % file_name).text if len(args) > 0: real_file_name = real_file_name % args return os.path.join(cls.get_dir_path(home_name), real_file_name) @classmethod def message_about(cls, module_file_path, event_name, *args): module_name = os.path.basename(module_file_path) mess_module = cls.config.find("./messages/module[@name=\'%s\']" % module_name) assert mess_module is not None message = mess_module.find("./message[@name=\'%s\']" % event_name).text if len(args) > 0: message = message % args return message @classmethod def option(cls, opt_name): tag = cls.config.find("./options/option[@name=\'%s\']" % opt_name) value = eval(tag.get('type'))(tag.text) return value @classmethod def hyper_para(cls, para_name): tag = cls.config.find("./hyperparameters/hyperparameter[@name=\'%s\']" % para_name) value = eval(tag.get('type'))(tag.text) return value @classmethod def _check_dirs(cls): for directory in cls.config.findall("./directories/directory"): path = cls.get_dir_path(directory.get('name')) if not os.path.isdir(path): os.mkdir(path) @classmethod def _init_logging(cls): formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s - %(message)s', '%Y %b %d %H:%M:%S') handlers = [ logging.FileHandler(cls.get_file_path('log_home', 'log_file', cls.option('train_name'))), logging.StreamHandler() ] logging.getLogger().setLevel(logging.INFO) for handler in handlers: handler.setFormatter(formatter) logging.getLogger().addHandler(handler)且看我逐行解析:
class ProjectInitializer: config = None work_dir = os.getcwd()類變數config是儲存xml解析樹的,在import類的時候,因為配置檔案的路徑不確定,所以先初始化為None,而work_dir則是要獲取當前的工作目錄作為全域性變數,這樣的好處是,無論後面我們是否改變了工作目錄,我們配置檔案指定的路徑都可以跟這個路徑組合成絕對路徑,固定儲存的位置。
xml獲取相關標籤的公式如下:
20.5.2.2. Supported XPath syntax
Syntax | Meaning |
---|---|
tag | Selects all child elements with the given tag. For example, spam selects all child elements named spam , and spam/egg selects all grandchildren named egg in all children named spam . |
* | Selects all child elements. For example, */egg selects all grandchildren named egg . |
. | Selects the current node. This is mostly useful at the beginning of the path, to indicate that it’s a relative path. |
// | Selects all subelements, on all levels beneath the current element. For example, .//egg selects all egg elements in the entire tree. |
.. | Selects the parent element. Returns None if the path attempts to reach the ancestors of the start element (the element find was called on). |
[@attrib] | Selects all elements that have the given attribute. |
[@attrib='value'] | Selects all elements for which the given attribute has the given value. The value cannot contain quotes. |
[tag] | Selects all elements that have a child named tag . Only immediate children are supported. |
[tag='text'] | Selects all elements that have a child named tag whose complete text content, including descendants, equals the given text . |
[position] | Selects all elements that are located at the given position. The position can be either an integer (1 is the first position), the expression last() (for the last position), or a position relative to the last position (e.g. last()-1 ). |
Predicates (expressions within square brackets) must be preceded by a tag name, an asterisk, or another predicate. position
predicates must be preceded by a tag name.
@classmethod def init_my_project(cls, config_file='config.xml'): assert os.path.isfile(config_file) assert (os.path.splitext(config_file)[-1]) == '.xml' cls.config = xml.etree.ElementTree.parse(config_file).getroot() cls._check_dirs() cls._init_logging()第一個靜態方法是用來初始化專案的,會在main函式的第一句被呼叫。從程式碼我們可以清楚看到,主要工作就是讀取指定的xml檔案,檢查需要的目錄是否存在,以及初始化logging的相關資訊。
@classmethod def get_dir_path(cls, home_name): path = cls.config.find("./directories/directory[@name=\'%s\']" % home_name).text if os.path.isabs(path): return path else: return os.path.join(cls.work_dir, path) @classmethod def get_file_path(cls, home_name, file_name, *args): real_file_name = cls.config.find("./files/file[@name=\'%s\']" % file_name).text if len(args) > 0: real_file_name = real_file_name % args return os.path.join(cls.get_dir_path(home_name), real_file_name)這兩個方法是用來獲取相關的目錄和檔案路徑的,為了便於在win和Linux之間遷移,我配置檔案一般不會存路徑分割符,而像程式碼中的使用os.path中的join方法來連線,這樣就保證了可遷移性。同時在第二個方法中我們可以看到,函式使用了Python的一個可變引數的特性*args,這個特性允許使用著動態輸入若干個引數,例如引數檔案中的log檔案我們會允許輸入一個字串來區分不同的日誌,這樣呼叫者獲取log檔案路徑的時候,只需要把這個字串按順序放在函式裡面,程式碼便會替換相應的內容,生成真正的檔名,再組合目錄的路徑成為絕對路徑返回。由於這個引數的個數是可變的,也滿足了字串中替代符個數不定的需要。
@classmethod def message_about(cls, module_file_path, event_name, *args): module_name = os.path.basename(module_file_path) mess_module = cls.config.find("./messages/module[@name=\'%s\']" % module_name) assert mess_module is not None message = mess_module.find("./message[@name=\'%s\']" % event_name).text if len(args) > 0: message = message % args return message這個方法是用來獲取提示資訊的,由於提示資訊非常多,而且一般每個模組有固定的提示資訊,很少是各個模組共用的,所以在提示資訊儲存的xml裡面,我增加了一個父標籤<module>,標籤的內容是這個檔案的檔名。然後在呼叫函式的時候我們有一個取巧的方法:
(MyInit.message_about(__file__, 'done_train')__file__這個變數儲存了當前模組的檔名,這樣在不同的模組裡面,我們便可以統一使用這樣的程式碼來進行呼叫,減少了錯誤的發生:
MyInit.message_about(__file__, 'start_train_file', file_path)上面是帶引數的呼叫。
@classmethod def option(cls, opt_name): tag = cls.config.find("./options/option[@name=\'%s\']" % opt_name) value = eval(tag.get('type'))(tag.text) return value @classmethod def hyper_para(cls, para_name): tag = cls.config.find("./hyperparameters/hyperparameter[@name=\'%s\']" % para_name) value = eval(tag.get('type'))(tag.text) return value這兩個都是獲取全域性變數的方法,其中option和hyperparameter並沒有太嚴格的區分,我憑我個人感覺認為後者對神經網路的定義相關性更大。方法中值得一提的是,tag.text存的都是str變數,這樣我們在使用的時候可能會面臨變數型別的報錯,所以我特意增加了一個type屬性,然後使用Python的特殊方法eval執行,例如是int的變數,eval('int)('4')的效果就等同於int('4'),這樣返回的全域性變數變可以通過xml檔案動態設定屬性了。
@classmethod def _check_dirs(cls): for directory in cls.config.findall("./directories/directory"): path = cls.get_dir_path(directory.get('name')) if not os.path.isdir(path): os.mkdir(path)該方法是檢查相關的目錄是否存在,沒有就建立,程式碼比較簡單。
@classmethod def _init_logging(cls): formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s - %(message)s', '%Y %b %d %H:%M:%S') handlers = [ logging.FileHandler(cls.get_file_path('log_home', 'log_file', cls.option('train_name'))), logging.StreamHandler() ] logging.getLogger().setLevel(logging.INFO) for handler in handlers: handler.setFormatter(formatter) logging.getLogger().addHandler(handler)最後一個方法是logging的初始化,由於本文主要講的是xml讀取,這部分程式碼只是簡單介紹一下。formatter是用來定義格式的,handlers裡面存了需要輸出的地方,本程式碼裡面是logfile和螢幕,把它設定到getLogger()獲取的root logger裡面,就可以對所有的log執行了。
上述類就是用來初始化我的程式碼的,我做人工智慧的程式設計基本上都是照搬這兩個檔案,然後修改一下xml配置檔案的內容,非常方便。
相關推薦
淺談使用xml作為配置檔案初始化自己的專案
當一個專案的程式碼不斷增加,其中很多的內容諸如全域性變數、提示語言等等都有必要放在一個獨立的檔案,方便變更。這個獨立的檔案有很多種,可以是init檔案、conf檔案、xml檔案,為了通用性,我選擇了xml檔案作為自己的配置檔案。對於《字元級的CNN文字分類器》一文中,我的xm
淺談Java類載入的初始化階段
類載入的初始化階段對類變數賦予正確的值。主要有兩種初始化方式,一種是通過類變數初始化語句;一種是靜態初始化語句。如下述程式碼所示,前者是類變數初始化語句,後者是靜態初始化語句。 public class Example1 { static int
[linux]mac profile配置檔案初始化與設定
mac profile配置檔案初始化與設定 unix系列系統中profile配置檔案的知識貼士: (一)全域性設定 下面的幾個檔案設定是全域性的,修改時需要root許可權 1)/etc/paths (全域性建議修改這個檔案 ) 編輯 paths,將環境變數新增到
淺談ASP.NET配置檔案加密
在剛剛完成的一個ASP.NET專案中,遇到了這麼一個問題,專案部署到生產環境中時,領導要求專案中的配置檔案(如web.config,app.config)中不能出現敏感字元,如:資料庫連線,等等。第一個想到的方法是,寫一個加密解密演算法,將這些配置檔案中的值以密文的方式存到c
在過濾器中獲取在web.xml配置的初始化引數
在過濾器中獲取在web.xml配置的初始化引數 例如 <filter> <filter-name>cross-origin</filter-n
springmvc中配置servlet初始化類
調用 靜態類 its tomcat -m class cat 讀取 gmv <bean id="InitStart" lazy-init="false" init-method="InitSystem" class="my.spring.uitl.InitStart
hibernate_基本配置和初始化步驟
導入jar包 導入 eclips ips conf 數據 hiberna 持久化類 步驟 1.hibernate使用步驟: 1)創建hibernate配置文件 2)創建持久化類 3)創建對象-關系映射 4)通過hibernate api編寫訪問數據庫的代碼 2.ecli
淺談ASP.NET配置文件加密
info provider 管理員 cti 打開 默認 命令 nec spa 在剛剛完成的一個ASP.NET項目中,遇到了這麽一個問題,項目部署到生產環境中時,領導要求項目中的配置文件(如web.config,app.config)中不能出現敏感字符,如:數據庫連接,等等。
一:Greenplum5.10.2 生產環境安裝配置 (系統初始化、安裝準備)
添加 dead 有關 zlib 直接 dconf tables .rpm grub.conf 服務簡介: Greenplum Master Master只存儲系統元數據,業務數據全部分布在Segments上。其作為整個數據庫系統的入口,負責建立與客戶端的連接,SQL的解析並
007-spring cloud gateway-GatewayAutoConfiguration核心配置-RouteDefinition初始化加載
hand 配置信息 包裝類 eid override repo shm 配置路由 本地緩存 一、RouteDefinitionLocator 在Spring-Cloud-Gateway的GatewayAutoConfiguration初始化加載中會加載RouteDef
struts2 xml(核心配置檔案)
struts.xml (核心配置檔案) <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts C
淺談MySQL主從配置
圖片 info 數據訪問層 watermark oot 負載 creat mar 並發 大型網站為了緩解大量的並發訪問,除了在網站實現分布式負載均衡,遠遠不夠。到了數據業務層、數據訪問層,如果還是傳統的數據結構,或者只是單單靠一臺服務器扛,如此多的數據庫連接操作,數據庫必然
Spring Data JPA applicationContext.xml的配置檔案
Spring Data JPA的概述 概述:是Dao層的解決方案,出現的目的是簡化JPA的開發。 想使用JPA的話,需要新增座標依賴,專案中已經引入了。 <dependency> <groupId>org.springframework.
淺談webuploader上傳檔案
官網:http://fex.baidu.com/webuploader/getting-started.html 案例: 檔案上傳進度 // 檔案上傳過程中建立進度條實時顯示。 uploader.on( 'uploadProgress', function( file,
淺談XML實體注入漏洞
本文作者:[email protected],本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載 學習了XXE漏洞,自己來總結一下,如果有什麼寫的不到位,不夠好的地方,還請師傅們指出。 0×00 XXE漏洞 XXE漏洞全稱XML External Entity
jrebel可以作用於xml等配置檔案嗎
之前看網上說idea不生效,eclipse可以生效。我自己實踐發現也不能生效。 不知道是不是換了新的jrebel的願因,剛才mybatis的xml檔案有的錯誤,進行了修改,熱部署載入了一下,發現xml檔案生效了。 顯示如下:2018-12-12 21:36:35 JRebel: Reload
三叔學FPGA系列之二:Cyclone V中的POR、配置、初始化,以及復位
對於FPGA內部的復位,之前一直比較迷,這兩天仔細研究官方資料手冊,解開了心中的諸多疑惑,感覺自己又進步了呢..... 一、關於POR(Power-On Reset ) FPGA在上電工作時,會先進入復位模式,將所有RAM位清除,並通過內部弱上拉電阻將使用者I/O置為三態。接著依次完成 配置、初始化工
9.淺談.dll .lib .def檔案格式究竟是什麼
我最近在github上下載了一個專案,需要配到fftw的第三方庫,可是看到一堆dll檔案,lib檔案,def檔案,頭都暈了,不知道這些東西是什麼,怎麼用,下面就我查詢的資料做一個小結。 據說,出現.lib .dll 這種檔案的原因是為了保護原始碼,這個以後機會我
maven打包漏掉mapper.xml和配置檔案
<resources> <resource> <directory>src/main/resources</directory> <includes>
ini、xml格式配置檔案的解析與拼裝
1.背景 在開發的過程中,我們通常會使用ini、xml、json等配置檔案對某些服務應用的引數進行配置,這些包含各層級結構的配置檔案,大致可以看作樹狀結構,其解析和拼裝並不是一項簡單的事情。 在本專案中,開發人員或者業務人員提供了這些配置檔案之後,需要解析出相應的配置項以