zoukankan      html  css  js  c++  java
  • python一等函数

    把函数视作对象

    在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:

    • 在运行时创建

    • 能赋值给变量或数据结构中的元素

    • 能作为参数传给函数

    • 能作为函数的返回结果

    说明python 函数是对象:这里创建了一个函数,然后调用它,读取它的 __doc__ 属性,并且确定函数对象本身是 function 类的实例。

    def factorial(n):
        '''returns n!'''
        return 1 if n < 2 else n * factorial(n - 1)
    
    print(factorial(5))    #调用
    print(help(factorial))   #打印__doc__属性
    print(type(factorial))   #打印类型
    
    #结果
    
    120
    Help on function factorial in module __main__:
    factorial(n)
    returns n!             #__doc__属性
    None
    <class 'function'>     #说明factorial是function类的一个实例

    说明函数是一等对象:通过别名使用函数(对应于第二点),把函数作为参数传递(对应于第三点)。

    #创建一个函数,只有函数在调用的时候才会运行
    def factorial(n):
        '''returns n!'''
        return 1 if n < 2 else n * factorial(n-1)
    
    fact = factorial
    print(fact)
    
    map_fact = map(factorial, range(11))
    print('map fact:', map_fact)
    print(list(map_fact))
    
    #结果
    
    <function factorial at 0x1007a2e18>
    map fact: <map object at 0x101c452e8>
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

    高阶函数

    接受函数为参数,或者把函数作为结果返回的函数是高阶函数。

    如map,filter,reduce,sorted。

    map(function, iterable, ...) 会根据提供的函数对指定序列做。第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

    filter(function, iterable接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

    reduce(function, iterable[, initializer])函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果

    sorted(iterable[, cmp[, key[, reverse]]])   排序函数

    sorted示例,reverse函数作为参数,逆置的单词排序。

    >>> def reverse(word):
    ...     return word[::-1]
    ... 
    >>> reverse('testing')
    'gnitset'
    >>> reverse(fruits)
    ['banana', 'raspberry', 'cherry', 'apple', 'fig', 'strawberry']
    >>> fruits
    ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    >>> sorted(fruits, key=reverse)    #reverse作为sorted函数的参数
    ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

    map和filter示例,计算阶乘:

    >>>def factorial(n):
        return 1 if n < 2 else n * factorial(n - 1)
    >>>list(map(factorial,range(6)))     #0到5的阶乘 
    [1, 1, 2, 6, 24, 120]
    
    list(map(factorial,filter(lambda n: n % 2,range(6))))   #过滤掉偶数的阶乘
    [1, 6, 120]

    reduce示例,求和:

    from functools import reduce    #reduce在functools模块下
    from operator import add
    reduce(add,range(100))
    4950
    sum(range(100))
    4950

    sum和reduce思想是把某个操作连续应用到序列的元素上,累计之前的结果,把一系列值规约成一个值。类似的函数还有any和all.

    all(iterable)   //如果iterable的每个元素都是真值,返回True,否则返回False

    any(iterable)    //只要有iterable的某个元素是真值就返回True。

    匿名函数 

    lambda函数定义体中不能赋值,也不能使用while和try等python语句。适合用在参数列表中。(lambda表达式会创建函数对象

    fruits = ['banana', 'raspberry', 'cherry', 'apple', 'fig', 'strawberry']
    sorted(fruits, key=lambda word: word[::-1])    #逆置的单词排序,代替了sorted示例中的reverse函数
    ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']

    可调用对象和可调用类型

    可以使用()符号调用的对象即为可调用对象

    如下:

    用户定义的函数:def语句或lambda函数创建

    内置函数:C语言实现的函数,如len或time.strftime

    内置方法:C语言实现的方法,如dict.get

    方法:类中定义的函数

    类:调用类时会调用__new__方法创建一个实例,然后运行__init__方法初始化实例返回调用方。

    类实例:如果类定义了__call方法,那么它的实例可以作为函数调用。

    生成器函数:使用yield关键字的函数或方法。

    可以使用callable()函数判断对象是否可以调用

    >>>[callable(obj) for obj in (abs,str,13)]
    [True, True, False]

    用户定义的可调用类型

    任何python对象都可以表现得像函数,只要实现__call__方法

    import random
    
    class BingoCage:
        def __init__(self, items):
            self._items = list(items)
            random.shuffle(self._items)    #打乱顺序
    
        def pick(self):
            try:
                return self._items.pop()
            except IndexError:
                raise LookupError('pick from empty BingoCage')
    
        def __call__(self, *args, **kwargs):   #实现__call__方法
            return self.pick()
    
    bingo = BingoCage(range(3))
    print(bingo.pick())
    print(bingo())     #调用bingo
    print(callable(bingo))    #判断是否可调用
    
    
    #结果:
    0
    2
    True

    函数内省

    函数对象不止有__doc__属性,还有很多其他属性,可用dir()函数查看

    >>>dir(factorial)
    ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

    用户定义的函数的属性

    名称 类型 说明
    __annotations__ dict 参数返回值和的注释信息
    __call__ method-wrapper 实现()运算符;即可调用对象协议
    __closure__ tuple 函数闭包,即自有变量的绑定(通常是None)
    __code__ code 编译成字节码的函数元数据和函数定义体
    __defaults__ tuple 形式参数的默认值
    __get__ method-wrapper 实现只读描述符协议
    __globals__ dict 函数所在模块中的全局变量
    __kwdefaults__ dict 仅限关键字形式参数的默认值
    __name__ str 函数名称
    __qualname__ str 函数的限定名称,如Random.choice

     获取函数参数信息(法一)

    函数的__default__属性保存着定位参数和关键字参数的默认值

    函数的__code__包含许多参数信息,如:

    __code__.co_argcount包含参数个数 

    __code__.co_varnames包含参数名称   (值得注意的是,这里包含函数体内定义的局部变量,真正的参数名称是前N个字符串,N的个数由co__argcount确定;且其中不包含前缀为*和**的变长参数)

    def clip(text, max_len=20):
        """在max_len前面或后面的第一个空格处截断文本
        """
        end = None
        if len(text) > max_len:
            space_before = text.rfind(' ', 0, max_len)
            if space_before >= 0:
                end = space_before
            else:
                space_after = text.rfind(' ', max_len)
                if space_after >=0:
                    end = space_after
        if end is None:
            end = len(text)
        return text[:end].rstrip()
    
    print(clip.__defaults__)          #从后往前扫描,故max_len的默认值为20
    print(clip.__code__)
    print(clip.__code__.co_argcount)    #包含局部变量
    print(clip.__code__.co_varnames)      #如结果,实际参数为2
    
    (20,) <code object clip at 0x0000016D5A545930, file "C:/py_code/nov06/Schimidt.py", line 1> 2 ('text', 'max_len', 'end', 'space_before', 'space_after')

    获取函数参数信息(法二)

    使用inspect模块下的signature()函数,signature()函数返回一个signature对象,它有一个parameters属性,这是一个有序映射,把参数名和inspecr.Parameter对象对应起来。各个Parameter有各自属性:name(名称),default(默认值),kind(类型)   //特殊的inspect._empty代表没有默认值

    from inspect import signature
    sig = signature(clip)             #clip为上个示例的函数,参数元组
    print(sig)
    print(str(sig))
    for name, param in sig.parameters.items():
        print(param.kind, ':', name, '=', param.default)
        #打印   参数类型:参数名称:参数默认值
    
    #结果
    (text, max_len=20)
    (text, max_len=20)
    POSITIONAL_OR_KEYWORD : text = <class 'inspect._empty'>
    POSITIONAL_OR_KEYWORD : max_len = 20

    kind属性值是_ParameterKind类的5个值之一,如下:

    POSITIONAL_OR_KEYWORD 可以通过定位参数和关键字参数传入的形参,多数python参数类型属于此类  
    VAR_POSITIONAL 定位参数元组 *   参数
    VAR_KEYWORD 关键字参数字典 **  参数
    KEYWORD_ONLY 仅限关键字参数  
    POSITONAL_ONLY 仅限定位参数  

      

     函数注解

    除了name,kind,default,inspect.Parameter对象还有一个annotation(注解)属性,它的值通常为inspect._empty,但可能包含注解句法提供的函数签名元素据。

    函数声明中的各个参数可以在:之后增加注解表达式。如果参数有默认值,注解放在参数名和 = 号之间。如果想注解返回值,在 )和函数声明末尾的:之间添加->和一个表达式,那个表达式可以是任意类型。

    为clip函数添加注解:

    def clip(text, max_len:'int > 0'=20) -> str:            #max_len参数注解和返回值注解
        """在max_len前面或后面的第一个空格处截断文本
        """
        end = None
        if len(text) > max_len:
            space_before = text.rfind(' ', 0, max_len)
            if space_before >= 0:
                end = space_before
            else:
                space_after = text.rfind(' ', max_len)
                if space_after >=0:
                    end = space_after
        if end is None:
            end = len(text)
        return text[:end].rstrip()
    
    
    print(clip.__annotations__)
    
    #结果
    {'max_len': 'int > 0', 'return': <class 'str'>}

    Python 对注解所做的唯一的事情是,把它们存储在函数的__annotations__ 属性里。仅此而已,Python 不做检查、不做强制、不做验证,什么操作都不做。换句话说,注解对 Python 解释器没有任何意义。注解只是元数据,可以供 IDE、框架和装饰器等工具使用。标准库中还没有什么会用到这些元数据,唯有inspect.signature() 函数知道怎么提取注解,如示例所示:

    sig = signature(clip)
    print(sig.return_annotation)
    for param in sig.parameters.values():              
        note = repr(param.annotation).ljust(13)    #param.annotation获取注解,rerp返回注解的string形式
        print(note, ':', param.name, '', param.default)
    
    
    <class 'str'>
    <class 'inspect._empty'> : text  <class 'inspect._empty'>
    'int > 0'     : max_len  20

    支持函数式编程的包

    operator模块为多个算术运算符提供了对应的函数,例如可以使用mul来代替使用匿名函数lambda a,b:a * b

    operator还有itemgetterattrgetter函数。

    itemgetter常见用途:根据元组的某个字段给元组列表排序。(itemgetter[i]类似lambda fields:fields[1],创建一个接受集合的函数,返回索引位1上的元素)

    from operator import itemgetter
    
    metro_data = [
        ('tokyo', 'JP', 36),
        ('mexico', 'IN', 21),
        ('new york', 'US', 20),
        ('sao paulo', 'BR', 19),
        ]
    
    for city in sorted(metro_data, key=itemgetter(1)):  #按照元组第二项排序
        print(city)
    #结果
    ('sao paulo', 'BR', 19)
    ('mexico', 'IN', 21)
    ('tokyo', 'JP', 36)
    ('new york', 'US', 20)
    
    
    for city in sorted(metro_data, key=itemgetter(2)):   #按照元组第三项排序
        print(city)
    #结果
    ('sao paulo', 'BR', 19)
    ('new york', 'US', 20)
    ('mexico', 'IN', 21)
    ('tokyo', 'JP', 36)

    多个参数传递给itemgetter,它构建的函数会返回提取的值构成的元组

    cc_name = itemgetter(1, 0)
    for city in metro_data:
        print(cc_name(city))
        
    #结果
    ('JP', 'tokyo')
    ('IN', 'mexico')
    ('US', 'new york')
    ('BR', 'sao paulo')

    functools.partial冻结参数

    functols.partial用于部分应用于一个函数。即:基于一个函数创建一个新的可调用对象,把原函数的某些参数固定。使用这个函数可以把接受一个或多个参数的函数改编成需要回调的API,这样需要的参数更少。

    from operator import mul      #乘法
    from functools import partial
    
    triple = partial(mul, 3)       #把第一个参数固定为3
    print(triple(7))
    print(list(map(triple, range(1, 10))))
    
    21
    [3, 6, 9, 12, 15, 18, 21, 24, 27]

    functools.partialmethod函数作业于partial一样,不过是用于处理方法。

    以上来自《流畅的python》第五章

  • 相关阅读:
    卡常技巧
    Java经典习题3
    Java经典习题4
    VC++ MFC 文件处理unicode
    批处理更换ip地址
    C#实现系统托盘
    驱动打印
    c++ vs2010 GetWindowText GetWindowTextW
    VC++ MFC ListBox 复选框
    C# 获取本机ip地址
  • 原文地址:https://www.cnblogs.com/lht-record/p/10241966.html
Copyright © 2011-2022 走看看