Автоматически введите параметры приведения в Python - PullRequest
13 голосов
/ 11 августа 2011

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

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

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

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')      

РЕДАКТИРОВАТЬ: Для тех, кто приходит сюда и хочет обновленную и краткую рабочую версию, перейдите сюда:

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')

Ответы [ 6 ]

15 голосов
/ 11 августа 2011

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

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, чтобы принимать другие логические значения, если хотите.

6 голосов
/ 11 августа 2011

Вы можете просто использовать простой 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: Как отметил комментарий Неда Батчелдера, он не будет принимать строки без кавычек, поэтому я добавил обходной путь, а также пример об автокаст-декораторе с аргументами ключевых слов.

import ast

def my_eval(s):
    try:
        return ast.literal_eval(s)
    except ValueError: #maybe it's 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)]
4 голосов
/ 11 августа 2011

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

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

Такой декоратор можно построить довольно легко.Примерно так (РЕДАКТИРОВАТЬ - работает!):

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.

2 голосов
/ 11 августа 2011

Если вы анализируете аргументы из командной строки, вы должны использовать модуль 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)
0 голосов
/ 01 октября 2015

Я знаю, что опоздал на эту игру, но как насчет 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
0 голосов
/ 11 августа 2011

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

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

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

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

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

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