一、文档字符串

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模块主要是做检查,检查函数,缺省值等。