推荐用于函数参数处理的Python模块?

时间:2012-02-23 16:39:25

标签: python parsing arguments

有许多Python模块可用于解析和协调命令行选项(argparse,getopt,blargs等)。 Python有很好的内置功能/习惯用法来处理各种函数参数(例如,默认值,* varargs,** keyword_args)。但是当我阅读各种项目的顶级函数代码时,我发现函数参数的规则和标准化明显少于命令行参数。

对于简单的功能,这不是问题;内置的参数功能非常有用,并且绰绰有余。但是有很多功能丰富的模块,它们的顶级函数提供了许多不同的参数和选项(一些互补或独占),不同的操作模式,默认值,过载等等 - 也就是说,它们具有参数复杂性接近命令行参数。他们似乎在很大程度上以 ad hoc 的方式处理他们的论点。

鉴于命令行处理模块的数量,以及它们随着时间的推移变得多么精致,我期望至少有一些模块可以简化复杂函数参数的争论。但我搜索了PyPi,stackoverflow和谷歌没有成功。那么......你会推荐使用函数(不是命令行!)参数处理模块吗?

---用例子更新---

很难给出一个真正简单的具体示例,因为在您处理复杂模块之前,用例不会出现。但是这里是解释代码中的问题的一个镜头:一个格式化程序模块,其默认值可以在格式化程序实例化中重写,或者在调用函数/方法时。由于只有几个选项,已经有大量的选项处理措辞,选项名称会重复 ad nauseam

defaults = { 'indent':     4,
              'prefix':    None,
              'suffix':    None,
              'name':      'aFormatter',
              'reverse':   False,
              'show_name': False
            }

class Formatter(object):

    def __init__(self, **kwargs):
        self.name    = kwargs.get('name',    defaults['name'])
        self.indent  = kwargs.get('indent',  defaults['indent'])
        self.prefix  = kwargs.get('prefix',  defaults['prefix'])
        self.suffix  = kwargs.get('suffix',  defaults['suffix'])
        self.reverse = kwargs.get('reverse', defaults['reverse'])
        self.show_name = kwargs.get('show_name', defaults['show_name'])

    def show_lower(self, *args, **kwargs):
        indent = kwargs.get('indent', self.indent) or 0
        prefix = kwargs.get('prefix', self.prefix) 
        suffix = kwargs.get('suffix', self.suffix)
        reverse = kwargs.get('reverse', self.reverse)
        show_name = kwargs.get('show_name', self.show_name)

        strings = []
        if show_name:
            strings.append(self.name + ": ")
        if indent:
            strings.append(" " * indent)
        if prefix:
            strings.append(prefix)
        for a in args:
            strings.append(a.upper() if reverse else a.lower())
        if suffix:
            strings.append(suffix)
        print ''.join(strings)

if __name__ == '__main__':
    fmt = Formatter()
    fmt.show_lower("THIS IS GOOD")
    fmt.show_lower("THIS", "IS", "GOOD")
    fmt.show_lower('this IS good', reverse=True)
    fmt.show_lower("something!", show_name=True)

    upper = Formatter(reverse=True)
    upper.show_lower("this is good!")
    upper.show_lower("and so is this!", reverse=False)

5 个答案:

答案 0 :(得分:3)

当我第一次阅读你的问题时,我心里想到你要求一个创可贴模块,并且它不存在,因为没有人想要编写一个能让坏设计继续存在的模块。

但我意识到情况比这更复杂。创建模块(如您描述的模块)的关键是创建可重用的通用案例代码。现在,很可能有些接口非常复杂。但是那些接口正好接口可能无法通过通用案例代码轻松处理。它们很复杂,因为它们解决了许多特殊情况下的问题域。

换句话说,如果一个接口实际上不能被重构,那么它可能需要很多自定义的特殊情况代码,这些代码不够可预测,不值得在模块中进行泛化。相反,如果一个接口可以很容易地使用你描述的那种模块进行修补,那么它也可能会被重构 - 在这种情况下它应该是重构的。

答案 1 :(得分:2)

  1. 我不认为命令行解析和函数参数处理有很多共同之处。命令行的主要问题是唯一可用的数据结构是字符串的平面列表,并且您没有可用于定义每个字符串含义的函数头的工具。在Python函数的标题中,您可以为每个参数指定名称,您可以接受容器作为参数,您可以定义默认参数值等。命令行解析库的作用实际上是为命令行提供了一些参数。 Python为函数调用提供的功能:为参数指定名称,分配默认值,转换为所需的类型等。在Python中,所有这些功能都是内置的,因此您不需要库来达到这种方便程度。

  2. 关于您的示例,有许多方法可以通过使用语言提供的功能来改进此设计。您可以使用默认参数值而不是defaults字典,可以将所有标志封装在FormatterConfig类中,并且只反复传递一个参数而不是所有这些参数。但是,让我们假设你想要完全在示例代码中给出的接口。实现此目的的一种方法是以下代码:

    class Config(dict):
        def __init__(self, config):
            dict.__init__(self, config)
            self.__dict__ = self
    
    def get_config(kwargs, defaults):
        config = defaults.copy()
        config.update(kwargs)
        return Config(config)
    
    class Formatter(object):
    
        def __init__(self, **kwargs):
            self.config = get_config(kwargs, defaults)
    
        def show_lower(self, *args, **kwargs):
            config = get_config(kwargs, self.config)
    
            strings = []
            if config.show_name:
                strings.append(config.name + ": ")
            strings.append(" " * config.indent)
            if config.prefix:
                strings.append(config.prefix)
            for a in args:
                strings.append(a.upper() if config.reverse else a.lower())
            if config.suffix:
                strings.append(config.suffix)
            print "".join(strings)
    

    Python提供了许多工具来完成这种参数处理。因此,即使我们决定不使用其中一些(如默认参数),我们仍然可以避免重复过多。

答案 2 :(得分:1)

如果您的API太复杂,您认为使用某个模块来处理传递给您的选项会更容易,实际解决方案很可能是简化您的API。事实上,一些模块有非常复杂的方式调用东西是一种耻辱,而不是一种功能。

答案 3 :(得分:0)

它在开发人员手中,但如果您正在创建一个可能对其他项目有用或将在其他用户之间发布的库,那么我认为首先需要确定您的问题并对其进行分析,

很好地记录你的功能,最好尽量减少参数的数量, 为功能参数提供默认值,用户可能无法指定确切需要传递的内容。

对于某些复杂的要求,您可以提供特殊的classmethods,可以覆盖高级编程,或者高级用户实际上希望实现他们正在使用的库,继承始终存在。

并且您也可以阅读PEP8也可能有用,但最终目标是指定最小参数数量,限制用户输入所需参数,以及为可选参数提供默认值的好处 - 顺便说一下您的library / code也很容易被普通开发人员理解......

答案 4 :(得分:0)

您可以为默认值编写更多通用代码。

如果您考虑违反相反的方法,请查看默认值并覆盖关键字(如果不存在)。

 defaults = { 'indent':     4,
          'prefix':    None,
          'suffix':    None,
          'name':      'aFormatter',
          'reverse':   False,
          'show_name': False
        }

class Formatter(object):

   def __init__(self, **kwargs):
      for d,dv in defaults.iteritems():
         kwargs[d] = kwargs.get(d, dv)

旁注: 我建议在__init__方法定义中使用默认值使用关键字args。这允许function定义真正成为您班级的其他开发者和用户的合约(Formatter

def __init__(self, indent=4, reverse=False .....etc..... ):