Skip to main content

一个库,供用户以 Python Dict 或 JSON 格式编写(研究中的实验)配置,同时可以从命令行读取参数

项目描述

请加星 请加星

如果你觉得这个工具不错,请点击按钮漂亮的明星点击此项目曝光度!谢谢

如果您觉得这个工具不错,请在本页面右上角轻点Star按钮,增加项目曝光度,谢谢!

中文文档

点此查看中文文档

易于使用的命令行配置工具

一个库,供用户以 Python Dict 或 JSON 格式编写(研究中的实验)配置,同时可以从命令行读取参数以修改值。

Python、命令行、命令行、配置、参数、配置标签、标签配置、配置、传参、参数修改。

Github 网址:https ://github.com/NaiboWang/CommandlineConfig

简单示例

# Install via pip
pip install commandline_config

# import package
from commandline_config import Config

# Define configuration dictionary
config = {
  "index":1,
  "lr": 0.1,
  "dbinfo":{
    "username": "NUS"
  }
}

# Generate configuration class based on configuration dict
c = Config(config)

# Print the configuration of the parameters
print(c)

# Read and write parameters directly via dot . and support multiple layers.
c.index = 2
c.dbinfo.username = "ZJU"
print(c.index, c.dbinfo.username, c["lr"])

# On the command line, modify the parameter values with --
python example.py --index 3 --dbinfo.username XDU

# Get the parameter descriptions via the help method in the code, or on the command line via -h or -help (customization required, see detailed documentation below for details)
c.help()

python example.py -h

目录

用法

请提交问题

如果您在使用本工具的过程中遇到任何问题,请在本项目的github页面提出问题,我会第一时间解决遇到的bug和问题。

同时,欢迎提交问题,提出您想在此工具中添加哪些功能,我会尽可能实现。

安装

有两种方法可以安装这个库:

    1. 通过 pip 安装:
      pip install commandline_config
    

    如果已经安装,可以通过以下命令升级:

      pip install commandline_config --upgrade
    
    1. 直接从github项目的文件夹中导入commandline_config.py文件/commandline_config到自己的项目目录下,需要安装依赖包prettytable
    pip install prettytable
    

配置方式

    1. 导入库:
    from commandline_config import Config
    
    1. 以 JSON/Python Dict 格式设置参数名称和初始值,并通过#注释添加参数说明。目前支持将一个 dict 嵌套在另一个 dict 中,并且可以嵌套无限层
    preset_config = {
          "index": 1,  # Index of party
          "dataset": "mnist",
          'lr': 0.01,  # learning rate
          'normalization': True,
          "pair": (1,2),
          "multi_information": [1, 0.5, 'test', "TEST"],  # list
          "dbinfo": {
              "username": "NUS",
              "password": 123456,
              "retry_interval_time": 5.5,
              "save_password": False,
              "pair": ("test",3),
              "multi":{
                  "test":0.01,
              },
              "certificate_info": ["1", 2, [3.5]],
          }
      }
    

    即生成程序的初始配置。dict中定义的每个keypreset_config都是参数名,每个value都是参数的初始值,同时根据设置值的类型自动检测参数的初始值类型。

    上面的配置包含七个参数:index, dataset, batch, normalization, pair, multi_information and dbinfo,其中参数索引的类型自动检测为int,默认值为1,描述为“当事人的索引”。

    同理,第二至第五个参数的类型和默认值为 string: "mnist"; float:0.01; bool:True; tuple:(1,2); list:[1,0.5,'test', "TEST"]

    第七个参数是一个dict类型的嵌套字典,其中也包含7个参数,与前7个参数的类型和默认值相同,这里不再赘述。

    1. preset_config通过将dict传递给Config您想要的任何函数来创建配置类对象。
    if __name__ == '__main__':
        config = Config(preset_config)
        # Or give the configuration a name:
        config_with_name = Config(preset_config, name="Federated Learning Experiments")
    
        # Or you can store the preset_config in local file configuration.json and pass the filename to the Config class.
        config_from_file = Config("configuration.json")
    

    这意味着配置对象已成功生成。

    1. print参数配置可以通过函数直接打印:
      print(config_with_name)
    

    输出结果为:

    Configurations of Federated Learning Experiments:
    +-------------------+-------+--------------------------+
    |        Key        |  Type | Value                    |
    +-------------------+-------+--------------------------+
    |       index       |  int  | 1                        |
    |      dataset      |  str  | mnist                    |
    |         lr        | float | 0.01                     |
    |   normalization   |  bool | True                     |
    |        pair       | tuple | (1, 2)                   |
    | multi_information |  list | [1, 0.5, 'test', 'TEST'] |
    |       dbinfo      |  dict | See sub table below      |
    +-------------------+-------+--------------------------+
    
    Configurations of dict dbinfo:
    +---------------------+-------+---------------------+
    |         Key         |  Type | Value               |
    +---------------------+-------+---------------------+
    |       username      |  str  | NUS                 |
    |       password      |  int  | 123456              |
    | retry_interval_time | float | 5.5                 |
    |    save_password    |  bool | False               |
    |         pair        | tuple | ('test', 3)         |
    |        multi        |  dict | See sub table below |
    |   certificate_info  |  list | ['1', 2, [3.5]]     |
    +---------------------+-------+---------------------+
    
    Configurations of dict multi:
    +------+-------+-------+
    | Key  |  Type | Value |
    +------+-------+-------+
    | test | float | 0.01  |
    +------+-------+-------+
    

    这里所有参数的信息都会以表格的形式打印出来。如果要更改打印样式,可以通过 修改config_with_name.set_print_style(style='')。可以取值为styleboth, tablejson表示同时打印table和json,只打印table,只打印json字典。

    例如:

      # 只打印 json 
      config_with_name set_print_style ( 'json' ) 
      print ( config_with_name ) 
      print ( "----------" ) 
      # 同时打印表和json 
      config_with_name . set_print_style ( 'table' )
      print(config_with_name)
    

    输出结果为:

    Configurations of Federated Learning Experiments:
    {'index': 1, 'dataset': 'mnist', 'lr': 0.01, 'normalization': True, 'pair': (1, 2), 'multi_information': [1, 0.5, 'test', 'TEST'], 'dbinfo': 'See below'}
    
    Configurations of dict dbinfo:
    {'username': 'NUS', 'password': 123456, 'retry_interval_time': 5.5, 'save_password': False, 'pair': ('test', 3), 'multi': 'See below', 'certificate_info': ['1', 2, [3.5]]}
    
    Configurations of dict multi:
    {'test': 0.01}
    
    ----------
      
    Configurations of Federated Learning Experiments:
    +-------------------+-------+--------------------------+
    |        Key        |  Type | Value                    |
    +-------------------+-------+--------------------------+
    |       index       |  int  | 1                        |
    |      dataset      |  str  | mnist                    |
    |         lr        | float | 0.01                     |
    |   normalization   |  bool | True                     |
    |        pair       | tuple | (1, 2)                   |
    | multi_information |  list | [1, 0.5, 'test', 'TEST'] |
    |       dbinfo      |  dict | See sub table below      |
    +-------------------+-------+--------------------------+
    {'index': 1, 'dataset': 'mnist', 'lr': 0.01, 'normalization': True, 'pair': (1, 2), 'multi_information': [1, 0.5, 'test', 'TEST'], 'dbinfo': 'See below'}
    
    Configurations of dict dbinfo:
    +---------------------+-------+---------------------+
    |         Key         |  Type | Value               |
    +---------------------+-------+---------------------+
    |       username      |  str  | NUS                 |
    |       password      |  int  | 123456              |
    | retry_interval_time | float | 5.5                 |
    |    save_password    |  bool | False               |
    |         pair        | tuple | ('test', 3)         |
    |        multi        |  dict | See sub table below |
    |   certificate_info  |  list | ['1', 2, [3.5]]     |
    +---------------------+-------+---------------------+
    {'username': 'NUS', 'password': 123456, 'retry_interval_time': 5.5, 'save_password': False, 'pair': ('test', 3), 'multi': 'See below', 'certificate_info': ['1', 2, [3.5]]}
    
    Configurations of dict multi:
    +------+-------+-------+
    | Key  |  Type | Value |
    +------+-------+-------+
    | test | float | 0.01  |
    +------+-------+-------+
    {'test': 0.01}
    

配置参数读写方式

写法

配置参数值可以用三种方式写入。

    1. 要接收命令行参数,只需传递--index 1命令行来修改 to 的index1。此外,将值传递给不同类型的参数的注意事项是:
    • 传递 bool 类型时,可以使用0orFalseFalse1Trueorno value after the parameterTrue--normalization 1或all--normalization True可以将配置中--normalization的参数值设置为Truenormalization
    • 传递列表类型时,可以传递空数组和多维数组。
    • 修改嵌套dict中的值,请使用--nested-parameter-name.sub-parameter-name.sub-parameter-name.….sub-parameter-name value修改嵌套对象中的值,例如将子对象中的参数--dbinfo.password 987654值改为; 将子对象中的dict中的参数值更改为```。目前这个工具可以支持无限层/级别的嵌套。passworddbinfo987654--dbinfo.multi.test 1testmultidbinfo
    • 请注意,参数索引必须在preset_config上面定义的对象中:
      python test.py --dbinfo.password 987654 --dbinfo.multi.test 1 --index 0 --dataset emnist --normalization 0 --multi_information [\'sdf\',1,\"3.3\",,True,[1,[]]] 
    
    1. 直接在代码中使用config.index = 2,将参数的值index改为2. 同样,列表类型参数可以分配为空数组或多维数组。对于嵌套对象,您可以使用将sub dictconfig.dbinfo.save_password=True中的参数值修改为.save_passworddbinfoTrue
    1. 方式1和2会触发类型检查,即如果预定义dict中赋值的类型和默认值的类型preset_config不匹配,程序会报错,因此,如果不想强制类型检查,可以使用config["index"] = "sdf"强制参数索引的值到字符串sdf(不推荐,会造成意想不到的影响)。

阅读方法

dataset通过config.dataset或直接读取参数值config["dataset"]

print(config.dataset, config["index"])

参数的值a将按以下顺序读取:最后修改config.a = *的值>--a 2命令行指定的值> "a":1preset_config定义的初始值。

对于list类型,如果传递的是多维数组,则可以通过python的标准切片读取信息:

config.dbinfo.certificate_info = [1,[],[[2]]]
print(config.dbinfo.certificate_info[2][0][0])

对于单个嵌套对象中的参数,读取参数值的方式有四种,都可以读取成功:

print(config.dbinfo.username)
print(config["dbinfo"].password)
print(config.dbinfo["retry_interval_time"])
print(config["dbinfo"]["save_password"])

将配置传递给函数

只需将上述配置对象作为参数传递给函数并调用它:

def print_dataset_name(c):
  print(c.dataset, c["dataset"], c.dbinfo.certificate_info)

print_dataset_name(c=config)

复制配置

可以通过以下deepcopy方法制作配置对象的深层副本:

from copy import deepcopy
copy_config = deepcopy(config)
# Modify new configuration's parameter value, will not affect the orignal configuration
copy_config.index=15 

将配置参数存储到本地文件或数据库

整个参数配置可以保存到本地文件,也可以上传到远程服务器,如mongodb,只需config.save()将配置保存为config name (or config if there is no name).json目录中的文件即可,也可以指定文件名和路径,如下:

config.save("config/test_config.json")

然后我们成功将配置保存到文件夹configuration.json内的本地文件中config。文件内容如下:

{
  "index": 1,
  "dataset": "mnist",
  "lr": 0.01,
  "normalization": true,
  "pair": [1, 2],
  "multi_information": [1, 0.5, "test", "TEST"],
  "dbinfo": {
    "username": "NUS",
    "password": 123456,
    "retry_interval_time": 5.5,
    "save_password": false,
    "pair": ["test", 3],
    "multi": { "test": 0.01 },
    "certificate_info": ["1", 2, [3.5]]
  }
}

存入数据库如mongodb,需要先用info = config.get_config()命令获取参数对应的json序列,再用json库序列化。

例如,要将config_with_name配置存储到mongodb

import pymongo
myclient = pymongo.MongoClient('mongodb://username:example.com:27017/', connect=False)
mydb = myclient['exps']
table = mydb["table"]
# Get the configurations
configuration = config.get_config()
# Insert configuration dict into mongodb table
table.insert_one(configuration)

# Or make configuration as part of a bigger dict
all_info = {
  "exp_time":"20220925",
  "configuration":configuration
}
table.insert_one(all_info)

请注意,JSON 不支持元组,因此无论是存储在本地还是存储在数据库中,元组参数都将转换为列表。

高级选项

将参数输入值限制为固定枚举类型

options通过将参数的Config参数传递给类来设置高级选项,例如枚举 Enum 类型Config

option={}
config = Config(preset_config, options=option)

如果要将参数的值限制在一定范围内,可以通过配置来实现:

advanced_options = {
    'lr': {
        "enum": [0.001, 15.5, 0.01, 0.1] # restrict the lr value to one of 0.001, 15.5, 0.01, 0.1
    },
    'index': {
        "enum": [1, 2, 3] # Restrict the index value to 1, 2 and 3
    },
    "dbinfo": {
        "username": {
            "enum": ["XDU", "ZJU", "NUS"] # restrict the dbinfo.username field to XDU, ZJU and NUS
        },
        "multi":{
            "test":{
                "enum": [1,0.1, 0.01, 15] # 3 layers nested
            }
        }
    },
}

config = Config(preset_config, options=advanced_options)

如果设置了enum,下面三种将参数设置为限定/指定值以外的值的方式都会报错。

    1. 的初始值设置为除inindex以外的值:1,2,3preset_config
    preset_config = {
      "index":4,
    }
    
    1. lr命令行为参数传递不合格/未指定的值
    python example.py --lr 0.02
    
    1. 代码将 的值更改为dbinfo.username以外的值XDU, ZJU and NUS
    config.dbinfo.username = "UEST"
    

    输出是:

    AttributeError: Can not set value 4 because the key 'index' has set enum list and you the value 4 is not in the enum list [1, 2, 3]!
    
    AttributeError: Can not set value 0.02 because the key 'lr' has set enum list and you the value 0.02 is not in the enum list [0.001, 15.5, 0.01, 0.1]!
    
    AttributeError: Can not set value nus because the key 'username' has set enum list and you the value nus is not in the enum list ['XDU', 'ZJU', 'NUS']!
    

打印参数帮助说明

设置参数说明

helpers通过在Config类中指定参数来设置参数描述助手。

helpers = {
    "index": "index of information",
    "dbinfo_help": "information dict for database",
    "dbinfo": {
        "username": "username for database",
        "multi":{
            "test":"test information"
        }
    }
}

config = Config(preset_config, helpers=helpers)

注意,由于dbinfo参数是a dict,如果要为 设置参数描述dbinfo,需要设置一个dbinfo_help参数来写helpers字典的描述,即_help在dict参数名后面加,设置dict字段的参数描述。

打印参数帮助

打印参数描述的两种方法,通过在命令行上传递-h或调用,或者通过在代码中调用函数。-helphelp()

config_with_name.help()

或者

python example.py -h
# OR
python example.py -help

请注意,它只是一个短斜杠-,没有添加其他命令行参数来获取帮助说明,两种方法的输出都是:

Parameter helps for Federated Learning Experiments:
+-------------------+-------+-------------------------------+
|        Key        |  Type | Comments                      |
+-------------------+-------+-------------------------------+
|       index       |  int  | index of information          |
|      dataset      |  str  | -                             |
|         lr        | float | -                             |
|   normalization   |  bool | -                             |
|        pair       | tuple | -                             |
| multi_information |  list | -                             |
|       dbinfo      |  dict | information dict for database |
+-------------------+-------+-------------------------------+

Parameter helps for dict dbinfo:
+---------------------+-------+-----------------------+
|         Key         |  Type | Comments              |
+---------------------+-------+-----------------------+
|       username      |  str  | username for database |
|       password      |  int  | -                     |
| retry_interval_time | float | -                     |
|    save_password    |  bool | -                     |
|         pair        | tuple | -                     |
|        multi        |  dict | Multiple Parameters   |
|   certificate_info  |  list | -                     |
+---------------------+-------+-----------------------+

Parameter helps for dict multi:
+------+-------+------------------+
| Key  |  Type | Comments         |
+------+-------+------------------+
| test | float | test information |
+------+-------+------------------+

需要注意的事情

与 Argparse 冲突

该库无法与 argparse 库同时读取命令行参数,因此请不要args = parser.parse_args()在使用该库时读取命令行参数。

输入值强制转换

参数的类型会被自动检测为与 中设置的初始值相同的类型preset_config,并强制将命令行参数的值转换为对应的类型值,如index上面preset_configdict中的默认值为1,那么参数索引的类型是int初始值为1。如果--index 15.5在命令行指定,参数index会自动赋值给 value 15,也就是15.5会自动强制转换为inttype 。

如果命令行参数上指定的参数值不能强制转换为特定类型,会报错,比如如果命令行指定--index sdf,as sdf with orignal format ofstring不能转换为int类型,会报错一个错误。

命令行传递时,list参数需要在字符串元素引号前加上反斜杠

当命令行参数设置为输入list类型时,如果列表中的元素是 a 则string,必须backslash \在 each 之前添加 a 才能single/double quote正确解析,否则参数值将被视为 an intorfloat类型。如果spaces命令行中有的话会自动合并(但是命令行环境不行zsh,如果是zsh环境则必须去掉list里面的所有空格,bashsh存在这个问题,就是zsh里面环境中,您不能在 ) 之间添加任何15空格。\'12\'--a [15,\'12\']

如果参数可以设置如下:

python test.py --array [1,2.3,\'sdf\'] 

即能正确解析出值为a的数组参数list和a的内容[1,2.3,'sdf', "qwe"],即同时包含int、float、string类型数据的list。

元组参数的命令行赋值需要引号,字符串元素前面必须有反斜杠

当命令行参数设置为输入tuple类型时,指定的元组类型值必须用 ; 括起来quotes。如果元组中的元素是 a string,则backslash必须在每个单/双引号之前添加a\以便正确解析,否则参数值将被视为 an intorfloat类型。同样的,如果spaces命令行中有的话会自动合并(但是命令行环境不能zsh,如果是zsh环境则必须去掉所有内部空格,bash和sh没有这个问题)。

例如,可以将参数设置为

python test.py --pair "(1,2,\'msg\')"

pair 参数的值是一个类型(1,2, "msg")的元组,即一个类型为int, float,的元组string

参数命名约定

如果参数名称中包含-+.space或等特殊字符other python reserved characters,则必须使用middle bracket []来读写参数值,而不是例如,如果参数名称是multi-information,则只能通过 访问config["multi-information"],不能做config.multi-information,因为减号sign -是python语言的保留符号。

无限层嵌套对象

现在该工具可以支持无限层嵌套,其他支持的参数类型有:int, float, string, bool, tuple and list.

参数完整性检查,所有要修改的参数必须预先定义

命令行传递的参数名称必须preset_config事先定义好,否则会报错,例如

python test.py --arg1 1

由于dictarg1中没有定义参数名preset_config,所以报错,表示arg1参数没有定义。该功能设置为执行参数完整性检查,以避免通过命令行输入错误的参数名称。

zsh环境下的特殊配置

如果zsh: no matches found在zsh Shell环境中传递list参数时出现,请setopt no_nomatch在文件末尾添加一行~/.zshrc,保存后source ~/.zshrc在命令行运行刷新zsh,问题就解决了。

完整转换示例

下面将举例说明该工具与该工具相比的便利性argparse

需要使用该argparse工具编写的代码:

parser = argparse.ArgumentParser(description='PyTorch local error training')
parser.add_argument('--model', default='vgg8b',
                    help='model, mlp, vgg13, vgg16, vgg19, vgg8b, vgg11b, resnet18, resnet34, wresnet28-10 and more (default: vgg8b)')
parser.add_argument('--dataset', default='CIFAR10',
                    help='dataset, MNIST, KuzushijiMNIST, FashionMNIST, CIFAR10, CIFAR100, SVHN, STL10 or ImageNet (default: CIFAR10)')
parser.add_argument('--batch-size', type=int, default=128,
                    help='input batch size for training (default: 128)')
parser.add_argument('--num-layers', type=int, default=1,
                    help='number of hidden fully-connected layers for mlp and vgg models (default: 1')
parser.add_argument('--lr', type=float, default=5e-4,
                    help='initial learning rate (default: 5e-4)')
parser.add_argument('--lr-decay-milestones', nargs='+', type=int, default=[200,300,350,375],
                    help='decay learning rate at these milestone epochs (default: [200,300,350,375])')
parser.add_argument('--optim', default='adam',
                    help='optimizer, adam, amsgrad or sgd (default: adam)')
parser.add_argument('--beta', type=float, default=0.99,
                    help='fraction of similarity matching loss in predsim loss (default: 0.99)')
args = parser.parse_args()

args.cuda = not args.no_cuda and torch.cuda.is_available()
if args.cuda:
    cudnn.enabled = True
    cudnn.benchmark = True

使用此工具转换后要编写的代码:

'''
:param model: model, mlp, vgg13, vgg16, vgg19, vgg8b, vgg11b, resnet18, resnet34, wresnet28-10 and more (default: vgg8b)
:param dataset: dataset, MNIST, KuzushijiMNIST, FashionMNIST, CIFAR10, CIFAR100, SVHN, STL10 or ImageNet (default: CIFAR10)
:param batch-size: input batch size for training (default: 128)
:param num-layers: number of hidden fully-connected layers for mlp and vgg models (default: 1)
:param lr: initial learning rate (default: 5e-4)
:param lr-decay-milestones: decay learning rate at these milestone epochs (default: [200,300,350,375])
:param optim: optimizer, adam, amsgrad or sgd (default: adam)
:param beta: fraction of similarity matching loss in predsim loss (default: 0.99)
'''
config = {
  'model':'vgg8b',
  'dataset':'CIFAR10',
  'batch-size':128,
  'num-layers':1,
  'lr':5e-4,
  'lr-decay-milestones':[200,300,350,375],
  'optim':'adam',
  'beta':0.99,
}
args = Config(config, name='PyTorch local error training')

args.cuda = not args.no_cuda and torch.cuda.is_available()
if args.cuda:
    cudnn.enabled = True
    cudnn.benchmark = True

正如我们所看到的,代码量减少了,并且更加结构化和整洁。

图书馆点击的另一个案例:

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo(f"Hello {name}!")

if __name__ == '__main__':
    hello()

可以转换为以下代码:

from commandline_config import Config

def  hello ( o ): 
    """简单的程序,向 NAME 打招呼总共 COUNT 次。""" 
    for  x  in  range ( o . count ): 
        print ( f "Hello { o . name } !" )

if  __name__  ==  '__main__' : 
    args  =  { 
      "count" : 1 ,  # 问候次数。
      "name" : "" ,  # 要打招呼的人。
    }
    选项 = 配置参数
    你好选项

示例运行脚本

您可以example.py在 Github 项目中运行来测试整个工具,文件中已经提供了大多数函数的代码:

# Get help for all parameters of example.py
python example.py -h
# Specify parameter values
python example.py --dbinfo.multi.test 0.01 --dbinfo.username NUS

破碎的想法

下面描述了作者个人开发的原因和这个包的好处/方便。

对于我们经常进行研究实验的我们来说,是否经常需要在一个python文件的开头设置很多命令行