2017-10-12 39 views
3

是否有使用Python的​​模块的最佳实践或样式准则?编写argparse解析器的最佳实践

我定期与​​一起工作,并且它很快占用相当数量的行来处理所有的配置。对于几乎所有我发现,坚持接近PEP 8结果干净,可读的代码,但不是在这里。最终的结果总是很难看的代码块。

痛苦读的是不是Python的:

Beautiful is better than ugly ... Readibilty counts

那么,有没有PEP或者提供如何更好地格式化这个代码的指导方针一些其他的资源?

丑陋的样品(以下大多PEP 8):

parser = argparse.ArgumentParser(description='A nontrivial modular command') 
subparsers = parser.add_subparsers(help='sub-command help') 

parser_load = subparsers.add_parser('load', help='Load something somewhere') 
parser_load.add_argument('--config', 
         help='Path to configuration file for special settings') 
parser_load.add_argument('--dir', default=os.getcwd(), 
         help='The directory to load') 
parser_load.add_argument('book', help='The book to load into this big thing') 
parser_load.add_argument('chapter', nargs='?', default='', 
         help='Optionally specify a chapter') 
parser_load.add_argument('verse', nargs='*', 
         help='Optionally pick as many verses as you want to' 
         ' load') 
parser_load.set_defaults(command='load') 

parser_write = subparsers.add_parser(
       'write', help='Execute commands defined in a config file') 
parser_write.add_argument('config', help='The path to the config file') 
parser_write.set_defaults(command='write') 

parser_save = subparsers.add_parser(
       'save', 
       help='Save this big thing for use somewhere later') 
parser_save.add_argument('-n', '--name', default=None, 
         help='The name of the component to save') 
parser_save.add_argument('path', help="The way out of Plato's cave") 
parser_save.set_defaults(command='save') 

... 

args = parser.parse_args() 
+2

查看'click':http://click.pocoo.org/5/,通过装饰器更好的参数 – economy

+0

除了不一致的地方,你断线我没有看到任何问题:你出现只有在需要符合80个字符的地方才会突破线条,而不是它可以提高可读性。我不相信这不是题外话:基于意见,因为它会根据您的编码标准而有所不同。 – TemporalWolf

+1

@TemporalWolf我可以看到,这就是为什么我问是否存在标准,而不仅仅是关于如何格式化代码的建议 – jpyams

回答

2

作为评论由TemporalWolf,我会用换行符更加一致,并且更多的人。即使现在的代码出现时间越长,我觉得更容易阅读:各个函数调用之间

  • 更多的垂直空间,因此更容易区分每行视觉
  • 一个参数,因此更容易看到正在使用哪些
  • 参数更接近左边界,因此较少的水平眼运动和较少的不想要的换行符(如在这里您拆分help串的一个)需要

此外,通过重命名parser_X/parser_YX_parser/Y_parser您可以更容易区分X/Y

parser = argparse.ArgumentParser(
    description='A nontrivial modular command' 
) 
subparsers = parser.add_subparsers(
    help='sub-command help' 
) 

load_parser = subparsers.add_parser(
    'load', 
    help='Load something somewhere' 
) 
load_parser.add_argument(
    '--config', 
    help='Path to configuration file for special settings' 
) 
load_parser.add_argument(
    '--dir', 
    default=os.getcwd(), 
    help='The directory to load' 
) 
load_parser.add_argument(
    'book', 
    help='The book to load into this big thing' 
) 
load_parser.add_argument(
    'chapter', 
    nargs='?', 
    default='', 
    help='Optionally specify a chapter' 
) 
load_parser.add_argument(
    'verse', 
    nargs='*', 
    help='Optionally pick as many verses as you want to load' 
) 
load_parser.set_defaults(
    command='load' 
) 

write_parser = subparsers.add_parser(
    'write', 
    help='Execute commands defined in a config file' 
) 
write_parser.add_argument(
    'config', 
    help='The path to the config file' 
) 
write_parser.set_defaults(
    command='write' 
) 

save_parser = subparsers.add_parser(
    'save', 
    help='Save this big thing for use somewhere later' 
) 
save_parser.add_argument(
    '-n', '--name', 
    default=None, 
    help='The name of the component to save' 
) 
save_parser.add_argument(
    'path', 
    help="The way out of Plato's cave" 
) 
save_parser.set_defaults(
    command='save' 
) 

... 

args = parser.parse_args() 
+0

不确定为什么您没有实现您在评论中提到的命名更改。我100%同意它。我最喜欢的格式是[不是那种不同](https://repl.it/M8gm/0),但你的建议我发现[这更好](https://repl.it/M8gm/1),并可能允许删除评论。 – TemporalWolf

+1

@TemporalWolf我没有实现它(但),因为我只有在发布答案后才有想法,并且不想再次编辑所有内容。 – mkrieger1

2

没有什么不对您的代码,这是使用​​模块的只是结果。我个人的偏好是将分析器的创建分解成函数。在这种情况下,您可以为您创建的每个子分析器创建一个函数。

def parse_args(args=sys.argv[1:]): 
    parser = argparse.ArgumentParser(description='A nontrivial modular command') 
    subparsers = parser.add_subparsers(help='sub-command help') 

    add_load_subparser(subparsers) 
    add_write_subparser(subparsers) 
    add_save_subparser(subparsers) 

    return parser.parse_args(args) 


def add_load_subparser(subparsers): 
    parser = subparsers.add_parser('load', help='Load something somewhere') 
    parser.add_argument('--config', 
         help='Path to configuration file for special settings') 
    parser.add_argument('--dir', default=os.getcwd(), 
         help='The directory to load') 
    parser.add_argument('book', help='The book to load into this big thing') 
    parser.add_argument('chapter', nargs='?', default='', 
         help='Optionally specify a chapter') 
    parser.add_argument('verse', nargs='*', 
         help='Optionally pick as many verses as you want to' 
         ' load') 
    parser.set_defaults(command='load') 


def add_write_subparser(subparsers): 
    parser = subparsers.add_parser(
      'write', help='Execute commands defined in a config file') 
    parser.add_argument('config', help='The path to the config file') 
    parser.set_defaults(command='write') 


def add_save_subparser(subparsers): 
    parser = subparsers.add_parser(
       'save', 
       help='Save this big thing for use somewhere later') 
    parser.add_argument('-n', '--name', default=None, 
         help='The name of the component to save') 
    parser.add_argument('path', help="The way out of Plato's cave") 
    parser.set_defaults(command='save') 


args = parse_args() 
1

开发人员对此特定模块的样式没有任何讨论(我一直在密切关注相关的bug /问题)。

我更关心解决问题而不是风格和布局,但喜欢易于阅读和理解的代码。如果有大块重复模式,我喜欢使用实用功能,词典和列表。

最近的一个SO问题,How to design object oriented subparsers for argparse?问了关于OOP子分析器的定义。我把他的初始级,并添加一个方法:

def make_sup(self,sp): 
     self.parser = sp.add_parser(self.name) 
     self.parser.add_argument('--foo') 
     self.parser.set_defaults(action=self) 

所以一组对象,可以用

cmds = [] 
cmds.append(Cmd('list')) 
cmds.append(Cmd('foo')) 
cmds.append(Cmd('bar')) 

甚至

cmds = [Cmd('list'), Cmd('foo'),...] 

,然后用来填充解析器与定义:

parser = argparse.ArgumentParser() 
sp = parser.add_subparsers(dest='cmd') 
for cmd in cmds: 
    cmd.make_sup(sp) 

这是不涉及参数的简单示例。

unittest文件,test_argparse.py有一个相当复杂的系统来简化解析器定义。

class Sig(object): 

    def __init__(self, *args, **kwargs): 
     self.args = args 
     self.kwargs = kwargs 

测试用例创建这些Sig对象的列表:

argument_signatures = [Sig('--foo-bar'), Sig('--baz', dest='zabbaz')] 
argument_signatures = [ 
    Sig('-x', type=float), 
    Sig('-3', type=float, dest='y'), 
    Sig('z', nargs='*'), 
] 

和语法分析器测试类具有类似的方法:

def no_groups(parser, argument_signatures): 
     """Add all arguments directly to the parser""" 
     for sig in argument_signatures: 
      parser.add_argument(*sig.args, **sig.kwargs) 

Ipython已经(或至少有几个版本后)使用config文件条目来定义参数的代码,该代码创建一个大的​​解析器。