• 《Fluent Python》 CH.05_数据结构-一等函数 (函数对象的各种内置函数、operator模块、itemgetter 和 attrgetter迭代/取值函数)


    取自第三部分 把函数视作对象

    其他

    • 转换命令:
    • jupyter nbconvert --to markdown E:PycharmProjectsTianChiProject0_山枫叶纷飞competitions13_fluent_pythonCH.05_数据结构-一等函数.ipynb

    5.1 把函数视为对象

    示例生成一个function函数,打印__doc__对象

    def factorial(n):
        '''returns n!'''
        return 1 if n < 2 else n * factorial(n-1)
    
    factorial(5)
    
    120
    
    type(factorial)
    
    
    function
    
    print('打印\_\_doc\_\_对象,返回注释信息!!')
    help(factorial)
    
    
    Help on function factorial in module __main__:
    
    factorial(n)
        returns n!
    
    factorial.__doc__
    
    
    'returns n!'
    
    list(map(factorial, range(11)))
    
    
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
    

    示例 5-2 通过别的名称使用函数,再把函数作为参数传递

    fact = factorial
    fact(5)
    
    120
    

    5.2 高阶函数

    接受函数为参数,或者把函数作为结果返回的函数是高阶函数(higher- order function)。
    map 函数就是一例,如上面的示例 5-2 所示。

    此外,内置函 数 sorted 也是:可选的 key 参数用于提供一个函数,它会应用到各个 元素上进行排序。

    示例 5-3 根据单词长度给一个列表排序

    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    sorted(fruits, key=len)
    
    
    ['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']
    

    示例 5-4 根据反向拼写给一个单词列表排序

    def reverse(word): return word[::-1]
    sorted(fruits, key=reverse)
    
    ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
    

    map、filter和reduce的现代替代品

    使用列表推导式

    5.3 匿名函数

    lambda 关键字在 Python 表达式内创建匿名函数

    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    sorted(fruits, key=lambda x: x[::-1])
    
    ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
    

    5.4 可调用对象

    如果想判断对象能否调用,可以使用内置的 callable() 函数。

    Python 数据模型文档列出了 7 种可调用对象。

    • 用户定义的函数
    • 内置函数 (使用 C 语言(CPython)实现的函数,如 len 或 time.strftime。)
    • 内置方法 (使用 C 语言实现的方法,如 dict.get。)
    • 方法 (在类的定义体中定义的函数。)
    • 类 (调用类时会运行类的 __new__ 方法创建一个实例,然后运行 __init__ 方法,初始化实例,最后把实例返回给调用方。)
    • 类的实例,如果类定义了 __call__ 方法,那么它的实例可以作为函数调用。
    • 生成器函数:使用 yield 关键字的函数或方法,调用生成器函数返回的是生成 器对象。

    callable() 函数

    Python 中有各种各样可调用的类型,因此判断对象能否调用,最安 全的方法是使用内置的 callable() 函数:

    abs, str, 13
    
    (<function abs(x, /)>, str, 13)
    
    [callable(obj) for obj in (abs, str, 13)]
    
    
    [True, True, False]
    

    5.5 用户定义的可调用类型

    不仅 Python 函数是真正的对象,任何 Python 对象都可以表现得像函数。为此,只需实现实例方法 __call__。

    示例 5-8 实现了 BingoCage 类。

    这个类的实例使用任何可迭代对象构 建,而且会在内部存储一个随机顺序排列的列表。调用实例会取出一个 元素。

    • __init__ 接受任何可迭代对象;在本地构建一个副本,防止列表参 数的意外副作用。
    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):
            return self.pick()
    
    
    print('对象的方法调用')
    bingo = BingoCage(range(3))
    bingo.pick()
    
    对象的方法调用
    
    
    
    
    
    2
    
    print('进行第一次类的示例的call调用')
    bingo()
    
    
    进行第一次类的示例的call调用
    
    
    
    
    
    0
    
    print('进行第二次类的示例的call调用')
    bingo()
    
    进行第二次类的示例的call调用
    
    
    
    
    
    1
    

    下面讨论把函数视作对象处理的另一方面:运行时内省。

    5.6 函数内省(函数的反射)

    除了 __doc__,函数对象还有很多属性。使用 dir 函数可以探知 factorial 具有下述属性:

    print('探知任意函数的更多属性~~')
    dir(factorial)[:5]
    
    探知任意函数的更多属性~~
    
    
    
    
    
    ['__annotations__', '__call__', '__class__', '__closure__', '__code__']
    

    详细的内省函数

    ['__annotations__', # dict 参数和返回值的注解

    '__call__', # method- wrapper 实现 () 运算符;即可调用对象协议

    '__class__',

    '__closure__',# tuple 函数闭包,即自由变量的绑定(通常是 None)

    '__code__', # code 编译成字节码的函数元数据和函数定义体
    '__defaults__',# tuple 形式参数的默认值
    '__delattr__',
    '__dict__',
    '__dir__',
    '__doc__',
    '__eq__',
    '__format__',
    '__ge__',
    '__get__',
    '__getattribute__',

    '__globals__',# dict 函数所在模块中的全局变量

    '__gt__',
    '__hash__',
    '__init__',
    '__init_subclass__',

    '__kwdefaults__',# dict 仅限关键字形式参数的默认值

    '__le__',
    '__lt__',
    '__module__',
    '__name__',
    '__ne__',
    '__new__',

    '__qualname__', #str 函数的限定名称,如 Random.choice( 参阅PEP 3155,https://www.python.org/dev/peps/pep-3155/)

    '__reduce__',
    '__reduce_ex__',
    '__repr__',
    '__setattr__',
    '__sizeof__',
    '__str__',
    '__subclasshook__']

    示例 5-9 列出常规对象没有而函数有的属性

    class C:
        pass
    obj = C()
    def func():
        pass
    sorted(set(dir(func)) - set(dir(obj)))
    
    ['__annotations__',
     '__call__',
     '__closure__',
     '__code__',
     '__defaults__',
     '__get__',
     '__globals__',
     '__kwdefaults__',
     '__name__',
     '__qualname__']
    

    5.7 从定位参数到仅限关键字参数

    Python 3 进一步提供了仅限关键字参数(keyword-only argument)。

    与之密切相 关的是,调用函数时使用 * 和 **“展开”可迭代对象,映射到单个参 数。

    示例 5-10 tag 函数用于生成 HTML标签;

    使用名为 cls 的关键 字参数传入“class”属性,这是一种变通方法,因为“ class”是 Python 的关键字

    def tag(name, *content, cls=None, **attrs):
        """生成一个或多个HTML标签"""
        if cls is not None:
            attrs['class'] = cls
        if attrs:
            attr_str = ''.join(' %s="%s"' % (attr, value)
            for attr, value in sorted(attrs.items()))
        else:
            attr_str = ''
        if content:
            return '
    '.join('<%s%s>%s</%s>' % (name, attr_str, c, name) for c in content)
        else:
            return '<%s%s />' % (name, attr_str)
    
    

    示例 5-11 tag 函数(见示例 5-10)众多调用方式中的几种

    tag('br')
    
    
    '<br />'
    
    tag('p', 'hello')
    
    
    '<p>hello</p>'
    
    print(tag('p', 'hello', 'world'))
    
    
    <p>hello</p>
    <p>world</p>
    

    5.8 获取关于参数的信息

    import bobo
    @bobo.query('/')
    def hello(person):
        return 'Hello %s!' % person
    
    hello('person')
    
    'Hello person!'
    

    类似于反射:

    bobo.query 装饰器把一个普通的函数(如 hello)与框架的请求处理 机制集成起来了。

    ,Bobo 会内省 hello 函数,发现它需要一个名为 person 的参数,然后从请求中获取那个名称对应的参数,将其传给 hello 函 数,因此程序员根本不用触碰请求对象。

    函数对象的 defaults 属性

    ,它的值是一个元组,里面保存着定 位参数和关键字参数的默认值。

    仅限关键字参数的默认值在 kwdefaults 属性中。然而,参数的名称在 code 属性中,它 的值是一个 code 对象引用,自身也有很多属性。

    示例 5-15 在指定长度附近截断字符串的函数

    def my_clip(text, max_len=80):
        """在max_len前面或后面的第一个空格处截断文本"""
        print(text)
        pass
    
    提取关于函数参数的信息__defaults__>>>>> 
    
    
    
    
    
    (80,)
    

    defaults

    %

    print('提取关于函数参数的信息__defaults__>>>>> ')
    my_clip.defaults

    code

    print('提取关于函数参数的信息__code__>>>')
    my_clip.__code__
    
    

    signature

    print('示例 5-17 提取函数的签名')
    from inspect import signature
    sig = signature(my_clip)
    sig
    
    示例 5-17 提取函数的签名
    
    
    
    
    
    <Signature (text, max_len=80)>
    
    str(sig)
    
    
    
    '(text, max_len=80)'
    

    5.9 函数的注解 参数解释

    Python 3 提供了一种句法,用于为函数声明中的参数和返回值附加元数 据。

    函数声明中的各个参数可以在 : 之后增加注解表达式。
    如果参数有默认 值,注解放在参数名和 = 号之间。

    如果想注解返回值,在 ) 和函数声明 末尾的 : 之间添加 -> 和一个表达式。那个表达式可以是任何类型。注 解中最常用的类型是类(如 str 或 int)和字符串(如 'int > 0')。

    但是,注解对 Python 解释器没有任何 意义。注解只是元数据,可以供 IDE、框架和装饰器等工具使用。

    在示例 5-19 中,max_len 参数的注解用的是字符串。 注解不会做任何处理,只是存储在函数的 annotations 属性(一 个字典)中:

    示例 5-19 有参数解释的 clip 函数

    def clip(text:str, max_len:'int > 0'=80) -> str:
        """在max_len前面或后面的第一个空格处截断文本 """
        pass
    
    
    clip.__annotations__
    
    {'text': str, 'max_len': 'int > 0', 'return': str}
    

    示例 5-20 从函数签名中提取注解

    signature 函数返回一个 Signature 对象,它有一个 return_annotation 属性和一个 parameters 属性,后者是一个字 典,把参数名映射到 Parameter 对象上。每个 Parameter 对象自己也 有 annotation 属性。

    signature(clip).return_annotation
    
    str
    
    for param in signature(clip).parameters.values():
        note = repr(param.annotation).ljust(13)
        print(note, ':', param.name, '=', param.default)
    
    
    <class 'str'> : text = <class 'inspect._empty'>
    'int > 0'     : max_len = 80
    

    5.10 支持函数式编程的包

    虽然 Guido 明确表明,Python 的目标不是变成函数式编程语言,但是得 益于 operator 和 functools 等包的支持,函数式编程风格也可以信 手拈来。

    5.10.1 operator模块

    示例 5-21 使用 reduce 函数和一个匿名函数计算阶乘

    from functools import reduce
    def fact(n):
        return reduce(lambda a,b:a*b, range(1, n+1))
    fact(4)
    
    24
    

    operator包 mul累乘函数

    operator 模块为多个算术运算符提供了对应的函数,从而避免编写 lambda a, b: a*b 这种平凡的匿名函数。

    使用算术运算符函数,可以 把示例 5-21 改写成示例 5-22 那样。

    from functools import reduce
    from operator import mul
    def fact(n):
        return reduce(mul, range(1, n+1))
    
    fact(2)
    
    2
    

    operator包 itemgetter 和 attrgetter 迭代函数

    operator 模块中还有一类函数,能替代从序列中取出元素或读取对象 属性的 lambda 表达式:

    • itemgetter(1) 的作用与 lambda fields: fields[1] 一样:创建一个接受集合的函数,返回索引位 1 上的元素。
    • attrgetter 与 itemgetter 作用类似,它创建的函数根据名称提取对 象的属性。如果把多个属性名传给 attrgetter,它也会返回提取的值 构成的元组。此外,如果参数名中包含 .(点号),attrgetter 会深 入嵌套对象,获取指定的属性。

    5.10.2 使用functools.partial冻结参数

    functools.partial 这个高阶函数用于部分应用一个函数。部分应用 是指,基于一个函数创建一个新的可调用对象,把原函数的某些参数固 定。

    使用这个functools.partial函数可以把接受一个或多个参数的函数改编成需要回调的 API,这样参数更少。示例 5-26 做了简单的演示。

    from operator import mul
    from functools import partial
    triple = partial(mul, 3)
    triple(7)
    
    21
    
    list(map(triple, range(1, 10)))
    
    
    [3, 6, 9, 12, 15, 18, 21, 24, 27]
    
    
    
    
    你不逼自己一把,你永远都不知道自己有多优秀!只有经历了一些事,你才会懂得好好珍惜眼前的时光!
  • 相关阅读:
    HDU_3496_(二维费用背包)
    HDU_3732_(多重背包)
    HDU_2079_(01背包)(dfs)
    HDU_2844_(多重背包)
    Codeforces_766_D_(并查集)
    HDU_3591_(多重背包+完全背包)
    struts2标签
    ongl 表达式
    result 相关
    struts2页面输出错误信息
  • 原文地址:https://www.cnblogs.com/zhazhaacmer/p/14391574.html
Copyright © 2020-2023  润新知