Python入门篇-类型注解

              Python入门篇-类型注解

                                      作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

 

 

一.函数定义的弊端

1>.动态语言很灵活,但是这种特性也是弊端

  Python是动态语言,变量随时可以被赋值,且能赋值为不同的类型
  Python不是静态编译型语言,变量类型是在运行器决定的
  动态语言很灵活,但是这种特性也是弊端
    def add(x, y):
      return x + y
    print(add(4, 5))
    print(add('hello', 'world'))
    add(4, 'hello')   难发现:由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行时才能暴露出问题
  难使用:函数的使用者看到函数的时候,并不知道你的函数的设计,并不知道应该传入什么类型的数据

2>.如何解决这种动态语言定义的弊端

增加文档Documentation String
  这只是一个惯例,不是强制标准,不能要求程序员一定为函数提供说明文档
  函数定义更新了,文档未必同步更新
    def add(x, y):
      '''
      :param x: int
      :param y: int
      :return: int
    '''
    return x + y
    print(help(add))

3>.函数注解

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 
 8 def add(x:int , y:int) -> int :
 9     '''
10     :param x: int
11     :param y: int
12     :return: int
13     '''
14     return x + y
15 
16 print(help(add))
17 print(add(4, 5))
18 print(add('Yin', 'zhengjie'))
19 
20 
21 
22 
23 #以上代码执行结果如下:
24 Help on function add in module __main__:
25 
26 add(x:int, y:int) -> int
27     :param x: int
28     :param y: int
29     :return: int
30 
31 None
32 9
33 Yinzhengjie

 

 

二.函数注解Function Annotations

1>.函数注解

  Python 3.5引入
  对函数的参数进行类型注解
  对函数的返回值进行类型注解
  只对函数参数做一个辅助的说明,并不对函数参数进行类型检查
  提供给第三方工具,做代码分析,发现隐藏的bug
  函数注解的信息,保存在__annotations__属性中

2>.变量注解

Python 3.6引入变量注解
  i : int = 3

 

三.业务应用-函数参数类型检查

1>.思路

  函数参数的检查,一定是在函数外
  函数应该作为参数,传入到检查函数中
  检查函数拿到函数传入的实际参数,与形参声明对比
  __annotations__属性是一个字典,其中包括返回值类型的声明。假设要做位置参数的判断,无法和字典中的声明对应。使用inspect模块

2>.inspet模块

 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 """
 9 signature(callable):
10     获取签名(函数签名包含了一个函数的信息,包括函数名、它的参数类型、它所在的类和名称空间及其他信息)
11 """
12 
13 def add(x:int, y:int, *args,**kwargs) -> int:
14     return x + y
15 
16 print(add.__annotations__)                 #不推荐使用它,返回的结果是无序的
17 
18 sig = inspect.signature(add)
19 print(sig, type(sig))                        # 函数签名
20 print('params : ', sig.parameters)          # OrderedDict,即返回的结果是一个有序字典
21 print('return : ', sig.return_annotation)
22 print(sig.parameters['y'], type(sig.parameters['y']))
23 print(sig.parameters['x'].annotation)
24 print(sig.parameters['args'])
25 print(sig.parameters['args'].annotation)
26 print(sig.parameters['kwargs'])
27 print(sig.parameters['kwargs'].annotation)
28 print("是否是函数:{}".format(inspect.isfunction(add)))
29 print("是否是类的方法:{}".format(inspect.ismethod(add)))
30 print("是否是生成器对象:{}".format(inspect.isgenerator(add)))
31 print("是否是生成器函数:{}".format(inspect.isgeneratorfunction(add)))
32 print("是否是类:{}".format(inspect.isclass(add)))
33 print("是否是模块:{}".format(inspect.ismodule(inspect)))
34 print("是否是内建对象:{}".format(inspect.isbuiltin(print)))            #还有很多is函数,需要的时候查阅inspect模块帮助
35 
36 
37 
38 
39 #以上代码执行结果如下:
40 {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}
41 (x:int, y:int, *args, **kwargs) -> int <class 'inspect.Signature'>
42 params :  OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)])
43 return :  <class 'int'>
44 y:int <class 'inspect.Parameter'>
45 <class 'int'>
46 *args
47 <class 'inspect._empty'>
48 **kwargs
49 <class 'inspect._empty'>
50 是否是函数:True
51 是否是类的方法:False
52 是否是生成器对象:False
53 是否是生成器函数:False
54 是否是类:False
55 是否是模块:True
56 是否是内建对象:True
 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 """
 9 Parameter对象
10     保存在元组中,是只读的
11     name,参数的名字
12     annotation,参数的注解,可能没有定义
13     default,参数的缺省值,可能没有定义
14     empty,特殊的类,用来标记default属性或者注释annotation属性的空值
15     kind,实参如何绑定到形参,就是形参的类型
16         POSITIONAL_ONLY,值必须是位置参数提供
17         POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供
18         VAR_POSITIONAL,可变位置参数,对应*args
19         KEYWORD_ONLY,keyword-only参数,对应*或者*args之后的出现的非可变关键字参数
20         VAR_KEYWORD,可变关键字参数,对应**kwargs
21 """
22 
23 def add(x, y:int=7, *args, z, t=10,**kwargs) -> int:
24     return x + y
25 
26 sig = inspect.signature(add)
27 print(sig)
28 print('params : ', sig.parameters) # 有序字典
29 print('return : ', sig.return_annotation)
30 print("*" * 20 + "我是分割线" + "*" * 20)
31 
32 for i, item in enumerate(sig.parameters.items()):
33     name, param = item
34     print(i+1, name, param.annotation, param.kind, param.default)
35     print(param.default is param.empty, end='\n\n')
36 
37 
38 
39 #以上代码执行结果如下:
40 (x, y:int=7, *args, z, t=10, **kwargs) -> int
41 params :  OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int=7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])
42 return :  <class 'int'>
43 ********************我是分割线********************
44 1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
45 True
46 
47 2 y <class 'int'> POSITIONAL_OR_KEYWORD 7
48 False
49 
50 3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>
51 True
52 
53 4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>
54 True
55 
56 5 t <class 'inspect._empty'> KEYWORD_ONLY 10
57 False
58 
59 6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>
60 True
Parameter对象

3>.小试牛刀

有函数如下
  def add(x, y:int=7) -> int:
    return x + y
  
请检查用户输入是否符合参数注解的要求?

思路
  调用时,判断用户输入的实参是否符合要求
  调用时,用户感觉上还是在调用add函数
  对用户输入的数据和声明的类型进行对比,如果不符合,提示用户
 1 #!/usr/bin/env python
 2 #_*_coding:utf-8_*_
 3 #@author :yinzhengjie
 4 #blog:http://www.cnblogs.com/yinzhengjie/tag/python%E8%87%AA%E5%8A%A8%E5%8C%96%E8%BF%90%E7%BB%B4%E4%B9%8B%E8%B7%AF/
 5 #EMAIL:y1053419035@qq.com
 6 
 7 import inspect
 8 
 9 def check(fn):
10     def wrapper(*args, **kwargs):
11         sig = inspect.signature(fn)
12         params = sig.parameters
13         values = list(params.values())
14         for i,p in enumerate(args):
15             param = values[i]
16             if param.annotation is not param.empty and not isinstance(p, param.annotation):
17                 print(p,'!==',values[i].annotation)
18         for k,v in kwargs.items():
19             if params[k].annotation is not inspect._empty and not isinstance(v, params[k].annotation):
20                 print(k,v,'!===',params[k].annotation)
21         return fn(*args, **kwargs)
22     return wrapper
23 
24 @check
25 def add(x, y:int=7) -> int:         #我们要求第二个参数必须是int类型,并且返回值类型也为int
26     return x + y
27 
28 print(add(2,1))
29 print(add(20,y=10))
30 print(add(y=100,x=200))
31 print(add("Yin","zhengjie"))        #我们在实际传参时故意不按照要求传参,发现会有相应的提示信息
32 
33 
34 
35 #以上代码执行结果如下:
36 3
37 30
38 300
39 zhengjie !== <class 'int'>
40 Yinzhengjie

 

posted @ 2019-06-04 07:16  尹正杰  阅读(3806)  评论(0编辑  收藏  举报