Я'м ищу эффективный способ, чтобы проверить переменные функции в Python. Например, Я'd, как проверить доводы типа и стоимости. Есть ли модуль для этого? Или я должен использовать что-то вроде декораторы, или какой-либо конкретный фразеологизм?
def my_function(a, b, c):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
В этом вытянутыми ответа, мы реализуем на Python 3.х-определенный тип проверки оформителя на основе ОПТОСОЗ 484-стиль тип подсказки в менее 275 линий чистом Python (большинство из которых пояснительные комментарии и замечания) – сильно оптимизированы для промышленного уровня реального использования в комплекте с пй.тест
-ориентированных тестов, осуществляющим все возможные крайние случаи.
Праздник на неожиданные офигенный медведь набрав:
>>> @beartype
... def spirit_bear(kermode: str, gitgaata: (str, int)) -> tuple:
... return (kermode, gitgaata, "Moksgm'ol", 'Ursus americanus kermodei')
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
AssertionError: parameter kermode=0xdeadbeef not of <class "str">
Этот пример показывает, медведя, вписав явно поддерживает проверку типов параметров и возвращаемых значений аннотированный как простые типы или кортежей таких типов. Golly!
О. К., Что'ы на самом деле впечатляют. @beartype
напоминает каждый other на Python 3.х-определенный тип проверки оформителя на основе ОПТОСОЗ 484-стиль тип подсказки в менее 275 строк чистом Python. Так что'ы, браток? руб.
Медведь набрав значительно более эффективные в пространстве и времени, чем все существующие реализации проверки типов в Python в меру моих ограниченных знаний в предметной области. (More об этом позже.)
Эффективность, как правило, не'т материи на Python, однако. Если это так, вы бы'т быть с помощью Python. Типа на самом деле проверка отклоняться от устоявшейся нормой избегать преждевременной оптимизации в Python? Да. Да, это делает.
Рассмотрим профилирования, который добавляет неизбежных накладных расходов для каждого профилированного метрика интерес (например, функции звонки, линий). Для того чтобы обеспечить точные результаты, эта нагрузка снижается за счет использования оптимизированных расширений с (например, _lsprof
с расширением мобилизованных cProfile
модуль), а не неоптимизированной чистом Python (например, "профиль" модулем). Эффективность действительно -- как раз важно при профилировании.
Проверка типа не отличается. Проверка типа добавляет накладные расходы на каждый вызов функции проверки типов приложений в идеале, все из них. Для предупреждения случайной (но, к сожалению, недалекий) коллег от снятия тип проверки вы тихо добавил после того, как в прошлую пятницу'с кофеином-протухли полуночнику, чтобы ваш гериатрический наследие в Django веб-приложение, типа проверка должна быть быстрой. Так быстро, что никто не замечает его's там, когда вы добавляете его, никого не предупредив. <суп>я делаю это все время! Прекратите читать это, если вы являетесь сотрудником_.</SUP и ГТ;
Если даже нелепой скорости это'т достаточно для прожорливых приложений, однако, медведь, вписав может быть глобально отключается при включении оптимизации в Python (например, путем передачи опция -o
, чтобы интерпретатор Python):
$ python3 -O
# This succeeds only when type checking is optimized away. See above!
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
(0xdeadbeef, 'People of the Cane', "Moksgm'ol", 'Ursus americanus kermodei')
Просто потому, что. Добро пожаловать на ношение печати.
Медведь набрав голый металл тип проверки – то есть проверка типа как можно ближе к ручной метод проверки типов в Python, как это возможно. Медведь набрав призван налагать штрафы производительность но, ограничения совместимости, или сторонних зависимостей (сверх тех, которые предусмотрены ручной подход, во всяком случае). Медведь ввода могут быть легко интегрированы в уже существующие программы и тестов без изменений.
Все'ы, наверное, знакомы с ручным подхода. Вы вручную утверждать
каждый параметр, передаваемый и/или возвращаемое значение возвращается из функции любую ранее в коде. Какой шаблон может быть проще и банальнее? Мы'вэ все это видели сто раз в раз сотрудники и вырвало немного в рот каждый раз, когда мы сделали. Повторение стареет быстро. Сухой, йо.
Сделать вашу блевотину сумки готова. Для краткости, позвольте'ы берут на себя функцию упрощенной easy_spirit_bear()
принимает единственный параметр, ул. единый`. Здесь's что ручной подход выглядит так:
def easy_spirit_bear(kermode: str) -> str:
assert isinstance(kermode, str), 'easy_spirit_bear() parameter kermode={} not of <class "str">'.format(kermode)
return_value = (kermode, "Moksgm'ol", 'Ursus americanus kermodei')
assert isinstance(return_value, str), 'easy_spirit_bear() return value {} not of <class "str">'.format(return_value)
return return_value
Питон 101, правильно? Многие из нас прошли этот урок.
Медведь набрав извлекает проверка типа выполняется вручную с помощью вышеуказанного подхода в динамически определенными функцию-оболочку, автоматически выполняя те же проверки, с дополнительным преимуществом привлечения зернистый ошибку TypeError
, а не исключения неоднозначного AssertionError
. Здесь's что автоматизированный подход выглядит так:
def easy_spirit_bear_wrapper(*args, __beartype_func=easy_spirit_bear, **kwargs):
if not (
isinstance(args[0], __beartype_func.__annotations__['kermode'])
if 0 < len(args) else
isinstance(kwargs['kermode'], __beartype_func.__annotations__['kermode'])
if 'kermode' in kwargs else True):
raise TypeError(
'easy_spirit_bear() parameter kermode={} not of {!r}'.format(
args[0] if 0 < len(args) else kwargs['kermode'],
__beartype_func.__annotations__['kermode']))
return_value = __beartype_func(*args, **kwargs)
if not isinstance(return_value, __beartype_func.__annotations__['return']):
raise TypeError(
'easy_spirit_bear() return value {} not of {!r}'.format(
return_value, __beartype_func.__annotations__['return']))
return return_value
Это'ы многословно. Но это's также в основном<суп>*</суп> так же быстро, как ручной подход. <суп>* Squinting предложил.</SUP и ГТ;
Обратите внимание на полное отсутствие функции проверки или итерации в оберточной функции, которая содержит аналогичное количество тестов как исходной функции, хотя и с дополнительными (возможно незначительным) стоимость тестирования ли и как параметры Тип проверил передаются при текущем вызове функции. Вы можете'т выиграть каждый бой. Может такой функции обертки actually быть надежно созданном для типа проверки произвольных функций в менее 275 строк чистом Python? Змея Plisskin говорит, "в реальной истории. Закурить есть?&от" И, да. У меня есть neckbeard.
Медведь бьет утку. Утка может летать, но медведь может бросить лосось на утку. <суп>тюнинг Канада, природа может удивить вас._</SUP и ГТ; Следующий вопрос.
Существующие решения не проанализировать голое железо проверки типа – по крайней мере, ни я'вэ grepped через. Все они многократно повторите проверку подписи типа-проверил функцию на вызов функции. Пока незначителен для одного вызова, проверками и необходимость их проведения надбавки, как правило, существенными в совокупности за все звонки. Такой на самом деле, really_ не ничтожно. Это's не просто касается эффективности, однако. Существующие решения часто не учитывают общие граничные случаи. Это включает большинство, если не все игрушки декораторов предоставляются как сайте StackOverflow ответы здесь и в других местах. Классические ошибки:
@checkargs
оформитель). isinstance()
встроенные. ошибку TypeError
на сбой проверки типа. Для детализации и вменяемость, проверка типа должна never поднять родовые исключения.
Медведь набрав добивается успеха там, где не несет неудачу. Все одно, все несут! Медведь набрав сдвиги в пространстве и времени стоит проверять сигнатуры функций Из время вызова функции в функции определения, то есть от функции оболочки, возвращаемой @beartype
декоратор в декоратор. Поскольку декоратор вызывается только один раз за определения функции, такая оптимизация дает лишний для всех.
Медведь печатать-это попытка иметь ваш проверка типа торт и съесть его тоже. Чтобы сделать это, @beartype
:
метод exec ()
. # If the active Python interpreter is *NOT* optimized (e.g., option "-O" was
# *NOT* passed to this interpreter), enable type checking.
if __debug__:
import inspect
from functools import wraps
from inspect import Parameter, Signature
def beartype(func: callable) -> callable:
'''
Decorate the passed **callable** (e.g., function, method) to validate
both all annotated parameters passed to this callable _and_ the
annotated value returned by this callable if any.
This decorator performs rudimentary type checking based on Python 3.x
function annotations, as officially documented by PEP 484 ("Type
Hints"). While PEP 484 supports arbitrarily complex type composition,
this decorator requires _all_ parameter and return value annotations to
be either:
* Classes (e.g., `int`, `OrderedDict`).
* Tuples of classes (e.g., `(int, OrderedDict)`).
If optimizations are enabled by the active Python interpreter (e.g., due
to option `-O` passed to this interpreter), this decorator is a noop.
Raises
----------
NameError
If any parameter has the reserved name `__beartype_func`.
TypeError
If either:
* Any parameter or return value annotation is neither:
* A type.
* A tuple of types.
* The kind of any parameter is unrecognized. This should _never_
happen, assuming no significant changes to Python semantics.
'''
# Raw string of Python statements comprising the body of this wrapper,
# including (in order):
#
# * A "@wraps" decorator propagating the name, docstring, and other
# identifying metadata of the original function to this wrapper.
# * A private "__beartype_func" parameter initialized to this function.
# In theory, the "func" parameter passed to this decorator should be
# accessible as a closure-style local in this wrapper. For unknown
# reasons (presumably, a subtle bug in the exec() builtin), this is
# not the case. Instead, a closure-style local must be simulated by
# passing the "func" parameter to this function at function
# definition time as the default value of an arbitrary parameter. To
# ensure this default is *NOT* overwritten by a function accepting a
# parameter of the same name, this edge case is tested for below.
# * Assert statements type checking parameters passed to this callable.
# * A call to this callable.
# * An assert statement type checking the value returned by this
# callable.
#
# While there exist numerous alternatives (e.g., appending to a list or
# bytearray before joining the elements of that iterable into a string),
# these alternatives are either slower (as in the case of a list, due to
# the high up-front cost of list construction) or substantially more
# cumbersome (as in the case of a bytearray). Since string concatenation
# is heavily optimized by the official CPython interpreter, the simplest
# approach is (curiously) the most ideal.
func_body = '''
@wraps(__beartype_func)
def func_beartyped(*args, __beartype_func=__beartype_func, **kwargs):
'''
# "inspect.Signature" instance encapsulating this callable's signature.
func_sig = inspect.signature(func)
# Human-readable name of this function for use in exceptions.
func_name = func.__name__ + '()'
# For the name of each parameter passed to this callable and the
# "inspect.Parameter" instance encapsulating this parameter (in the
# passed order)...
for func_arg_index, func_arg in enumerate(func_sig.parameters.values()):
# If this callable redefines a parameter initialized to a default
# value by this wrapper, raise an exception. Permitting this
# unlikely edge case would permit unsuspecting users to
# "accidentally" override these defaults.
if func_arg.name == '__beartype_func':
raise NameError(
'Parameter {} reserved for use by @beartype.'.format(
func_arg.name))
# If this parameter is both annotated and non-ignorable for purposes
# of type checking, type check this parameter.
if (func_arg.annotation is not Parameter.empty and
func_arg.kind not in _PARAMETER_KIND_IGNORED):
# Validate this annotation.
_check_type_annotation(
annotation=func_arg.annotation,
label='{} parameter {} type'.format(
func_name, func_arg.name))
# String evaluating to this parameter's annotated type.
func_arg_type_expr = (
'__beartype_func.__annotations__[{!r}]'.format(
func_arg.name))
# String evaluating to this parameter's current value when
# passed as a keyword.
func_arg_value_key_expr = 'kwargs[{!r}]'.format(func_arg.name)
# If this parameter is keyword-only, type check this parameter
# only by lookup in the variadic "**kwargs" dictionary.
if func_arg.kind is Parameter.KEYWORD_ONLY:
func_body += '''
if {arg_name!r} in kwargs and not isinstance(
{arg_value_key_expr}, {arg_type_expr}):
raise TypeError(
'{func_name} keyword-only parameter '
'{arg_name}={{}} not a {{!r}}'.format(
{arg_value_key_expr}, {arg_type_expr}))
'''.format(
func_name=func_name,
arg_name=func_arg.name,
arg_type_expr=func_arg_type_expr,
arg_value_key_expr=func_arg_value_key_expr,
)
# Else, this parameter may be passed either positionally or as
# a keyword. Type check this parameter both by lookup in the
# variadic "**kwargs" dictionary *AND* by index into the
# variadic "*args" tuple.
else:
# String evaluating to this parameter's current value when
# passed positionally.
func_arg_value_pos_expr = 'args[{!r}]'.format(
func_arg_index)
func_body += '''
if not (
isinstance({arg_value_pos_expr}, {arg_type_expr})
if {arg_index} < len(args) else
isinstance({arg_value_key_expr}, {arg_type_expr})
if {arg_name!r} in kwargs else True):
raise TypeError(
'{func_name} parameter {arg_name}={{}} not of {{!r}}'.format(
{arg_value_pos_expr} if {arg_index} < len(args) else {arg_value_key_expr},
{arg_type_expr}))
'''.format(
func_name=func_name,
arg_name=func_arg.name,
arg_index=func_arg_index,
arg_type_expr=func_arg_type_expr,
arg_value_key_expr=func_arg_value_key_expr,
arg_value_pos_expr=func_arg_value_pos_expr,
)
# If this callable's return value is both annotated and non-ignorable
# for purposes of type checking, type check this value.
if func_sig.return_annotation not in _RETURN_ANNOTATION_IGNORED:
# Validate this annotation.
_check_type_annotation(
annotation=func_sig.return_annotation,
label='{} return type'.format(func_name))
# Strings evaluating to this parameter's annotated type and
# currently passed value, as above.
func_return_type_expr = (
"__beartype_func.__annotations__['return']")
# Call this callable, type check the returned value, and return this
# value from this wrapper.
func_body += '''
return_value = __beartype_func(*args, **kwargs)
if not isinstance(return_value, {return_type}):
raise TypeError(
'{func_name} return value {{}} not of {{!r}}'.format(
return_value, {return_type}))
return return_value
'''.format(func_name=func_name, return_type=func_return_type_expr)
# Else, call this callable and return this value from this wrapper.
else:
func_body += '''
return __beartype_func(*args, **kwargs)
'''
# Dictionary mapping from local attribute name to value. For efficiency,
# only those local attributes explicitly required in the body of this
# wrapper are copied from the current namespace. (See below.)
local_attrs = {'__beartype_func': func}
# Dynamically define this wrapper as a closure of this decorator. For
# obscure and presumably uninteresting reasons, Python fails to locally
# declare this closure when the locals() dictionary is passed; to
# capture this closure, a local dictionary must be passed instead.
exec(func_body, globals(), local_attrs)
# Return this wrapper.
return local_attrs['func_beartyped']
_PARAMETER_KIND_IGNORED = {
Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD,
}
'''
Set of all `inspect.Parameter.kind` constants to be ignored during
annotation- based type checking in the `@beartype` decorator.
This includes:
* Constants specific to variadic parameters (e.g., `*args`, `**kwargs`).
Variadic parameters cannot be annotated and hence cannot be type checked.
* Constants specific to positional-only parameters, which apply to non-pure-
Python callables (e.g., defined by C extensions). The `@beartype`
decorator applies _only_ to pure-Python callables, which provide no
syntactic means of specifying positional-only parameters.
'''
_RETURN_ANNOTATION_IGNORED = {Signature.empty, None}
'''
Set of all annotations for return values to be ignored during annotation-
based type checking in the `@beartype` decorator.
This includes:
* `Signature.empty`, signifying a callable whose return value is _not_
annotated.
* `None`, signifying a callable returning no value. By convention, callables
returning no value are typically annotated to return `None`. Technically,
callables whose return values are annotated as `None` _could_ be
explicitly checked to return `None` rather than a none-`None` value. Since
return values are safely ignorable by callers, however, there appears to
be little real-world utility in enforcing this constraint.
'''
def _check_type_annotation(annotation: object, label: str) -> None:
'''
Validate the passed annotation to be a valid type supported by the
`@beartype` decorator.
Parameters
----------
annotation : object
Annotation to be validated.
label : str
Human-readable label describing this annotation, interpolated into
exceptions raised by this function.
Raises
----------
TypeError
If this annotation is neither a new-style class nor a tuple of
new-style classes.
'''
# If this annotation is a tuple, raise an exception if any member of
# this tuple is not a new-style class. Note that the "__name__"
# attribute tested below is not defined by old-style classes and hence
# serves as a helpful means of identifying new-style classes.
if isinstance(annotation, tuple):
for member in annotation:
if not (
isinstance(member, type) and hasattr(member, '__name__')):
raise TypeError(
'{} tuple member {} not a new-style class'.format(
label, member))
# Else if this annotation is not a new-style class, raise an exception.
elif not (
isinstance(annotation, type) and hasattr(annotation, '__name__')):
raise TypeError(
'{} {} neither a new-style class nor '
'tuple of such classes'.format(label, annotation))
# Else, the active Python interpreter is optimized. In this case, disable type
# checking by reducing this decorator to the identity decorator.
else:
def beartype(func: callable) -> callable:
return func
И leycec сказал: @beartype
произведет проверку типа быстрее, и это было так.
Нет ничего идеального. Даже медведь печатать._
Медведь набрав вовсе не типа проверьте параметры unpassed назначены значения по умолчанию. В теории, это могло. Но не в 275 линиях или меньше и уж точно не в ответе на StackOverflow. Сейф (...probably полностью unsafe) предполагается, что функция реализаторы утверждают, что они знали, что они делают, когда они определены значения по умолчанию. Поскольку значения по умолчанию, как правило, константы (...they'd лучше быть!), перепроверки типы констант, которые не меняются на каждый вызов функции назначены одно или несколько значений по умолчанию противоречило бы основополагающим принципом медведь набираете: "и Дон'т повторять себе снова и oooover и oooo-oooover снова и.и" Показать меня неправильно и я буду душ вы с голоса.
ОПТОСОЗ 484 ("и намеки типа„) формализованное использование аннотаций функция впервые введена в ОПТОСОЗ 3107 ("и функция аннотации на"). В Python 3.5 внешне поддерживает оформление нового верхнего уровня ввод
модуль, стандартный API для создания сколь угодно сложные типы из простых типов (например, вызвать[[Arg1Type, Arg2Type], Тип_возвращаемого_значения]
, типа, описывающая функция, принимающая два аргумента типа Arg1Type
и Arg2Type
и возвращает значение типа Тип_возвращаемого_значения
).
Медведь набрав поддерживает ни одну из них. В теории, это могло. Но не в 275 линиях или меньше и уж точно не в ответе на StackOverflow.
Медведь, набрав, тем не менее, профсоюзы поддерживают типов таким же образом, что isinstance (строение) поддерживает союзов типов: как кортежи. Этот внешне соответствует ввод.Тип союза – с очевидной оговоркой, что печатать.Союз поддерживает сколь угодно сложные типы, в то время как кортежи принято поддержка@beartype
только простые классы. В свою защиту скажу, 275 линий.
Здесь'ы суть его. <суп>сделать это, gist? Я'прекратим.</SUP и ГТ;
Что касается самой @beartype
декоратор, эти пй.тест
тесты могут быть интегрированы в существующие наборы тестов без изменений. Драгоценные, это'т его?
Теперь обязательное neckbeard разглагольствовать никто не просил.
В Python 3.5 не предоставляет фактической поддержка с использованием ПЭП 484 типов. wat? Это's садится: нет проверки типа, без определения типа, типа нет ничего'. Вместо этого, разработчики должны регулярно выполняете весь свой исходный код через супертяжелом стороннего переводчика с CPython фантики реализации факсимиле такой поддержки (например, mypy). Конечно, эти фантики ввести:
Наиболее подходящие для Python идиома четко document что функция ожидает, а потом просто стараюсь использовать все, что передается в функцию и либо разрешить исключения тиражирования или просто ловить ошибки и поднять вместо ошибку TypeError
. Тип-проверку следует избегать, поскольку это идет вразрез с утка-типирование. Тестирование значение может быть ОК – в зависимости от контекста.
Единственное место, где проверка действительно имеет смысл в системе или подсистеме точки входа, такие как веб-форм, аргументы командной строки и т. д. Везде, как долго, как ваши функции должным образом задокументировано, это'ы звонящего'ы ответственность, чтобы пройти соответствующие аргументы.
Правка: по состоянию на 2019 оказывается больше поддержки, используя аннотации типов и статическая проверка в Python; проверить ввод модуль mypy. Ответ 2013 следующее:
Проверка типа вообще не подходящие для Python. В Python, это более привычно использовать утиной типизацией. Пример:
В ВЫ код, предположим, что аргумент (в вашем примере "а") ходит как int
, а крякает как инт
. Например:
def my_function(a):
return a + 7
Это означает, что не только функции работы с числами, он также работает с поплавками и любой пользовательский класс Сдобавитьметод определен, тем меньше (иногда ничего) должно быть сделано если вы, или кто-то другой, хотите расширить свои функции для работы с чем-то еще. Однако, в некоторых случаях вам может понадобиться
инт`, тогда вы могли бы сделать нечто подобное:
def my_function(a):
b = int(a) + 7
c = (5, 6, 3, 123541)[b]
return c
а функция по-прежнему работает для любого "а", что определяет методинт`.
В ответ на ваши другие вопросы, я думаю, что это лучшее (как и другие ответы уже сказал, чтобы сделать это:
def my_function(a, b, c):
assert 0 < b < 10
assert c # A non-empty string has the Boolean value True
или
def my_function(a, b, c):
if 0 < b < 10:
# Do stuff with b
else:
raise ValueError
if c:
# Do stuff with c
else:
raise ValueError
Некоторые проверки типа декораторов я сделал:
import inspect
def checkargs(function):
def _f(*arguments):
for index, argument in enumerate(inspect.getfullargspec(function)[0]):
if not isinstance(arguments[index], function.__annotations__[argument]):
raise TypeError("{} is not of type {}".format(arguments[index], function.__annotations__[argument]))
return function(*arguments)
_f.__doc__ = function.__doc__
return _f
def coerceargs(function):
def _f(*arguments):
new_arguments = []
for index, argument in enumerate(inspect.getfullargspec(function)[0]):
new_arguments.append(function.__annotations__[argument](arguments[index]))
return function(*new_arguments)
_f.__doc__ = function.__doc__
return _f
if __name__ == "__main__":
@checkargs
def f(x: int, y: int):
"""
A doc string!
"""
return x, y
@coerceargs
def g(a: int, b: int):
"""
Another doc string!
"""
return a + b
print(f(1, 2))
try:
print(f(3, 4.0))
except TypeError as e:
print(e)
print(g(1, 2))
print(g(3, 4.0))
Один из способов-это использовать утверждать
:
def myFunction(a,b,c):
"This is an example function I'd like to check arguments of"
assert isinstance(a, int), 'a should be an int'
# or if you want to allow whole number floats: assert int(a) == a
assert b > 0 and b < 10, 'b should be betwen 0 and 10'
assert isinstance(c, str) and c, 'c should be a non-empty string'
Вы можете использовать проверку типа принимает/возвращает оформители с PythonDecoratorLibrary Это's очень легкий и читабельный:
@accepts(int, int, float)
def myfunc(i1, i2, i3):
pass
Существуют различные способы, чтобы проверить, что переменная в Python. Итак, перечислим некоторые:
isinstance(obj, а типа)
функция принимает переменную, в OBJ
и дает "истинный" это тот же тип тип
ты перечислил.
issubclass(объект, класс)
функция, которая принимает переменную в OBJ
, и дает true
, если в obj
подкласс класс
. Так, например, issubclass(Кролик, животное)даст вам
истинное` значение
hasattr
- еще один пример, демонстрируемый этой функции super_len
:
def super_len(o):
if hasattr(o, '__len__'):
return len(o)
if hasattr(o, 'len'):
return o.len
if hasattr(o, 'fileno'):
try:
fileno = o.fileno()
except io.UnsupportedOperation:
pass
else:
return os.fstat(fileno).st_size
if hasattr(o, 'getvalue'):
# e.g. BytesIO, cStringIO.StringI
return len(o.getvalue())
hasattr
склоняется больше к утка-типирование, и то, что, как правило, более pythonic но этот термин до самоуверенных.
Просто как Примечание, утверждать
утверждения обычно используются в тестировании, в противном случае просто использовать оператор if/else` иначе.
Я сделал довольно много исследований на эту тему недавно, так как я не был доволен многими библиотек я нашел там.
Я закончил разработку библиотеки для решения этого имени valid8. Как поясняется в документации, это значение для проверки в основном (хотя он поставляется в комплекте с простым типом валидации функций), и вы, возможно, пожелает связать его с PEP484 тип проверки, такие как исполнения или pytypes.
Таким образом можно выполнить проверку с valid8 один (и mini_lambda
на самом деле, чтобы определить логику проверки, но это не является обязательным) в вашем случае:
# for type validation
from numbers import Integral
from valid8 import instance_of
# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len
@validate_arg('a', instance_of(Integral))
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', instance_of(str), Len(s) > 0)
def my_function(a: Integral, b, c: str):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
# check that it works
my_function(0.2, 1, 'r') # InputValidationError for 'a' HasWrongType: Value should be an instance of <class 'numbers.Integral'>. Wrong value: [0.2].
my_function(0, 0, 'r') # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0) # InputValidationError for 'c' Successes: [] / Failures: {"instance_of_<class 'str'>": "HasWrongType: Value should be an instance of <class 'str'>. Wrong value: [0]", 'len(s) > 0': "TypeError: object of type 'int' has no len()"}.
my_function(0, 1, '') # InputValidationError for 'c' Successes: ["instance_of_<class 'str'>"] / Failures: {'len(s) > 0': 'False'}
И это же пример использования PEP484 тип подсказки и делегирование тип проверки, чтобы принудительно
:
# for type validation
from numbers import Integral
from enforce import runtime_validation, config
config(dict(mode='covariant')) # type validation will accept subclasses too
# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len
@runtime_validation
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', Len(s) > 0)
def my_function(a: Integral, b, c: str):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
# check that it works
my_function(0.2, 1, 'r') # RuntimeTypeError 'a' was not of type <class 'numbers.Integral'>
my_function(0, 0, 'r') # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0) # RuntimeTypeError 'c' was not of type <class 'str'>
my_function(0, 1, '') # InputValidationError for 'c' [len(s) > 0] returned [False].
Как правило, вы делаете что-то вроде этого:
def myFunction(a,b,c):
if not isinstance(a, int):
raise TypeError("Expected int, got %s" % (type(a),))
if b <= 0 or b >= 10:
raise ValueError("Value %d out of range" % (b,))
if not c:
raise ValueError("String was empty")
# Rest of function
Это проверяет тип входных аргументов при вызове функции:
def func(inp1:int=0,inp2:str="*"):
for item in func.__annotations__.keys():
assert isinstance(locals()[item],func.__annotations__[item])
return (something)
first=7
second="$"
print(func(first,second))
Также проверьте с секунда=9
(это должно дать ошибку утверждения)
def someFunc(a, b, c):
params = locals()
for _item in params:
print type(params[_item]), _item, params[_item]
Демо:
>> someFunc(1, 'asd', 1.0)
>> <type 'int'> a 1
>> <type 'float'> c 1.0
>> <type 'str'> b asd
подробнее о местные жители()
Если вы хотите сделать проверку на несколько функций, которые можно добавить логику внутри декоратора такой:
def deco(func):
def wrapper(a,b,c):
if not isinstance(a, int)\
or not isinstance(b, int)\
or not isinstance(c, str):
raise TypeError
if not 0 < b < 10:
raise ValueError
if c == '':
raise ValueError
return func(a,b,c)
return wrapper
и использовать его:
@deco
def foo(a,b,c):
print 'ok!'
Надеюсь, что это помогает!
Если вы хотите проверить **kwargs
, *аргументы
а также нормальных аргументов на одном дыхании, вы можете использовать местные жители()
функция, как первый оператор в определение функции, чтобы получить словарь аргументов.
Затем используйте тип ()
, чтобы изучить аргументы, например при переборе дикт.
def myfunc(my, args, to, this, function, **kwargs):
d = locals()
assert(type(d.get('x')) == str)
for x in d:
if x != 'x':
assert(type(d[x]) == x
for x in ['a','b','c']:
assert(x in d)
whatever more...
Это не решение для вас, но если вы хотите ограничить вызовы функций в отношении отдельных видов параметра, то вы должны использовать PROATOR { Python-функции валидатора прототип }. вы можете обратиться по следующей ссылке. https://github.com/mohit-thakur-721/proator
def myFunction(a,b,c):
"This is an example function I'd like to check arguments of"
if type( a ) == int:
#dostuff
if 0 < b < 10:
#dostuff
if type( C ) == str and c != "":
#dostuff