【问题标题】:How can I deduplicate this argument handling code?如何删除此参数处理代码的重复数据?
【发布时间】:2019-11-27 15:39:19
【问题描述】:

我有以下代码在最终调用 matplotlib 的脚本中从命令行设置 y 轴和 x 轴限制(这里,ax 是 matplotlib axes 对象,但这并不重要,并且pArgumentParser 实例):

p.add_argument('--ylim', help='Set the y axis limits explicitly (e.g., to cross at zero)', type=float, nargs='+')
p.add_argument('--xlim', help='Set the x axis limits explicitly', type=float, nargs='+')

# more stuff


if args.ylim:
    if (len(args.ylim) == 1):
        ax.set_ylim(args.ylim[0])
    elif (len(args.ylim) == 2):
        ax.set_ylim(args.ylim[0], args.ylim[1])
    else:
        sys.exit('provide one or two args to --ylim')

if args.xlim:
    if (len(args.xlim) == 1):
        ax.set_xlim(args.xlim[0])
    elif (len(args.xlim) == 2):
        ax.set_xlim(args.xlim[0], args.xlim[1])
    else:
        sys.exit('provide one or two args to --xlim')

如果你相信DRY 可能会让你的眼睛发烫:这两个块是相同的,只是xlim 在第二个处替换了ylim

如何重构它以删除重复项?我调用两次的某种限制设置功能似乎很明显,但是我如何传递我想在一种情况下调用 set_ylim 而在另一种情况下调用 set_xlim 的事实?

请注意,在不指定 --*lim 参数中的一个或两个的情况下调用脚本是完全有效的,并且应该表现得好像从未调用过相应的 set_*lim 函数(即,如上面的代码中一样 - 尽管如果函数被调用,但效果与示例相同,也很好)。

【问题讨论】:

    标签: python python-3.x refactoring argparse dry


    【解决方案1】:

    函数是一流的对象。如果您编写 ax.set_ylim 而不调用它,您将获得对 set_ylim() 函数的引用,该函数绑定到 ax

    def check(name, lim, setter) 
        if not lim:
            return
    
        if (len(lim) == 1):
            setter(lim[0])
        elif (len(lim) == 2):
            setter(lim[0], lim[1])
        else:
            sys.exit(f'provide one or two args to {name}')
    
    check('--ylim', args.ylim, ax.set_ylim)
    check('--xlim', args.xlim, ax.set_xlim)
    

    【讨论】:

      【解决方案2】:

      使用 argparse 有几个选项:

      定义一个自定义操作,将值的数量限制在指定范围内,如Python argparse: Is there a way to specify a range in nargs?

      这具有在传入参数的上下文中处理错误的优势;并且由于您的输入应该是可以接受的,因此您可以解压缩 args。

      # From: https://stackoverflow.com/a/4195302/3279716
      def required_length(nmin,nmax):
          class RequiredLength(argparse.Action):
              def __call__(self, parser, args, values, option_string=None):
                  if not nmin<=len(values)<=nmax:
                      msg='argument "{f}" requires between {nmin} and {nmax} arguments'.format(
                          f=self.dest,nmin=nmin,nmax=nmax)
                      raise argparse.ArgumentTypeError(msg)
                  setattr(args, self.dest, values)
          return RequiredLength
      
      # Make parser
      parser=argparse.ArgumentParser(prog='PROG')
      parser.add_argument('--xlim', nargs='+', type=int, action=required_length(1,2))
      parser.add_argument('--ylim', nargs='+', type=int, action=required_length(1,2))
      
      # Use args in code
      ax.set_xlim(*args.xlim)
      ax.set_ylim(*args.ylim)
      

      或者,您可以允许输入长度为 1 或更多 (nargs=“+”),然后手动检查输入的长度并在需要时引发解析器错误:

      # Make parser
      parser=argparse.ArgumentParser(prog='PROG')
      parser.add_argument('--xlim', nargs='+', type=int)
      parser.add_argument('--ylim', nargs='+', type=int)
      
      # Check args
      if not 1 <= len(args.xlim) <= 2:
          parser.error("xlim must have length 1 or 2")
      if not 1 <= len(args.ylim) <= 2:
          parser.error("ylim must have length 1 or 2")
      
      # Use args in code
      ax.set_xlim(*args.xlim)
      ax.set_ylim(*args.ylim)
      

      【讨论】:

      • 我认为这不适用于未设置某些 --xlim--ylim 的(通常)情况,但似乎 set_xlim is a no-op 在通过 0 时论据。
      • 您还可以将默认参数指定为 [None, None],这应该不会影响它们。
      • 确实,我现在意识到,即使在调用mlp API 函数时,也有一种方法可以不指定它们。但是,导致调用(例如)set_xlim([None, None]) 方法的默认 arg 方法与您在答案中显示的方法相比没有优势,不是吗?也许我错过了什么。
      • 在我的两种方法中,我认为xlimylim 都是必需的,因为nargs=‘+’。 None 列表解决了这个问题。但是,您可以改用 nargs=‘*’ 并稍微更改检查
      • 啊,我错过了。是的,一个关键(未记录的)要求是需要支持将--[x|y]lim 关闭,实际上这是调用此脚本的最常见方式。将它们关闭应该“好像”没有调用 set_* 方法。我正在更新问题以包含 argparse 行以使其更清晰。
      猜你喜欢
      • 2021-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多