Python学习:函数类型自动检查
2020-12-25 本文已影响0人
khaos
问题
python语言是一门易学易用的语言,不过相比起c++、java语言,就是读别人的代码,是比较痛苦的,尤其代码量比较大的情况,更是如此。
def add(a, b):
return a + b
print(10,20)
print('10','20')
问题的根本原因,就是python中一切皆是对象,函数丢失了类型信息,读起来相当痛苦,只有执行过程,才比较方便掌握代码。
解决方案
方法一,类型注释
python语言缔造者,也已经看出来了代码中没有类型信息的问题。当下的python3版本,已经提供了类型标记。比如:
def add(a:int, b:int)->int:
return a + b
上述代码中,add表示两个整数的和。不过,这个特性,可以理解为是一种注释,实际代码运行的时候,同样还可以传递字符串的参数。IDE可以利用这个能力,进行代码检查提示。
在python3.6滞后的版本,建议坚持用这种写法写python代码,毕竟代码还是要给阅读的,不带类型信息,读起来缺失很费劲。
方法二,类型装饰器
python有个装饰器特性,装饰器可以轻松实现面向切面的编程,比如函数的运行时间统计,函数出入口日志打印等等。代码如下:
from functools import wraps
from inspect import signature
def type_assert(*type_args, **type_kwargs):
def check(func):
sig = signature(func)
bound_types = sig.bind_partial(*type_args, **type_kwargs).arguments
@wraps(func)
def wrapper(*args, **kwargs):
bound_values = sig.bind(*args, **kwargs)
for name, value in bound_values.arguments.items():
if name not in bound_types:
continue
if not isinstance(value, bound_types[name]):
raise TypeError('argument {} must be {}'.format(
name, bound_types[name]))
return func(*args, **kwargs)
return wrapper
return check
测试代码如下:
# 可以不指定参数使用。@type_assert(int,int)
@type_assert(a=int, b=int))
def add(a, b):
return a + b
print(add(10, 10))
print(add(10.0, 10.0))
运行结果如下,类型信息在运行时得到有效检查。
20
Traceback (most recent call last):
File "/Users/xxx/data/code/python/type_assert.py", line 38, in <module>
print(add(10.0, 10.0))
File "/Users/xxx/data/code/python/type_assert.py", line 18, in wrapper
raise TypeError('argument {} must be {}'.format(
TypeError: argument a must be <class 'int'>
同样,类型type_assert还可以为参数指定多种类型,完全不用修改代码。比如:
@type_assert(a=(int, float), b=(int, float))
def add(a, b):
return a + b
print(add2(10, 10))
print(add2(10.0, 10.0))
并且,类型type_assert还可以用于类的方法上,进行类型检查。只不过,因为类的方法有个self参数,指定参数的时候需要写明参数名称(当然,self是不用指定类型)。比如:
class Math:
@type_assert(a=int, b=int)
def add(self, a, b):
return a + b
math = Math()
math.add(10, 20)
math.add(10.0, 20.0)
输出:
Traceback (most recent call last):
File "/Users/xxx/data/code/python/type_assert.py", line 40, in <module>
math.add(10.0, 20.0)
File "/Users/xxx/data/code/python/type_assert.py", line 18, in wrapper
raise TypeError('argument {} must be {}'.format(
TypeError: argument a must be <class 'int'>
这个方法看上去还比较不错,简单实用。但也不是万能的,不建议到处用,毕竟会降低性能,建议公开给外部调用的方法上使用类型断言。另外,对于类型检查比较严格时,是需要自己处理的。比如下面的代码:
@type_assert(a=(int, float, str), b=(int, float, str))
def add(a, b):
return a + b
add(10.0,'20.0')
这个运行时,因为类型不同引起的异常,type_assert是检查不出来的。如果要支持,就需要在add函数中检查不同类型,该如何处理了。