Использование Google Fire для упрощения программ командной строки Python

задняя часть Python Командная строка Google
Использование Google Fire для упрощения программ командной строки Python

Hello World

Чтобы представить, что такое Огонь, посмотрите на простой пример, чтобы понять

# calc.py
import fire

class Calculator(object):
  """A simple calculator class."""

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

Далее мы вводим bash для выполнения скрипта, написанного выше

> python calc.py double 10
20
> python calc.py double --number=16
32

Выше приведен официальный пример кода.С огнем написание программ командной строки Python становится очень простым, и нам больше не нужно иметь дело с громоздким анализом параметров командной строки. Затем мы имитируем HelloWorld и пишем сценарий командной строки для вычислений числа пи и факториала.

настоящий бой

import math
import fire


class Math(object):

    def pi(self, n):
        s = 0.0
        for i in range(n):
            s += 1.0/(i+1)/(i+1)
        return math.sqrt(6*s)

    def fact(self, n):
        s = 1
        for i in range(n):
            s *= (i+1)
        return s


if __name__ == '__main__':
    fire.Fire(Math)

Далее мы запускаем

>  python maths.py pi 10000
3.14149716395
>  python maths.py pi 100000
3.14158310433
>  python maths.py pi 1000000
3.14159169866
>  python maths.py fact 10
3628800
>  python maths.py fact 15
1307674368000
>  python maths.py fact 20
2432902008176640000

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

открытый модуль

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

import math
import fire


def pi(n):
    s = 0.0
    for i in range(n):
        s += 1.0/(i+1)/(i+1)
    return math.sqrt(6*s)

def fact(n):
    s = 1
    for i in range(n):
        s *= (i+1)
    return s


if __name__ == '__main__':
    fire.Fire()

Обратите внимание, что вызов функции Fire не имеет параметров, запустите его

>  python maths.py fact 20
2432902008176640000
>  python maths.py pi 1000000
3.14159169866

выставить функцию

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

import math
import fire


def pi(n):
    s = 0.0
    for i in range(n):
        s += 1.0/(i+1)/(i+1)
    return math.sqrt(6*s)


if __name__ == '__main__':
    fire.Fire(pi)

Если открыта функция, может быть раскрыта только одна функция. Если открыты две функции, только последняя вступит в силу. Запустите ее.

>  python maths.py 1000
3.14063805621

Выставить словарь

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

import math
import fire


def pi(n):
    s = 0.0
    for i in range(n):
        s += 1.0/(i+1)/(i+1)
    return math.sqrt(6*s)

def fact(n):
    s = 1
    for i in range(n):
        s *= (i+1)
    return s


if __name__ == '__main__':
    fire.Fire({
        "pi[n]": pi
    })

Мы только выставили функцию pi и изменили имя, запустили ее и увидели эффект

>  python maths.py pi[n] 1000
3.14063805621

Если мы используем оригинальное имя функции, мы увидим понятное сообщение об ошибке, указанное огнем

>  python maths.py pi 1000
Fire trace:
1. Initial component
2. ('Cannot find target in dict:', 'pi', {'pi[n]': <function pi at 0x10a062c08>})

Type:        dict
String form: {'pi[n]': <function pi at 0x10a062c08>}
Length:      1

Usage:       maths.py
             maths.py pi[n]

открытый объект

import math
import fire


class Maths(object):

    def pi(self, n):
        s = 0.0
        for i in range(n):
            s += 1.0/(i+1)/(i+1)
        return math.sqrt(6*s)

    def fact(self, n):
        s = 1
        for i in range(n):
            s *= (i+1)
        return s


if __name__ == '__main__':
    fire.Fire(Maths())

бегать

>  python maths.py pi 1000
3.14063805621
>  python maths.py fact 20
2432902008176640000

выставленный класс

Это мы продемонстрировали в реальной боевой ссылке выше, поэтому я не буду повторяться здесь.

класс против объекта

В приведенном выше примере мы обнаружили, что нет никакой разницы между раскрытием классов и раскрытием объектов, так что какой из них должен быть более элегантным? Это зависит от того, есть ли у конструктора класса параметры.Если это конструктор без параметров, то разницы между экспозицией класса и объекта нет, а если у конструктора класса есть параметры, то она будет другой. Преобразуйте его. Урок математики, добавьте коэффициент увеличения.

import math
import fire


class Maths(object):

    def __init__(self, coeff):
        self.coeff = coeff

    def pi(self, n):
        s = 0.0
        for i in range(n):
            s += 1.0/(i+1)/(i+1)
        return self.coeff * math.sqrt(6*s)

    def fact(self, n):
        s = 1
        for i in range(n):
            s *= (i+1)
        return self.coeff * s


if __name__ == '__main__':
    fire.Fire(Maths)

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

> python maths.py pi 1000 --coeff=2
6.28127611241

Если значение параметра не указано, время выполнения будет ошибка

> python maths.py pi 1000
Fire trace:
1. Initial component
2. ('The function received no value for the required argument:', 'coeff')

Type:        type
String form: <class '__main__.Maths'>
File:        ~/source/rollado/maths.py
Line:        5

Usage:       maths.py COEFF
             maths.py --coeff COEFF

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

открытое имущество

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

> python maths.py coeff --coeff=2
2
> python maths.py coeff --coeff=3
3

Другой более простой пример

# example.py
import fire
english = 'Hello World'
spanish = 'Hola Mundo'
fire.Fire()

бегать

$ python example.py english
Hello World
$ python example.py spanish
Hola Mundo

принцип

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

Все переменные в Python являются объектами, включая обычные целые числа, строки, числа с плавающей запятой, логические значения и т. д. Теоретически структура объекта может быть рекурсивно продолжена для формирования сложного связанного вызова.

цепная экспозиция

Далее мы проверяем эту теорию и пробуем комплексное цепное воздействие.

import fire


class Chain(object):

    def __init__(self):
        self.value = 1

    def incr(self):
        print "incr", self.value
        self.value += 1
        return self

    def decr(self):
        print "decr", self.value
        self.value -= 1
        return self

    def get(self):
        return self.value


if __name__ == '__main__':
    fire.Fire(Chain)

запустить его

> python chains.py incr incr incr decr decr get
incr 1
incr 2
incr 3
decr 4
decr 3
2

Круто! Мы достигаем приятного эффекта цепочки, имея сам объект метода внутри каждого метода.

Далее мы пытаемся деконструировать встроенные строковые объекты.

# xyz.py
import fire

value = "hello"

if __name__ == '__main__':
    fire.Fire()

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

> python xyz.py value
hello
> python xyz.py value upper
HELLO
> python xyz.py value upper lower
Traceback (most recent call last):
  File "xyz.py", line 7, in <module>
    fire.Fire()
  File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 127, in Fire
    component_trace = _Fire(component, args, context, name)
  File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 366, in _Fire
    component, remaining_args)
  File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 542, in _CallCallable
    result = fn(*varargs, **kwargs)
TypeError: upper() takes no arguments (1 given)

К сожалению, встроенный строковый объект, по-видимому, не поддерживает цепные вызовы, и первая верхняя строка выполняется успешно. Но огонь дает специальный символ для решения этой проблемы.

> python xyz.py value upper - lower
hello
> python xyz.py value upper - lower - upper
HELLO
> python xyz.py value upper - lower - upper - lower
hello

Знак минус используется для обозначения конца параметра, чтобы последующие параметры не отображались как параметры функции.

Пусть redis-py станет командной строкой за секунды

Наконец, давайте возьмем еще один классный небольшой пример, раскрывающий StrictRedis для redis-py и превращающий его в командную строку.

import fire
import redis


if __name__ == '__main__':
    fire.Fire(redis.StrictRedis)

Это так просто, тогда вы можете играть с командной строкой

>  python client.py flushdb
True
>  python client.py set codehole superhero
True
>  python client.py get codehole
superhero
>  python client.py exists codehole
True
>  python client.py keys "*"
codehole
>  python client.py delete codehole
1
# 指定地址
> python client.py set codehole superhero --host=127.0.0.1 --port=6379
True

Суммировать

С такой небольшой библиотекой классов, как Google Fire, мы можем избавиться от сложного анализа параметров командной строки. Мы часто говорим, что написание кода должно быть красивым и элегантным, без хорошей библиотеки классов этого идеала достичь не очень просто. Если нет огня, у вас есть возможность попробовать красиво написать сложный код разбора параметров командной строки и показать учителю.

Читайте более продвинутые статьи, обратите внимание на паблик-аккаунт "Code Cave"