Рекомендуемые модули Python для обработки аргументов функций? - PullRequest
5 голосов
/ 23 февраля 2012

Существует множество модулей Python для анализа и координации параметров командной строки (argparse, getopt, blargs и т. Д.). И Python наделен хорошими встроенными функциями / идиомами для обработки различных аргументов функций (например, значения по умолчанию, * varargs, ** keyword_args). Но когда я читаю код различных проектов для функций верхнего уровня, я вижу заметно меньше дисциплины и стандартизации аргументов функций, чем аргументов командной строки.

Для простых функций это не проблема; встроенные функции аргумента прекрасно работают и более чем достаточны. Но есть много функционально богатых модулей, чьи функции верхнего уровня предоставляют множество различных аргументов и опций (некоторые дополняют или исключают), различные режимы работы, значения по умолчанию, переопределения и т. Д., То есть они имеют сложность аргументов приближается к аргументу командной строки. И они, кажется, в значительной степени обрабатывают свои аргументы ad hoc способами.

Учитывая количество модулей обработки командной строки и то, насколько изящными они стали со временем, я бы ожидал, по крайней мере, несколько модулей для упрощения обработки сложных аргументов функций. Но я безуспешно искал PyPi, stackoverflow и Google. Итак ... есть ли модули (не командная строка!), Которые вы бы порекомендовали?

--- обновление с примером ---

Трудно привести действительно простой конкретный пример, потому что вариант использования не появляется, пока вы не разберетесь со сложным модулем. Но вот попытка объяснить проблему в коде: модуль форматирования со значениями по умолчанию, которые можно переопределить при создании экземпляра форматера или при вызове функции / метода. Для того, чтобы иметь только несколько опций, уже есть очень много словоблоков обработки опций, и имена опций повторяются 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 ]

3 голосов
/ 23 февраля 2012

Когда я впервые прочитал ваш вопрос, я подумал, что вы запрашиваете модуль для лейкопластыря, и что его не существует, потому что никто не хочет писать модуль, который позволит сохранить плохой дизайн.

Но я понял, что ситуация сложнее, чем это. Смысл создания модуля, подобного тому, который вы описываете, заключается в создании повторно используемого кода общего назначения. Теперь вполне может быть, что есть некоторые интерфейсы, которые оправданно сложны. Но эти интерфейсы точно интерфейсы, которые, вероятно, не могут быть легко обработаны общим кодом. Они сложны, потому что они решают проблемную область с множеством особых случаев.

Другими словами, если интерфейс действительно не может быть подвергнут рефакторингу, то он, вероятно, требует много специального, особого случая кода, который недостаточно предсказуем, чтобы его можно было обобщить в модуле. И наоборот, если интерфейс может быть легко исправлен с помощью модуля того типа, который вы описываете, то он, вероятно, также может быть подвергнут рефакторингу - в таком случае это должно быть.

2 голосов
/ 24 февраля 2012
  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 предлагает множество инструментов для такой обработки аргументов.Поэтому, даже если мы решим не использовать некоторые из них (например, аргументы по умолчанию), мы все равно сможем избежать повторения слишком много.

1 голос
/ 23 февраля 2012

Если ваш API настолько сложен, что вы думаете, что будет проще использовать какой-то модуль для обработки переданных вам опций, есть большая вероятность, что фактическое решение состоит в том, чтобы упростить ваш API. Тот факт, что в некоторых модулях есть очень сложные способы вызова вещей, является позором, а не особенностью.

0 голосов
/ 23 февраля 2012

Вы можете написать более общий код для значения по умолчанию.

Если вы думаете о дефолте с другой стороны, просмотрите значения по умолчанию и перезапишите ключевые слова, если их не существует.

 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)

Примечание стороны: Я бы рекомендовал использовать ключевые слова args в определении метода __init__ со значениями по умолчанию. Это позволяет определению function действительно стать контрактом с другими разработчиками и пользователями вашего класса (Formatter)

def __init__(self, indent=4, reverse=False .....etc..... ):
0 голосов
/ 23 февраля 2012

Это в руках разработчика, но если вы создаете библиотеку, которая может быть полезна для некоторых других проектов или будет опубликована для других пользователей, то я думаю, что сначала вам нужно определить свою проблему и проанализировать ее,

Хорошо документируйте свои функции. Хорошо минимизировать количество аргументов, укажите значения по умолчанию для функциональных аргументов, когда у пользователей могут возникнуть проблемы с указанием того, что именно нужно передать.

и для некоторых сложных требований вы можете предоставить специальный classmethods, который можно переопределить для продвинутого программирования или для опытных пользователей, которые действительно хотят добиться того, что они играют с библиотекой, наследование всегда есть.

и вы также можете прочитать PEP8 , что может быть полезно, но конечная цель - указать минимальное количество аргументов, ограничить пользователей вводом требуемых аргументов, полезно указывать значения по умолчанию для необязательных аргументов - в способ, которым ваш library / code легко понятен и обычным разработчикам ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...