文章目录
无论是个人开发还是团队协作,在进行代码开发的时候,统一的代码规范是十分重要的。
这里说的代码规范指的不是变量或者函数的命名规范,而是项目结构的规范。
在这里,我要介绍一个由@幻灰龙和@ccat开发的开源项目pyouter。这个项目很好的为开发者提供了一个规范的项目框架,不管是个人开发还是团队开发,都可以通过使用这套框架来共同规范起开发逻辑。
pyouter的内部原理实现可以参考https://blog.csdn.net/huanhuilong/article/details/121481377或者https://github.com/fanfeilong/pyouter,这篇文章只做项目的实践介绍。
测试项目的github地址为pyouter_test
pip包的安装
# https://pypi.org/project/pyouter/0.0.1 pip install pyouter
项目目录结构
假设我们开发的项目名为pyouter_test,我们的项目目录应该如下所示:
- pyouter_test
- src
- data(可自定义)
- test(可自定义)
- server(可自定义)
- config
- config.py
- main.py
- options.py
- src
项目实现
定义好我们的目录结构之后,首先进行main.py和options.py的实现。
main.py是我们整个项目的入口,options.py是实现整个项目结构的逻辑
main.py
from pyouter.app import App from pyouter.router import Router from options import get_args_parser from config.config import load_config def main_dispatch(): """ 主路由 """ router = Router( init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])), ) return router def run(): """ 入口函数 """ # 获取命令行参数 args_parser = get_args_parser() options = args_parser.parse_args() # 加载配置文件 config = load_config(options) app = App( config=config, parser=args_parser ) app.use( router=Router( pyouter_test = main_dispatch() ) ) app.run() if __name__=="__main__": run()
options.py
from argparse import ArgumentParser from pyouter.default import create_parser def add_custom_arguments(parser: ArgumentParser): ''' 用户定制化参数选项 * --cluster: 开发环境还是线上环境 ''' parser.add_argument( "--cluster", dest="cluster", help="cluster dev or pro", default="dev", nargs='?', type=str, metavar="CLUSTER" ) def get_args_parser(): parser = create_parser("pyouter test") add_custom_arguments(parser) return parser def show_help(): """ 命令行选项说明: == """ help = '\n'.join([ show_help.__doc__, add_custom_arguments.__doc__ ]) print(help)
我们在main函数里面配置好了整个项目的入口,在options.py里面设置了一个参数"–cluster"。
我们还在main函数里面调用了config/config.py
里面的load_config函数,这个函数load了一个字典格式的config,方便我们对项目的环境进行配置,类似以下代码:
config.py
def load_config(options): if options.cluster == 'dev': config = {'server_ip':'127.0.0.1'} elif options.cluster == 'pro': config = {'server_ip':'192.168.0.1'} return config
写完上面三个python代码后可以在src目录下运行命令
python main.py pyouter_test.init --cluster dev
会打印结果
如果运行命令
python main.py pyouter_test.init --cluster pro
则会打印结果
二级叶子结点test实现
综上,项目的基本框架已经实现了,接下来实现叶子结点的函数。
例如,我们在src/test/test_hello.py
有一个函数
def test_helloworld(config,options): print('hello world!')
我们要怎么直接运行这个函数呢?
首先,我们需要在src/test/下面定义一个__init__.py
from pyouter.router import Router def dispatch(): from test.test_hello import test_helloworld router=Router( test_hello = test_helloworld ) return router
然后在main.py
里面修改main_dispatch()
函数
def main_dispatch(): """ 主路由 """ from test import dispatch as test_dispatch router = Router( init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])), test = test_dispatch() ) return router
接下来运行命令
python main.py pyouter_test.test.test_hello --cluster dev
则会直接运行test_helloworld函数,终端打印
三级叶子结点server实现
上面的test_helloworld函数只是二级的叶子结点实现,假如我们要进行一个三级的叶子结点实现可以参考以下的server实现。
假如我们在src/data/
目录下有一个data.txt
文件,里面只有三行数据
test server data
我们在src/server/data/read_data.py
里面有个函数read_data_txt
函数
def read_data_txt(config,options): data_path = './data/data.txt' with open(data_path) as f: for line in f: print(line + ' ')
如果我们要直接运行这个函数,我们还需要分别在src/server/
和src/server/read_data/
下面分别定义一个__init__.py
。src/server/data/
下面的__init__.py
实现如下:
from pyouter.router import Router def dispatch(): from server.data.read_data import read_data_txt router=Router( read_data_txt = read_data_txt ) return router
src/server/
下面的__init__.py
实现如下:
from pyouter.router import Router def dispatch(): from server.data import dispatch as data_dispatch router=Router( data = data_dispatch() ) return router
然后修改main.py
里面的main_dispatch()
函数:
def main_dispatch(): """ 主路由 """ from test import dispatch as test_dispatch from server import dispatch as server_dispatch router = Router( init = lambda config,options: print('server_ip:{}'.format(config['server_ip'])), test = test_dispatch(), server = server_dispatch() ) return router
接下来运行命令
python main.py pyouter_test.server.data.read_data_txt --cluster dev
终端会打印结果
项目结构细节
上述实现的整体项目类似于一颗树,根目录是src/main.py
,test_helloworld
和read_data_txt
函数分别是二级叶子结点和三级叶子结点,如下图所示:
在运行命令的时候用.
来隔开不同层级结点
python main.py pyouter_test.server.data.read_data_txt
在运行命令的时候可以在后面添加定制化参数如--cluster
python main.py pyouter_test.server.data.read_data_txt --cluster dev
通过options.cluster
可以获得该参数的值"dev"
,还可以在src/options.py
里面的def add_custom_arguments(parser: ArgumentParser)
函数新增定制化参数,比如新增--input_file
和--output_file
参数:
def add_custom_arguments(parser: ArgumentParser): ''' 用户定制化参数选项 * --cluster: 开发环境还是线上环境 ''' parser.add_argument( "--cluster", dest="cluster", help="cluster dev or pro", default="dev", nargs='?', type=str, metavar="CLUSTER" ) parser.add_argument( "--input_file", dest="input_file", help="input_file path", default="", nargs='?', type=str, metavar="INPUT_FILE" ) parser.add_argument( "--output_file", dest="output_file", help="output_file path", default="", nargs='?', type=str, metavar="OUTPUT_FILE" )
执行以下命令的时候可以给input_file
和output_file
参数赋值
python main.py pyouter_test.server.data.read_data_txt --cluster dev --input_file './data/data_txt' --output_file './data/res.txt'
值得注意的是,无论是命令行输入的相对路径还是函数里面实现的路径都是针对/src/main.py
的相对路径。