Se ti ostini a aggiungere il controllo di tipo al codice, si potrebbe voler esaminare annotations e come potrebbero semplificare ciò che avete da scrivere. Uno dei modelli questions su StackOverflow ha introdotto un correttore di testo piccolo e offuscato che sfrutta le annotazioni. Ecco un esempio in base alla tua domanda:
>>> 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'>
>>>
Si potrebbe guardare il tipo-checker e meraviglia, "Che diavolo è che facendo?" Ho deciso di scoprirlo e trasformarlo in codice leggibile. La seconda bozza ha eliminato la funzione b
(è possibile chiamarla verify
). Il terzo e ultimo progetto realizzato alcuni miglioramenti ed è mostrata in basso per il vostro uso:
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:
Sono passati più di quattro anni da quando questa risposta è stato scritto, e molto è cambiato in Python da quel momento. Come risultato di questi cambiamenti e della crescita personale del linguaggio, sembra utile rivisitare il codice di controllo dei tipi e riscriverlo per sfruttare le nuove funzionalità e la migliore tecnica di codifica. Pertanto, viene fornita la seguente revisione che apporta alcuni miglioramenti marginali al decoratore di funzioni statictypes
(ora rinominato 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
Stai ancora lavorando con un linguaggio fortemente tipizzato ... dinamico! = Debole – Brian
[Parlando di "fortemente tipizzato" e "debolmente digitato" ...] (http : //stackoverflow.com/a/9929697/395760) – delnan
La domanda nel titolo è una perfetta domanda SO, e se è il modo efficiente/migliore, tu hai la risposta da solo. Tuttavia la domanda successiva alla fine che hai lì, è più di una domanda http://programmers.stackexchange.com/. – woozyking