Ищите идиоматический способ оценки False, если аргумент False в Python 3
У меня есть цепочка функций, каждая из которых определена в другом месте класса:
fus(roh(dah(inp)))
где inp
- либо словарь, либо bool(False)
.
Желаемый результат состоит в том, что если inp
или любая из функций, оцененных в False
, False
возвращается стеком функций.
Я попытался использовать тернарные операторы, но они не оцениваются правильно.
def func(inp):
return int(inp['value']) + 1 if inp else False
выдает TypeError, bool не подлежит расшифровке, если i == False
, потому что inp['value']
оценивается перед условным.
Я знаю, что могу сделать это явно:
def func(inp):
if inp == False:
return False
else:
return inp['value'] + 1
но есть тонна функций, и это будет почти четверть длины моего кода. Он также переписывает одни и те же строки кода снова и снова, что говорит мне о том, что это неправильный способ сделать что-то.
Я подозреваю, что декоратор с аргументами является ответом, но чем больше я играю с ним, тем менее я уверен в этом.
def validate_inp(inp):
def decorator(func):
def wrapper(*args):
return func(inp) if inp else False
return wrapper
return decorator
@validate_inp(inp)
def func(inp):
return int(inp['value']) + 1
К сожалению, вызов декоратора вызывает значение NameError, 'inp' не определен. Но я не уверен, что неправильно использую декоратор, или декоратор - неправильное решение.
Ищите комментарий, критику, предложение и/или проверку здравомыслия.
Если вы нашли это, пытаясь решить вашу собственную проблему...
Вероятно, вы хотите использовать пустые словари вместо логического False. Подставки к @chepner.
В моем приложении использование False было "нормально", не предлагало никаких преимуществ и вызывало некоторые короткие блоки кода.
Я нашел, что все проще, используя пустой словарь. Я обертываю функции, которые используют dict с декоратором, который улавливает KeyError, созданный ссылкой dict['value']
, где dict
пуст.
Ответы
Ответ 1
Декоратор должен выглядеть так:
def validate_inp(fun):
def wrapper(inp):
return fun(inp) if inp else False
return wrapper
@validate_inp
def func(inp):
return int(inp['value']) + 1
print(func(False))
print(func({'value': 1}))
Если вы хотите использовать свой декоратор с членом класса:
def validate_inp(fun):
def wrapper(self, inp):
return fun(self, inp) if inp else False
return wrapper
class Foo(object):
@validate_inp
def func(self, inp):
return int(inp['value']) + 1 if inp else False
foo = Foo()
print(foo.func(False))
print(foo.func({'value': 1}))
Ответ 2
Я попытался использовать тернарные операторы, но они не оцениваются правильно.
def func(inp):
return int(inp['value']) + 1 if inp else False
выдает TypeError, bool не подлежит расшифровке, если i == False
, потому что inp['value']
оценивается перед условным.
Это неверно - этот код работает. Кроме того, вы можете просто написать
def func(inp):
return inp and (int(inp['value']) + 1)
Чтобы автоматически обернуть такие функции, создайте функцию, которая обертывает функцию:
def fallthrough_on_false(function):
def inner(inp):
return inp and function(inp)
return inner
Это должно быть улучшено с помощью functools.wraps
для переноса декораторов и имен, и, вероятно, оно должно принимать переменное количество аргументов, чтобы разрешить дополнительные расширения:
from functools import wraps
def fallthrough_on_false(function):
@wraps(function)
def inner(inp, *args, **kwargs):
return inp and function(inp, *args, **kwargs)
return inner
Ответ 3
Если вы передаете значение непосредственно декоратору, вы не должны его параметризовать. В вашем случае inp
фактически передается функции, а не декоратору. Таким образом, реализация становится такой:
>>> def validate_inp(f):
... def wrapper(inp):
... if not inp:
... return False
... return f(inp)
... return wrapper
...
>>> @validate_inp
... def func(inp):
... return int(inp['value']) + 1
...
>>> func(False)
False
>>> func({'value': 1})
2
Эти две строки
@validate_inp
def func(inp):
можно понять как это
func = validate_inp(func)
Итак, func
- фактически функция wrapper
, возвращаемая функцией validate_inp
. С этого момента всякий раз, когда вы вызываете func
, вызывается wrapper
, а inp
будет передаваться только функции wrapper
. Затем wrapper
будет решать, следует ли вызывать фактический func
или нет, в зависимости от значения inp
.
Если вы хотите реализовать один и тот же декоратор в классе, вам просто нужно учитывать первый параметр self
в функции wrapper
. Вот оно.
>>> class Test(object):
...
... def validate_inp(fun):
... def wrapper(self, inp):
... if not inp:
... return False
... return fun(self, inp)
... return wrapper
...
... @validate_inp
... def func(self, inp):
... return int(inp['value']) + 1
...
...
>>> Test().func(False)
False
>>> Test().func({'value': 1})
2
Так как wrapper
является фактическим func
, он также принимает self
и inp
. И когда вы вызываете функцию f
(которая является фактической func
), вам просто нужно передать self
в качестве первого параметра.
Ответ 4
Это, вероятно, не то, что вы искали, но помогают ли они вам?
1.
Используйте словарь .get вместо []
. Здесь вы можете определить значение возврата. Например.
In [1548]: inp
Out[1548]: {'6': 'Hi'}
In [1549]: inp.get('5',99)
Out[1549]: 99
-
isinstance
может использоваться, чтобы проверить, является ли переменная словарем.
In [1550]: isinstance(inp, dict)
Out[1550]: True
Объединяя их (inp - тот же словарь, что и выше)
In [1554]: print "True" if isinstance(inp, dict) and len(inp.keys()) else "False"
True
Ответ 5
Один из вариантов может заключаться в определении настраиваемого исключения и небольшой оболочки:
class FalseInput(Exception): pass
def assert_not_false(inp):
# I'll assume `inp` has to be precisely False,
# and not something falsy like an empty dictionary.
if inp is False:
raise FalseInput
return inp
Измените каждую из своих функций, чтобы поднять то же исключение вместо возврата False. Затем просто поймайте исключение один раз, в верхней части стека вызовов, но сначала заверните ввод.
try:
x = fus(roh(dah(assert_not_false(inp))))
except FalseInput:
x = False
Это может быть более эффективным, так как вам не обязательно будет вызывать все функции; если inp
начинается с False
, assert_not_false
немедленно вызовет исключение, и вы перейдете прямо к предложению except
.
Ответ 6
Еще один вариант: вспомогательная функция, которая принимает начальное значение и функции и применяет функции до тех пор, пока не встречается False
:
def apply(x, *functions):
for func in functions:
if x is False:
return x # or break
x = func(x)
return x
outp = apply(inp, dah, roh, fus)