1. 程式人生 > >淺談使用xml作為配置檔案初始化自己的專案

淺談使用xml作為配置檔案初始化自己的專案

當一個專案的程式碼不斷增加,其中很多的內容諸如全域性變數、提示語言等等都有必要放在一個獨立的檔案,方便變更。這個獨立的檔案有很多種,可以是init檔案、conf檔案、xml檔案,為了通用性,我選擇了xml檔案作為自己的配置檔案。對於《字元級的CNN文字分類器》一文中,我的xml檔案是這樣的:

<?xml version="1.0" encoding="UTF-8" ?>
<config>
    <directories>
        <directory name="train_text_home">TrainText</directory>
<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> <module
name="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是一種非常開放的語言,所有的標籤、屬性命名都沒有相關的規定,什麼時候使用子標籤,什麼時候使用屬性全憑作者的習慣,這裡我給出一種判斷的標準:對於資料的儲存,儘量使用子標籤,例如上述中hyperparameter(超參)屬於我要存的資料,所以我用標籤來存。但name和type屬於這個資料的一些屬性,我使用屬性去儲存。但其實這樣的分類法有時候並不會很清晰。例如電影,一部電影有標題、簡介、演員等內容,這些屬於這部電影的屬性,但從另外一個角度來說,這些又是屬於我們要存起來的資料。所以有另外一種標準,我們可以把需要直接使用的內容看成資料,例如這個超參直接賦給程式,電影的標題、簡介和演員直接輸出到前端頁面,而name和type屬於對這些資料的描述,我們不會直接使用它們,所以作為屬性。在上述檔案中,大家還可以看到我在部分內容裡面使用了格式字串“%s%d%f”等,關於這點的意義之一,我在另一篇文章中提到,就是為了可以儲存整條文字,方便語言包的製作。具體怎樣指定相關的變數,後面會介紹。

有了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

SyntaxMeaning
tagSelects 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 findwas 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等配置檔案對某些服務應用的引數進行配置,這些包含各層級結構的配置檔案,大致可以看作樹狀結構,其解析和拼裝並不是一項簡單的事情。 在本專案中,開發人員或者業務人員提供了這些配置檔案之後,需要解析出相應的配置項以