Jeśli nalegać na dodanie typu sprawdzanie do kodu, można zajrzeć do annotations i jak mogą uprościć co masz napisać. Jeden z questions na StackOverflow wprowadził małą, zaciemnioną weryfikację typu, korzystając z adnotacji. Oto przykład oparty na twoim pytaniu:
>>> def statictypes(a):
def b(a, b, c):
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
return c
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
>>> @statictypes
def orSearch(d: dict, query: dict) -> type(None):
pass
>>> orSearch({}, {})
>>> orSearch([], {})
Traceback (most recent call last):
File "<pyshell#162>", line 1, in <module>
orSearch([], {})
File "<pyshell#155>", line 5, in <lambda>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "<pyshell#155>", line 5, in <listcomp>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "<pyshell#155>", line 3, in b
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
TypeError: d should be <class 'dict'>, not <class 'list'>
>>> orSearch({}, [])
Traceback (most recent call last):
File "<pyshell#163>", line 1, in <module>
orSearch({}, [])
File "<pyshell#155>", line 5, in <lambda>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "<pyshell#155>", line 5, in <listcomp>
return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
File "<pyshell#155>", line 3, in b
if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
TypeError: query should be <class 'dict'>, not <class 'list'>
>>>
Możesz popatrzeć na sprawdzającego typ i zastanawiać się: "Co, u licha, to robi?" Postanowiłem sam się przekonać i przekształcić go w czytelny kod. Drugi szkic wyeliminował funkcję b
(można ją nazwać verify
). Trzeci i ostatni projekt wykonał kilka ulepszeń i jest pokazany na dole do użytku:
import functools
def statictypes(func):
template = '{} should be {}, not {}'
@functools.wraps(func)
def wrapper(*args):
for name, arg in zip(func.__code__.co_varnames, args):
klass = func.__annotations__.get(name, object)
if not isinstance(arg, klass):
raise TypeError(template.format(name, klass, type(arg)))
result = func(*args)
klass = func.__annotations__.get('return', object)
if not isinstance(result, klass):
raise TypeError(template.format('return', klass, type(result)))
return result
return wrapper
Edit:
Minęło ponad cztery lata, ponieważ odpowiedź została napisana, a wiele się zmieniło w Pythonie od tego czasu. W wyniku tych zmian i rozwoju osobistego w języku, wydaje się korzystne ponowne przejrzenie kodu sprawdzającego typ i przepisanie go w celu skorzystania z nowych funkcji i udoskonalonej techniki kodowania. Dlatego wprowadzono następującą rewizję, która wprowadza kilka marginalnych ulepszeń do dekoratora funkcji statictypes
(obecnie zmienionej na nazwę static_types
).
#! /usr/bin/env python3
import functools
import inspect
def static_types(wrapped):
def replace(obj, old, new):
return new if obj is old else obj
signature = inspect.signature(wrapped)
parameter_values = signature.parameters.values()
parameter_names = tuple(parameter.name for parameter in parameter_values)
parameter_types = tuple(
replace(parameter.annotation, parameter.empty, object)
for parameter in parameter_values
)
return_type = replace(signature.return_annotation, signature.empty, object)
@functools.wraps(wrapped)
def wrapper(*arguments):
for argument, parameter_type, parameter_name in zip(
arguments, parameter_types, parameter_names
):
if not isinstance(argument, parameter_type):
raise TypeError(f'{parameter_name} should be of type '
f'{parameter_type.__name__}, not '
f'{type(argument).__name__}')
result = wrapped(*arguments)
if not isinstance(result, return_type):
raise TypeError(f'return should be of type '
f'{return_type.__name__}, not '
f'{type(result).__name__}')
return result
return wrapper
Nadal pracujesz z językiem o silnym typie ... dynamicznym! = Słabym – Brian
[Mówiąc o "mocno wpisanym" i "słabo wpisanym" ...] (http : //stackoverflow.com/a/9929697/395760) – delnan
Pytanie w tytule to idealne pytanie na SO i czy jest to efektywny/najlepszy sposób, sam masz odpowiedź. Jednak pytanie uzupełniające na końcu, które tam masz, jest bardziej http://programmers.stackexchange.com/ pytanie. – woozyking