• Python函数详解


    # ------------------------------------一等函数------------------------------------
    # 龟叔: 虽然我把函数定为一等对象,但是我并不把Python当作函数式编程语言.
    """
    Python中,所有的函数都是一等对象:
        1.在运行时创建
        2.能赋值给变量或数据结构中的元素
        3.能作为参数传给函数
        4.能作为函数的返回值
    """
    
    
    # ------------------------------------把函数视作对象------------------------------------
    def factorial(n):
        '''return n!'''
        return 1 if n < 2 else n * factorial(n - 1)
    
    
    print(factorial(42))  # 1405006117752879898543142606244511569936384000000000
    print(factorial.__doc__)  # return n!  __doc__是函数对象众多属性中的一个
    print(type(factorial))  # <class 'function'>  factorial是function类的实例
    """
    __doc__属性用于生成对象的帮助文本.
    为了展示函数对象的"一等"本性,我们可以把factorial函数赋值给变量fact,然后通过变量名调用.我们还可以把它作为参数传给map函数.
    map函数返回一个可迭代对象,里面的元素是把第一个参数(一个函数)应用到第二个参数(一个可迭代对象)中各个元素上得到的结果.
    """
    fact = factorial
    print(fact)  # <function factorial at 0x0000020CA4741F28>
    print(fact(5))  # 120
    print(map(fact, range(11)))  # <map object at 0x0000020CA484B4A8>
    print(list(map(fact, range(11))))  # [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
    
    # ------------------------------------高阶函数------------------------------------
    """
    有了一等函数,就可以使用函数式风格编程.函数式编程的特点之一是使用高阶函数.
    接受函数为参数,或者把函数作为结果返回的函数是高阶函数,例如map函数,sorted函数.
    """
    values = ['sda', 'asdasda', 'sadwqedwq', 'sadsa', 'qwwd']
    sorted(values, key=len)  # ['sda', 'qwwd', 'sadsa', 'asdasda', 'sadwqedwq']
    """
    任何单参数函数都能作为key参数的值.
    例如,将各个单词反过来拼写,然后排序.
    """
    
    
    def reverse(word):
        return word[::-1]
    
    
    reverse('testing')  # gnitset
    sorted(values, key=reverse)  # ['sda', 'asdasda', 'sadsa', 'qwwd', 'sadwqedwq']
    
    """
    在函数式编程范式中,最为人熟知的高阶函数有map,filter,reduce和apply.apply函数在Python3中移除了.
    函数式语言通常会提供map,filter和reduce三个高阶函数.在Python3中,map和reduce还是内置函数,但是由于引入了列表推导式和生成器表达式,
    它们变得没那么重要了.列表推导式或生成器表达式具有map和filter两个函数的功能,而且更易于阅读.
    """
    list(map(fact, range(6)))  # [1, 1, 2, 6, 24, 120]
    [fact(i) for i in range(6)]  # [1, 1, 2, 6, 24, 120]
    list(map(factorial, filter(lambda n: n % 2, range(6))))  # [1, 6, 120]
    [factorial(n) for n in range(6) if n % 2]  # [1, 6, 120]
    
    """
    在Python3中,map和filter返回生成器,因此它们的直接替代品是生成器表达式.(在Python2中这两个函数返回列表.)
    在Python2中,reduce是内置函数,但是在Python3中放到了functools模块里了.这个函数最常用于求和,自2003年发布Python2.3开始,
    最好使用内置的sum函数.
    """
    from functools import reduce
    from operator import add
    
    print(reduce(add, range(100)))  #
    print(sum(range(100)))  # 4950
    """
    sum和reduce的通用思想是把某个操作连续应用到序列的元素上,累计之前的结果,把一个系列值归约成一个值.
    all和any也是内置的归约函数:
    all(iterable) 如果iterable的每个元素都是真值,返回True;all([])返回True.
    any(iterable) 如果iterable中有元素是真值,就返回True;any([])返回False.
    """
    
    # ------------------------------------匿名函数------------------------------------
    """
    lambda关键字在Python表达式内创建匿名函数.
    然而,Python简单的句法限制了lambda函数的定义体只能使用纯表达式.换句话说,
    lambda函数的定义体中不能赋值,也不能使用while和try等Python语句.
    在参数列表中最适合使用匿名函数.
    """
    print(sorted(values, key=lambda word: word[::-1]))
    """
    除了作为参数传给高阶函数之外,Python很少使用匿名函数.由于句法上的限制,非平凡的lambda表达式要么难以阅读,要么无法写出.
    lambda句法只是语法糖,与def句法一样,lambda表达式会创建函数对象.
    """
    
    # ------------------------------------可调用对象------------------------------------
    """
    除了用户定义的函数,调用运算符(即())还可以应用到其他对象上.如果想判断对象能否调用,可以使用内置的callable()函数.Python列出了7种可调用对象:
    1.用户定义的函数----使用def语句或lambda表达式创建.
    2.内置函数----使用C语言(CPython)实现的函数,例如len或time.strftime.
    3.内置方法----使用C语言实现的方法,如dict.get.
    4.方法----在类的定义体种定义的函数.
    5.类----调用类时会运行类的__new__方法创建一个实例,然后运行__init__方法,初始化实例,最后把实例返回给调用方.因为Python没有new运算符,
      所以调用类相当于调用函数.
    6.类的实例----如果类定义了__call__方法,那么它的实例可以作为函数调用.
    7.生成器函数----使用yield关键字的函数或方法.调用生成器函数返回的是生成器对象.
    
    Python种有各种各样可调用的类型,因此判断对象是否可调用,最安全的方法是使用内置的callable()函数.
    """
    
    # ------------------------------------用户定义的可调用类型------------------------------------
    """
    不仅Python函数是真正的对象,任何Python对象都可以表现得像函数.为此,只需实现实例方法__call__.
    """
    import random
    
    
    class BingoCage:
    
        def __init__(self, items):
            # 接受任何可迭代对象;在本地构建一个副本,防止列表参数的意外副作用.
            # shuffle一定能完成工作,因为self._items是列表.
            self._items = list(items)
            random.shuffle(self._items)
    
        def pick(self):
            try:
                return self._items.pop()
            except IndexError:
                raise LookupError("空的!")
    
        def __call__(self, *args, **kwargs):
            return self.pick()
    
    
    bingo = BingoCage(range(3))
    print(bingo.pick())  #
    print(bingo())  # 1
    print(callable(bingo))  # True
    """
    实现__call__方法的类是创建函数对象的简便方式,因此必须在内部维护一个状态,让它在调用之间可用.例如bingo中剩余的元素.
    """
    
    # ------------------------------------函数内省------------------------------------
    """
    除了__doc__,函数对象还有很多属性.使用dir函数可用探知factorial具有下述属性:
    dir(factorial)
    ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__',
     '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__',
     '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__',
     '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
     '__subclasshook__']
    
    其中大多数属性是Python对象共有的.
    与用户定义的常规类一样,函数使用__dict__属性存储赋予它的用户属性.这相当于一种基本形式的注解.一般来说,为函数随意赋予属性不是很常见的做法.
    但是Django框架这么做了.
    """
    
    
    class C: pass  # 创建一个空的用户定义的类
    
    
    obj = C()  # 创建一个实例
    
    
    def func(): pass  # 创建一个空函数
    
    
    print(sorted(set(dir(func)) - set(dir(obj))))
    # 计算差集,然后排序,得到类的实例没有而函数有的属性方法.
    # ['__annotations__', '__call__', '__closure__', '__code__', '__defaults__',
    # '__get__', '__globals__', '__kwdefaults__', '__name__', '__qualname__']
    """
    __annotations__ dict            参数和返回值的注解
    __call__        method-wrapper  实现()运算符;即可调用对象协议
    __closure__     tuple           函数闭包,即自由变量的绑定(通常是None)
    __code__        code            编译成字节码的函数元数据和函数定义体
    __defaults__    tuple           形式参数的默认值
    __get__         method-wrapper  实现只读描述符协议
    __globals__     dict            函数所在模块中全局变量
    __kwdefaults__  dict            仅限关键字形式参数的默认值
    __name__        str             函数名称
    __qualname__    str             函数的限定名称
    """
    
    # ------------------------------------从定位参数到仅限关键字参数------------------------------------
    """
    Python最好的特性之一是提供了极为灵活的参数处理机制,而且Python3进一步提供了仅限关键字参数.与之密切相关的是,
    调用函数时使用*和**展开可迭代对象,映射到单个参数.
    
    示例:tag函数用于生成HTML标签;使用名为cls的关键字参数传入class属性,这是一种变通方法,因为class是Python的关键字
    """
    
    
    def tag(name, *content, cls=None, **attrs):
        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)
    
    
    print(tag('br'))  # <br />  传入单个定位参数,生成一个指定名称的空标签.
    print(tag('p', 'hello'))  # <p>hello</p>  第一个参数后面的任意个参数会被*content捕获,存入一个元组.
    print(tag('p', 'hello', 'world'))  # <p>hello</p>  <p>world</p>
    print(tag('p', 'hello', id=33))  # <p id=33>hello</p>  tag函数签名中没有明确指定名称的关键字参数会被**attrs捕获,存入一个字典.
    print(tag('p', 'hello', 'world',
              cls='sidebar'))  # <p class=sidebar>hello</p>  <p class=sidebar>world</p>  cls参数只能作为关键字参数传入.
    print(tag(content='testing', name='img'))  # <img content=testing /> 调用tag函数时,即便第一个定位参数也能作为关键字参数传入.
    my_tag = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'cls': 'framed'}
    # 在my_tag前面加上**,字典中的所有元素作为单个参数传入,同名键会绑定到对应的具名参数上,余下的则被**attrs捕获.
    print(tag(**my_tag))  # <img class=framed src=sunset.jpg title=Sunset Boulevard />
    
    """
    仅限关键字参数是Python3新增的特性,在上述示例中,cls参数只能通过关键字参数指定,它一定不会捕获未命名的定位参数.
    定义函数时若想指定仅限关键字参数,要把它们放到前面有*的参数后面.如果不行支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放一个*.
    """
    
    
    def f(a, *, b):
        """
        注意,仅限关键字参数不一定要有默认值.
        """
        return a, b
    
    
    print(f(1, b=2))  # (1, 2)
    
    # ------------------------------------获取关于参数的信息------------------------------------
    """
    下面以一个Web框架中示例为引子.HTTP微框架Bobo中有个使用函数内省的好例子.
    """
    import bobo
    
    
    @bobo.query('/')
    def hello(person):
        """
        bobo.query装饰器会把一个普通的函数与框架的请求处理机制集成起来.Bobo会内省hello函数,
        发现它需要一个名为person的参数,然后从请求中获取哪个名称对应的参数,并将其传给hello函数,
        因此程序员根本不用触碰请求对象.
        """
        return 'Hello %s!' % person
    
    
    """
    安装Bobo,然后启动开发服务器,执行上面的脚本,访问http://localhost:8080/看到的信息是'Missing form variable person',HTTP状态码是403.
    这是因为,Bobo知道调用hello函数必须传入person参数,但是在请求中找不到同名参数.
    
    在shell会话中使用curl展开这个行为:
    >curl -i http://localhost:8080/
        HTTP/1.0 403 Forbidden
        Date: Sun, 28 Jun 2020 06:56:26 GMT
        Server: WSGIServer/0.2 CPython/3.6.8
        Content-Type: text/html; charset=UTF-8
        Content-Length: 103
        
        <html>
        <head><title>Missing parameter</title></head>
        <body>Missing form variable person</body>
        </html>
    
    >curl -i http://localhost:8080/?person=Jim
        HTTP/1.0 200 OK
        Date: Sun, 28 Jun 2020 06:56:40 GMT
        Server: WSGIServer/0.2 CPython/3.6.8
        Content-Type: text/html; charset=UTF-8
        Content-Length: 10
        
        Hello Jim!
    
    Bobo是怎么知道函数需要哪个参数的呢?它又是怎么知道参数有没有默认值呢?
    函数对象有个__default__属性,它的值是一个元组,里面保存着定位参数和关键字参数的默认值.仅限关键字参数的默认值在__kwdefaults__属性中.
    然而,参数的名称在__code__属性中,它的值是一个code对象引用,自身也有很多属性.
    为了说明这些属性的用途,下面定义一个clip函数,然后再审查它.
    """
    
    
    def clip(text, max_len=80):
        """
        在max_len前面或后面的第一个空格处截断文本
        """
        end = None
        if len(text) > max_len:
            # 返回字符串最后一次出现的位置,如果没有匹配项则返回-1,第2个参数是开始查找的位置,第3个参数是结束查找位置
            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__)  # (80,)
    print(clip.__code__)  # <code object clip at 0x0000016531806270, file ..., line 280>
    print(clip.__code__.co_varnames)  # ('text', 'max_len', 'end', 'space_before', 'space_after')
    print(clip.__code__.co_argcount)  # 2
    """
    可以看出,这种组织信息的方式并不是最遍历的.参数名称在__code__.co_varnames中,不过里面还有函数定义体中创建的局部变量.
    因此,参数名称是前N个字符,N的值由__code__.co_argcount确定.顺便说一下,这里不包含前缀为*或**的变长参数.
    参数的默认值只能通过它们在__defaults__元组中的位置确定,因此要从后面向前面扫描才能把参数和默认值对应起来.
    在这个示例中clip函数有两个参数,text和max_len,其中一个有默认值,即80,因此它必然属于最后一个参数,即max_len.这很不舒服.
    幸好我们有更好的方式----使用inspect模块
    """
    from inspect import signature
    
    sig = signature(clip)
    print(str(sig))  # (text, max_len=80)
    for name, param in sig.parameters.items():
        print(param.kind, ':', name, '=', param.default)
    """
    POSITIONAL_OR_KEYWORD : text = <class 'inspect._empty'>
    POSITIONAL_OR_KEYWORD : max_len = 80
    """
    """
    这样就好多了,inspect.signature函数返回一个inspect.Signature对象,它有一个parameters属性,这是一个有序映射,
    把参数名和inspect.Parameter对象对应起来.各个Parameter属性也有自己的属性,例如name,default和kind.
    特殊的inspect._empty值表示没有默认值,考虑到None是有效的默认值.
    kind属性的值是_ParameterKind类中的5个值之一:
    POSITIONAL_OR_KEYWORD   可以通过定位参数和关键字参数传入的形参
    VAR_POSITIONAL          定位参数元组
    VAR_KEYWORD             关键字参数字典
    KEYWORD_ONLY            仅限关键字参数
    POSITION_ONLY           仅限定位参数,目前,Python声明函数的句法不支持.
    
    inspect.Signature对象有个bind方法,它可以把任意个参数绑定到签名中的形参上,所用的规则与实参到形参的匹配方式一样.
    框架可以使用这个方法在正真调用函数前验证参数:
    """
    sig = signature(tag)
    my_tag = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'cls': 'framed'}
    bound_args = sig.bind(**my_tag)  # 把一个承诺书字典传给.bind()方法.
    print(
        bound_args)  # <BoundArguments (name='img', cls='framed', attrs={'title': 'Sunset Boulevard', 'src': 'sunset.jpg'})>
    for name, value in bound_args.arguments.items():
        # 迭代bound_args.arguments(一个OrderedDict对象)中的元素,显示参数的名称和值.
        print(name, '=', value)
    """
    name = img
    cls = framed
    attrs = {'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}
    """
    # del my_tag['name']  # 把必须指定的参数name从my_tag中删除
    # bound_args = sig.bind(**my_tag)
    # TypeError: missing a required argument: 'name'  抛出TypeError.
    """
    这个示例在inspect模块的帮助下,展示了Python数据模型把实参绑定给函数调用中的形参的机制,这与解释器使用的机制相同.
    """
    
    # ------------------------------------函数注解------------------------------------
    """
    Python3提供了一种句法,用于为函数声明中的参数和返回值附加元数据.
    """
    
    
    def clip(text: str, max_len: 'int > 0' = 80) -> str:
        """
        与原clip函数唯一的区别在第一行
        在max_len前面或后面的第一个空格处截断文本
        """
        end = None
        if len(text) > max_len:
            # 返回字符串最后一次出现的位置,如果没有匹配项则返回-1,第2个参数是开始查找的位置,第3个参数是结束查找位置
            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()
    
    
    """
    函数声明中的各个参数可以在:之后增加注解表达式.如果参数有默认值,注解放在参数名和=号之间.如果想注解返回值,在)和函数声明末尾的:之间添加->和一个表达式.
    那个表达式可以是任何类型.注解中最常见的类型是类(如str或int)和字符串(如'int > 0').
    注解不会做任何处理!只是存储在函数的__annotations__属性(一个字典)中.
    """
    print(clip.__annotations__)  # {'text': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}
    """
    'return'键保存的是返回值注解,即函数声明中以->标记的部分.
    Python对注解所做的唯一的事情是,把它们存储在函数的__annotations__属性里,仅此而已.换句话说,注解对Python解释器没有任何意义.
    注解只是元数据,可以供IDE,框架和装饰器等工具使用.
    """
    sig = signature(clip)
    print(sig.return_annotation)  # <class 'str'>
    for param in sig.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
    """
    """
    signature函数返回一个Signature对象,它有一个return_annotation属性和一个parameters属性,后者是一个字典,把参数名映射到Parameter对象上.
    每个Parameter对象自己也有annotation属性.
    在未来,Bobo等框架可以支持注解,并进一步自动处理请求.例如,使用price:float注解的参数可以自动把查询字符串转换成函数期待的float类型.
    """
    
    # ------------------------------------支持函数式编程的包------------------------------------
    """
    虽然Python的目标不是编程函数式编程语言,但是得益于operator和functools等包的支持,函数式编程风格也可以信手拈来.
    operator模块:
    在函数式编程中,经常需要把运算符当作函数使用.例如,不使用递归计算阶乘.求和可以使用sum函数,但是求积则没有这样的函数.
    我们可以使用reduce函数,但是需要一个函数计算序列中两个元素之积.
    """
    from functools import reduce
    
    
    def fact(n):
        return reduce(lambda a, b: a * b, range(1, n + 1))
    
    
    print(fact(5))  # 120
    """
    operator模块为多个算数运算符提供了对应的函数,从而避免编写lambda a, b: a * b这种平凡的匿名函数.
    """
    from operator import mul
    
    
    def fact(n):
        return reduce(mul, range(1, n + 1))
    
    
    print(fact(5))  # 120
    """
    operator模块还有一类函数,能替代从序列中取出元素或读取对象属性的lambda表达式:
    itemgetter常见用途: 根据元组的某个字段给元组列表排序.
    """
    from operator import itemgetter
    metro_areas = [
        ("Tokyo", "JP", 36.933, (31.689722, 139.691667)),
        ("Delhi NCR", "IN", 36.933, (32.689722, 139.691667)),
        ("Mexico City", "MX", 36.933, (33.689722, 139.691667)),
        ("New York-Newark", "US", 36.933, (34.689722, 139.691667)),
        ("Sao Paulo", "BR", 36.933, (35.689722, 139.691667)),
    ]
    # itemgetter(1)的作用与lambda fields: fields[1]一样
    for city in sorted(metro_areas, key=itemgetter(1)):
        print(city)
    
    cc_name = itemgetter(1, 0)
    # 如果把多个参数传给itemgetter,它构建的函数会返回提取的值构成的元组.
    for city in metro_areas:
        print(cc_name(city))
        """
        ('JP', 'Tokyo')
        ('IN', 'Delhi NCR')
        ('MX', 'Mexico City')
        ('US', 'New York-Newark')
        ('BR', 'Sao Paulo')
        """
    """
    itemgetter使用[]运算符,因此它不仅支持序列,还支持映射和任何实现__getitem__方法的类.
    
    attrgetter和itemgetter作用类似,它创建的函数根据名称提取对象的属性.如果把多个属性名传给attrgetter,它也会返回提取的值构成的元组.
    此外,如果参数名中包含.(点号),attrgetter会深入嵌套对象,获取指定的属性.
    """
    from collections import namedtuple
    LatLong = namedtuple('LatLong', 'lat long')  # 使用namedtuple定义LatLong
    Metropolis = namedtuple('Metropolis', 'name cc pop coord')  # 再定义Metropolis
    # 使用Metropolis实例构建metro_areas列表,注意,我们使用嵌套的元组拆包提取(lat, long),然后使用它们构建LatLong,作为Metropolis的coord属性.
    metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long)) for name, cc, pop, (lat, long) in metro_areas]
    print(metro_areas[0])  # Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=31.689722, long=139.691667))
    print(metro_areas[0].coord.lat)  # 31.689722
    from operator import attrgetter
    name_lat = attrgetter('name', 'coord.lat')
    for city in sorted(metro_areas, key=attrgetter('coord.lat')):
        print(name_lat(city))
        """
        ('Tokyo', 31.689722)
        ('Delhi NCR', 32.689722)
        ('Mexico City', 33.689722)
        ('New York-Newark', 34.689722)
        ('Sao Paulo', 35.689722)
        """
    """
    下面是operator模块中定义的部分函数,省略了以_开头的名称,因为它们基本上是实现细节:
    import operator
    print([name for name in dir(operator) if not name.startswith('_')])
    ['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf',
     'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand',
     'iconcat', 'ifloordiv', 'ilshift', 'imatmul', 'imod', 'imul', 'index',
     'indexOf', 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_', 'is_not',
     'isub', 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift',
     'lt', 'matmul', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_', 'or_',
     'pos', 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor']
    
    这个52个名称中大部分的作用不言而喻.以i开头,后面是另一个运算符的那些名称(如iadd,iand等),对应的是增量赋值运算符(+=,&=).
    如果第一参数是可变的,那么这些运算符函数会就地修改它;否则,作用与不带i的函数一样,直接返回运算结果.
    """
    
    """
    methodcaller:
    它会自行创建函数.methodcaller创建的函数会在对象上调用参数指定的方法.
    """
    from operator import methodcaller
    s = 'The time has come'
    upcase = methodcaller('upper')
    print(upcase(s))  # THE TIME HAS COME
    hiphenate = methodcaller('replace', ' ', '_')
    print(hiphenate(s))  # The_time_has_come
    """
    第二个测试表明,methodcaller还可以冻结某些参数,也就是部分应用.这与functools.parial函数的作用类似.
    """
    
    # ------------------------------------使用functools.partial冻结参数------------------------------------
    """t
    functools模块提供了一系列高阶函数,其中最为人熟知的或许是reduce.余下的函数中,最有用的是partial及其变体partialmethod.
    functools.partial这个高阶函数用于部分应用一个函数.部分应用是指,基于一个函数创建一个新的可调用对象,把原函数的某些参数固定.
    使用这个函数可以把接受一个或多个参数的函数改编成需要回调的API,这样参数更少.
    """
    from operator import mul
    from functools import partial
    triple = partial(mul, 3)  # 使用mul创建triple函数,把第一个定位参数定为3.
    print(triple(7))  # 21
    print(list(map(triple, range(1, 10))))  # [3, 6, 9, 12, 15, 18, 21, 24, 27]
    """
    partial的第一个参数是一个可调用对象,后面跟着任意个要绑定的定位参数和关键字参数.
    """
    picture = partial(tag, 'img', cls='pic-frame')
    print(picture(src='test.jpg'))  # <img class=pic-frame src=test.jpg />
    print(picture)  # functools.partial(<function tag at 0x00000241EF3F6A60>, 'img', cls='pic-frame')
    print(picture.func)  # <function tag at 0x00000241EF3F6A60>
    print(picture.args)  # ('img',)
    print(picture.keywords)  # {'cls': 'pic-frame'}
    """
    partialmethod函数的作用与partial一样,不过是用于处理方法的.
    """
    
  • 相关阅读:
    Best Time to Buy and Sell Stock I II III
    数据挖掘算法面试题
    C# 从CIL代码了解委托,匿名方法,Lambda 表达式和闭包本质
    ASP.NET MVC 5
    net破解一(反编译,反混淆-剥壳,工具推荐)
    面试题及相关参考答案
    Linux 查看内核版本命令的相关说明
    c# 获取应用程序exe文件路径及退出应用程序的几种方法
    C# WebBrowser设置代理
    c# combobox控件的使用
  • 原文地址:https://www.cnblogs.com/zyyhxbs/p/13204104.html
Copyright © 2020-2023  润新知