Подтвердить что ты не робот

Автоматически вводить параметры ввода в Python

Фон:
Я в основном запускаю скрипты python из командной строки в конвейерах, и поэтому мои аргументы всегда являются строками, которые необходимо вводить типам соответствующего типа. Я делаю много маленьких скриптов каждый день и набираю каждый параметр для каждого script занимает больше времени, чем нужно.

Вопрос:
Существует ли канонический способ автоматического ввода параметров роли для функции?

Мой путь:
Я разработал декоратор, чтобы делать то, что хочу, если нет лучшего способа. Декоратором является autocast fxn ниже. Украшенный fxn является fxn2 в примере. Обратите внимание, что в конце блока кода я передал 1 и 2 как строки, и если вы запустите script, он автоматически добавит их. Это хороший способ сделать это?

def estimateType(var):
    #first test bools
    if var == 'True':
            return True
    elif var == 'False':
            return False
    else:
            #int
            try:
                    return int(var)
            except ValueError:
                    pass
            #float
            try:
                    return float(var)
            except ValueError:
                    pass
            #string
            try:
                    return str(var)
            except ValueError:
                    raise NameError('Something Messed Up Autocasting var %s (%s)' 
                                      % (var, type(var)))

def autocast(dFxn):
    '''Still need to figure out if you pass a variable with kw args!!!
    I guess I can just pass the dictionary to the fxn **args?'''
    def wrapped(*c, **d):
            print c, d
            t = [estimateType(x) for x in c]
            return dFxn(*t)
    return wrapped

@autocast
def fxn2(one, two):

   print one + two 

fxn2('1', '2')      

EDIT: для тех, кто приходит сюда, и хочет, чтобы обновленная и краткая рабочая версия появилась здесь:

https://github.com/sequenceGeek/cgAutoCast

И здесь также работает рабочая версия, основанная на выше:

def boolify(s):
    if s == 'True' or s == 'true':
            return True
    if s == 'False' or s == 'false':
            return False
    raise ValueError('Not Boolean Value!')

def estimateType(var):
    '''guesses the str representation of the variables type'''
    var = str(var) #important if the parameters aren't strings...
    for caster in (boolify, int, float):
            try:
                    return caster(var)
            except ValueError:
                    pass
    return var

def autocast(dFxn):
    def wrapped(*c, **d):
            cp = [estimateType(x) for x in c]
            dp = dict( (i, estimateType(j)) for (i,j) in d.items())
            return dFxn(*cp, **dp)

    return wrapped

######usage######
@autocast
def randomFunction(firstVar, secondVar):
    print firstVar + secondVar

randomFunction('1', '2')
4b9b3361

Ответ 1

Если вы хотите автоматически конвертировать значения:

def boolify(s):
    if s == 'True':
        return True
    if s == 'False':
        return False
    raise ValueError("huh?")

def autoconvert(s):
    for fn in (boolify, int, float):
        try:
            return fn(s)
        except ValueError:
            pass
    return s

Вы можете настроить boolify, чтобы принимать другие логические значения, если хотите.

Ответ 2

Вы можете просто использовать простой eval для ввода строки, если вы доверяете источнику:

>>> eval("3.2", {}, {})
3.2
>>> eval("True", {}, {})
True

Но если вы не доверяете источнику, вы можете использовать literal_eval из модуля ast.

>>> ast.literal_eval("'hi'")
'hi'
>>> ast.literal_eval("(5, 3, ['a', 'b'])")
(5, 3, ['a', 'b'])

Edit: Как замечает Ned Batchelder, он не будет принимать строки без кавычек, поэтому я добавил обходной путь, а также пример об автозаполнении с аргументами ключевого слова.

import ast

def my_eval(s):
    try:
        return ast.literal_eval(s)
    except ValueError: #maybe it a string, eval failed, return anyway
        return s       #thanks gnibbler

def autocaste(func):
    def wrapped(*c, **d):
        cp = [my_eval(x) for x in c]
        dp = {i: my_eval(j) for i,j in d.items()} #for Python 2.6+
        #you can use dict((i, my_eval(j)) for i,j in d.items()) for older versions
        return func(*cp, **dp)

    return wrapped

@autocaste
def f(a, b):
    return a + b

print(f("3.4", "1")) # 4.4
print(f("s", "sd"))  # ssd
print(my_eval("True")) # True
print(my_eval("None")) # None
print(my_eval("[1, 2, (3, 4)]")) # [1, 2, (3, 4)]

Ответ 3

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

@signature(int, str, int)
func(x, y, z):
    ...

Такой декоратор можно построить довольно легко. Что-то вроде этого (EDIT - работает!):

def signature(*args, **kwargs):
    def decorator(fn):
        def wrapped(*fn_args, **fn_kwargs):
            new_args = [t(raw) for t, raw in zip(args, fn_args)]
            new_kwargs = dict([(k, kwargs[k](v)) for k, v in fn_kwargs.items()])

            fn(*new_args, **new_kwargs)

        return wrapped

    return decorator

И так же, вы можете теперь наполнять функции сигнатурами типов!

@signature(int, int)
def foo(x, y):
    print type(x)
    print type(y)
    print x+y

>>> foo('3','4')
<type: 'int'>
<type: 'int'>
7

В принципе, это тип-явная версия метода @utdemir.

Ответ 4

Если вы разбираете аргументы из командной строки, вы должны использовать argparse модуль (если вы используете Python 2.7).

Каждый аргумент может иметь ожидаемый тип, поэтому знание того, что с ним делать, должно быть относительно простым. Вы даже можете определить свои собственные типы.

... довольно часто строка командной строки должна интерпретироваться как другой тип, такой как float или int. Аргумент ключевого слова типа add_argument() позволяет выполнять любые необходимые проверки типов и преобразования типов. Общие встроенные типы и функции могут использоваться непосредственно как значение аргумента типа:

parser = argparse.ArgumentParser()
parser.add_argument('foo', type=int)
parser.add_argument('bar', type=file)
parser.parse_args('2 temp.txt'.split())
>>> Namespace(bar=<open file 'temp.txt', mode 'r' at 0x...>, foo=2)

Ответ 5

В вашем фрагменте есть несколько проблем.

#first test bools
if var == 'True':
        return True
elif var == 'False':
        return False

Это всегда будет проверяться на True, потому что вы тестируете строки 'True' и 'False'.

В python нет автоматического принуждения типов. Ваши аргументы, когда вы получаете через *args и **kwargs, могут быть любыми. Сначала будет искать список значений (каждый из которых может быть любым типом данных, примитивным и сложным), а второй будет искать сопоставление (с возможным возможным действительным отображением). Поэтому, если вы напишете декоратор, вы получите хороший список проверок ошибок.

Обычно, если вы хотите отправить строку str, только когда вызывается функция, введите ее в строку через (str) и отправьте ее.

Ответ 6

Я знаю, что я пришел поздно в этой игре, но как насчет eval?

def my_cast(a):
try:
    return eval(a)
except:
    return a

или, альтернативно (и более безопасно):

from ast import literal_eval

def mycast(a):
  try:
    return literal_eval(a)
  except:
    return a