一、文档字符串
1)Python是文档字符串。Documentation Strings。
在函数语句块的第一行,且习惯是多行的文本,所以多行使用三引号。
惯例是首字母大写。第一行写概述,空一行,第三行写详细描述,
可以使用特殊属性__doc__ 访问这个文档。
必须写在第一行。
#
def add(x,y):
"""This is s function of addition""" 文档字符串。
a=x+y
return x+y
print("name={} \n doc={}".format(add.__name__,add.__doc__))
print(help(add))
name=add
doc=This is s function of addition
Help on function add in module __main__:
add(x, y)
This is s function of addition
None
2)存在副作用,因为原函数对象的属性都被替换了。
3)#第一次代码
import datetime
import time
def copy_properties(src,dest):
dest.__name__ = src.__name__
dest.__doc__ = src.__doc__
def logger(fn):
def wrapper(*args,**kwargs):
print("args={},kwargs={}".format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
duration = datetime.datetime.now() - start
print("function{}took{}s.".format(fn.__name__,duration.total_seconds()))
return ret
copy_properties(fn,wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
"""this is a add function"""
print("===call add =======")
time.sleep(2)
return x+y
print(add(4,y=1),add.__name__,add.__doc__)
#第二次代码(带参装饰器)
import datetime
import time
def copy_properties(src):
def _inner(dest):
dest.__name__ = src.__name__
dest.__doc__ = src.__doc__
return dest
return _inner
def logger(fn):
@copy_properties(fn)
def wrapper(*args,**kwargs):
"""I am wrapper"""
print("args={},kwargs={}".format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
duration = datetime.datetime.now() - start
print("function{}took{}s.".format(fn.__name__,duration.total_seconds()))
return ret
# copy_properties(fn,wrapper)
return wrapper
@logger #add = logger(add)
def add(x,y):
"""this is a add function"""
print("===call add =======")
time.sleep(2)
return x+y
print(add(4,y=1),add.__name__,add.__doc__)
标示符和名称并不是一一对应的。
3)带参装饰器
通过copy_properties函数将被包装函数的属性覆盖掉包装函数。
凡是被装饰的函数都需要复制这些属性,这个函数很通用。
可以将复制属性的函数构建成装饰器函数,带参装饰器。(带参装饰器进行柯里化)。
本质:装饰器函数,装饰别的函数,增强别的(业务函数)函数功能。
#带参装饰器代码
import datetime
import time
def copy_properties(src):
def _inner(dest):
dest.__name__ = src.__name__
dest.__doc__ = src.__doc__
return dest
return _inner
def logger(durtion)
def _logger(fn):
@copy_properties(fn)
def wrapper(*args,**kwargs):
"""I am wrapper"""
print("args={},kwargs={}".format(args,kwargs))
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
print(duration)
if delta>duration:
print('low')
else:
print('fast')
#print("function{}took{}s.".format(fn.__name__,duration.total_seconds()))
return ret
# copy_properties(fn,wrapper)
return wrapper
return _logger
@logger(5) #add = logger(add)
def add(x,y):
"""this is a add function"""
print("===call add =======")
time.sleep(2)
return x+y
print(add(4,y=1),add.__name__,add.__doc__)
4) 带参装饰器:是一个函数。函数作为他的形参,返回值是一个不带参的装饰器函数。
使用@functionname(参数列表)方式调用,
可以看做是在装饰器外层又加了一层函数。
函数作为形参。
34、functools模块。
Functools.update_wrapper(wrapper,wrappered,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_WPDATES)
类似copy_properties功能
Wrapper包装函数、被更新者,wrapper被包装函数、数据源。
元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性。
'__module__', '__name__', '__qualname__', '__doc__', '__annotations__'
模块名、名称、限定名、文档、参数注解
元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典。
增加一个__wrapped__属性,保留着wrapped函数。
#代码块
import functools,time,datetime
def logger(durtion,func=lambda name,durtion:print('{}took{}s'.format(name,durtion))):
def _logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
n = fn(*args,**kwargs)
delta = (datetime.datetime.now() - start).total_seconds()
if delta > durtion:
func(fn.__name__,duration)
return n
return functools.update_wrapper(wrapper,fn)
return _logger
@logger(5) #add = logger(5)(add)
def add(x,y):
time.sleep(1)
return x+y
print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep='\n')
二、类型注解
1)函数定义的弊端。
Python是动态语言,变量可以被赋值,且赋值为不同类型。
难发现,由于不做任何类型检查,直到运行期问题才显现出来,或者线上运行才可以发现。
难使用:函数的使用者看到这个函数的时候,并不知道其函数设计,并不知道应该传入什么类型的参数。
2)如何解决弊端
(1)增加文档注释。(弊端:函数定义更新了,文档未必同步更新).使用双的三引号。
(2)函数注解:只是一个提示性文件。对函数参数进行类型注解。对函数参数做一个辅助的说明,并不对函数参数进行类型检查。第三方工具,做代码分析,发现隐藏的bug。
.__annotations__。
def add(x,y):
"""
:param x:
:param y:
:return: int
"""
return x+y
print(help(add))
(3)3.6.3进行的变量注解。I:int = 3
3)业务应用;函数参数类型检查
(1)函数参数检查,一定是在函数外。
函数应该作为参数,传入到检查函数中。
检查函数拿到函数传入的实际参数。与形参声明进行对比。
__annotations__属性是一个字典,其中包括返回值类型的声明使用inspect模块。
(2)inspect。提供获取对象信息的函数,可以检查函数的类、类型检查。
import inspect
def add(x:int,y:int,*args,**kwargs):
return x+y
sig = inspect.signature(add)
print(sig,type(sig))
print('params:',sig.parameters)
print('return:',sig.return_annotation)
print(sig.parameters['y'],type(sig.parameters['y']))
print(sig.parameters['x'])
print(sig.parameters['args'])
print(sig.parameters['args'].annotation)
print(sig.parameters['kwargs'])
print(sig.parameters['kwargs'].annotation)
#第一个print:(x:int, y:int, *args, **kwargs) <class 'inspect.Signature'> sig表现出来的是对象,对象是signature类型
#第二个print:params: OrderedDict([('x', <Parameter "x:int">), ('y', <Parameter "y:int">), ('args', <Parameter "*args">), ('kwargs', <Parameter "**kwargs">)]) 类型是映射代理 ,返回的是有序的字典。Orderdict。
#第三个print:return: <class 'inspect._empty'> 通过__annotation查看的是声明的类型。
#第四个print:y:int <class 'inspect.Parameter'> 返回的是一个类
#第五个print:x:int 返回的是一个类
#第六个print:*args 返回的是一个类
#第七个print:<class 'inspect._empty'> 表示声明的类型,未声明表示空
#第八个print:**kwargs 返回的是一个类
#第九个print:<class 'inspect._empty'> 表示声明的类型,未声明表示空
4)模块提供的信息:
Signature(callable)获取签名,(函数签名包含了一个函数的信息包括函数名、参数类型,所在的类和名称空间及其其他信息)
Def add(x,y): add(x,y)就是函数的签名:
(1)inspect.signature(callable,*,follow_wrapped=True)
(2)Params=sig.parameters()
可变类型收集的就是不同类型数据的,所有后面没有必要加类型注解。
5)模块提供的信息
inspect.isfunction(add) #是否是函数
inspect.ismethod(add) #是否是类方法
inspect.isgenerator(add) #是否是生成器对象
inspect.isgeneratorfunction(add) #是否是生成器函数
inspect.isclass(add) #是否是类
inspect.ismodule(inspect) #是否是模块
inspect.isbuiltin(print) #是否是内建对象
6)Parameter对象
保存在元组中,只是只读的。
name参数的名字。
annotaion参数的注解,可能没有定义。
Default参数的缺省值,可能没有定义。
empty,特殊得类,用来标记default属性或者注释annotation属性的空值。
Kind实参如何绑定到形参,就是形参的类型。
POSITIONAL_ONLY ,值必须是位置参数提供
POSITIONAL_OR_KEYWORD,值可以作为关键字或者位置参数提供。
VAR_POSITIONAL,可变位置参数,对应*args
KEYWORD_ONLY,keyword-noly参数,对应*或者*args之后出现的非可变关键字参数。
VAR_KEYWORD,可变关键字参数,对应**kwargs。
(1) #课堂例子:
import inspect
def add(x,y:int=1,*args,z,t=10,**kwargs):
return x+y
sig = inspect.signature(add)
print(sig)
print('params:',sig.parameters)
print('return:',sig.return_annotation)
print('~~~~~~')
for i,item in enumerate(sig.parameters.items()):
name,param = item
print(i+1,name,param.annotation,param.kind,param.default)
print(param.default is param.empty,end='\n\n')
(x, y:int=1, *args, z, t=10, **kwargs)
params: OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int=1">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)])
return: <class 'inspect._empty'>
~~~~~~
1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
True
2 y <class 'int'> POSITIONAL_OR_KEYWORD 1
False
3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'>
True
4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'>
True
5 t <class 'inspect._empty'> KEYWORD_ONLY 10
False
6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'>
True
(2) 业务应用
有函数如下
def add(x, y:int=7) -> int:
return x + y
请检查用户输入是否符合参数注解的要求
#第一步代码:解决位置参数(传入的参数为位置参数)
思路:1,为了不侵入原来代码,所以使用,装饰器。
2,导入inspect模块。
3,利用sig获取函数签名(add)
4,利用sig.paramters(fn)获取对象,是一个有序的字典。
5,字典里面的k放在字典里面,为list(params.keys())。
6,字典里面的v值放在字典里面,作为找一个列表,list(paramts.values())
7,用户输入的值利用迭代器取出,判断用户输入的values是否和定义的类型一样。
8,如果为真,打印。
import inspect
def check(fn):
#@funtools.wraps(fn)
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters #有序字典
#keys = [x for x in params.keys()]
keys = list(params.keys())
values = list(params.values()) #参数对象列表
for i,val in enumerate(args):
if isinstance(val,values[i].annotation):
print(keys[i],'==',val)
#n = fn(*args,**kwargs)
return fn(*args,**kwargs)
return wrapper
@check
def add(x:int,y:int=7)->int:
return x + y
print(add(4,5))
#第二步代码,解决关键词参数传参。(传入的实参采用关键字)
思路:1,迭代,k,v inkwargs.Items()迭代传参后的字典。
2,判断类型v的类型,与签名得到的有序字典的类型判断是否一致。
3,类型一致就进行打印。
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters #有序字典
keys = list(params.keys()) #定义的
values = list(params.values())# 定义的
for i,val in enumerate(args): #形参和实参声明一致
if isinstance(val,values[i].annotation):
print(keys[i],'==',val)
for k,v in kwargs.items():
if isinstance(v,params[k].annotation):
print(k,'====',v)
return fn (*args,**kwargs)
return wrapper
@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))
#第三步,解决传参为位置参数和关键字参数混合传参。
import inspect
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
values = list(params.values())
for i,val in enumerate(args):
if isinstance(val,values[i].annotation):
print(keys[i],'==',val)
for k,v in kwargs.items(): #迭代定义的传参后的字典,
if isinstance(v,params[k].annotation): #判断传参后的v与定义的比较。
print(k,'===',v)
return fn(*args,**kwargs)
return wrapper
@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))
#第四步;解决没有注解的不进行比较的问题:
思路:采用短路与解决,如果不是定义类型的,就不会进行下一步比较、
import inspect
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
values = list(params.values())
for i,val in enumerate(args):
if values[i].annotation is not inspect._empty and isinstance(val,values[i].annotation):
print(keys[i],'==',val)
for k,v in kwargs.items(): #迭代定义的传参后的字典,
if params[k].annotation is not params[k].empty and isinstance(v,params[k].annotation): #判断传参后的v与定义的比较。
print(k,'===',v)
return fn(*args,**kwargs)
return wrapper
@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))
Inpsect模块主要是做检查,检查函数,缺省值等。