• 流畅的Python第五章,一等函数笔记


    Python中又一个名称叫一等对象,满足以下条件的程序实体:

    1、在运行时创建

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

    3、能作为参数传给函数

    4、能作为函数的返回结果

    所以Python中,正数、字符串、字典与函数都是一等对象。

    5.1把函数当做对象:

    把函数当做对象,通过简单的__doc__可以输出函数的说明。

    In [55]: def demo(a,b): 
        ...:     '''返回a,b''' 
        ...:     return a,b 
        ...:                                                                                                                
    
    In [56]: demo.__doc__                                                                                                   
    Out[56]: '返回a,b'
    
    In [57]:  
    

     通过高阶函数把函数传递进去。

    def fact(n):
        '''returns n!'''
        return 1 if n < 2 else n * fact(n - 1)
    
    print(list(map(fact, range(10))))
    
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    

     fact就是传递进去的函数。

    5.2高阶函数

    接受函数为参数,或者把函数作为结果返回的函数为高阶函数(hight-order function)。

    map,filter,reduce,包括sorted都是高阶,sorted的key可以接收函数.

    按照这个定义,我们的闭包函数,装饰器函数,都可以称为高阶函数。

    map,filter和reduce的现代替换品

    map与filter因为列表生成式的使用,基本很多需要使用他们的地方都可以用列表生成式。

    def fact(n):
        '''returns n!'''
        return 1 if n < 2 else n * fact(n - 1)
    
    '''
    接收函数为参数,或者把函数作为结果返回的函数是高阶函数。
    所以,map,filter,reduce,sorted(因为key能接收函数)
    '''
    
    print(fact(10))
    
    print(list(map(fact, range(10))))   # map需要传入函数fact
    
    print([fact(i) for i in range(10)])   # 直接列表生成式,每个参数直接使用了函数fact,产生的返回值放入列表。
    
    print(list(map(fact, filter(lambda n : n % 2, range(10)))))   # 在map的函数后面的可迭代对象进行了filter的过滤,需要能被2整除
    
    # filter第一个只返回:后面条件成立的数值。
    def condition(n):
        if n % 2 != 0:
            return n
    print(list(map(fact, filter(condition, range(10)))))
    
    print([fact(i) for i in range(10) if i % 2])     # 列表生成式第一个写函数或者参数,第二个写循环体,第三个写条件。
    # 书中直接写if i % 2应该就是! % 2不能为0,这个写法更加精简。所以以后条件如果返回只要是真,写入就可以,生成的时候就会执行。
    print([fact(i) for i in range(10) if i % 2 !=0])   # 如果我写,我一半写成这样。
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/高级函数.py
    3628800
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
    [1, 6, 120, 5040, 362880]
    [1, 6, 120, 5040, 362880]
    [1, 6, 120, 5040, 362880]
    
    Process finished with exit code 0
    

     reduce现在用的很少,一般用在求和上面。

    from functools import reduce
    from operator import add
    
    def s_add(x, y):
        return x + y
    
    print(reduce(add, range(100)))
    print(reduce(s_add, range(100)))
    print(sum(range(100)))
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t5.2.py
    4950
    4950
    4950
    
    Process finished with exit code 0
    

     归约函数all,any是蛮好的函数.

    all(iterable)  里面的可迭代对象全部为真返回真。(有点像and)

    any(iterable) 有一个真就返回真。 (有点像or)

    a1 = '1232434'
    a2 = [1, 2, 3, 0]
    a3 = {'name': 'sidian', 'age': None}
    a4 = (None, 9, 8)
    
    print(a1, all(a1), any(a1))
    print(a2, all(a2), any(a2))
    print(a3, all(a3), any(a3))
    print(a4, all(a4), any(a4))
    
    1232434 True True
    [1, 2, 3, 0] False True
    {'name': 'sidian', 'age': None} True True
    (None, 9, 8) False True
    

    5.3匿名函数

    lambda是Python表达式内创建匿名函数。然而Python简单的语法限制了lambda函数的定义题只能使用纯表达式。换句话说,lambda函数的定义体中不能赋值,也不能使用while和try等Python语句。

    lambda(init:return),lambda函数通过分号相隔,前面是输入参数,后面是返回参数。

    lambda语法是语法糖,更def一样,会创建可以调用的函数对象。

    5.4可调用对象

    可调用对象就是能用()的对像(里面有__call__的方法),可以用callable来测试是否是可调用对象,Python数据模型里面有7种可调用对象。

    1、用户定义的函数

    比如使用def或者lambda创建的对象。

    2、内置函数

    使用C语言显示的函数,比如len或time.strftime

    3、内置方法

    使用C语言实现的方法,比如dict.get

    4、方法

    在类的实体中定义的函数

    5、类

    在实例化类的使用,首先调用的是__call__,然后调用__new__创建对象,最后__init__来初始化对象。

    6、类的实例

    在类里面定义了__call__,那么实例就可以成为调用对象。

    7、生成器

    调用生成器函数可以返回一个生成器对象。

    5.5 用户定义的可调用类型。

    通过类里面给予定义函数__call__

    import random
    
    class BingCage:
    
        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 = BingCage('abc')
    for i in range(5):
        try:
            print(bingo.pick())
        except LookupError as e:
            print(e)
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t_5.5.py
    b
    c
    a
    pick from empty BingoCage
    pick from empty BingoCage
    
    Process finished with exit code 0
    

    5.6 函数内省

    函数有很多属性,我们通过dir来查看。

    dir(lambda x : x[2])                                                                                           
    Out[57]: 
    ['__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__']
    

     首先说一个,函数对象有__dict__属性,所以可以通过.或者setattr来进行属性赋值,这个我以前还真不知道。(但一般很少对函数对象进行属性赋值)

    def demo(): 
        ...:     ... 
        ...:                                                                                                                
    
    In [63]: demo.abc = 'abc'                                                                                               
    
    In [64]: demo.__dict__                                                                                                  
    Out[64]: {'abc': 'abc'}
    
    In [65]: setattr(demo,'name','sidian')                                                                                  
    
    In [66]: demo.name                                                                                                      
    Out[66]: 'sidian'
    
    In [67]: demo.__dict__                                                                                                  
    Out[67]: {'abc': 'abc', 'name': 'sidian'}
    
    In [68]:                                                                                                                
    

     下面列出函数独有,但对象没有的属性,我准备跟类也对比一下。

    class C:
        ...
    
    def func():
        ...
    
    c = C()
    
    print(set(dir(C)) - set(dir(func)))
    print(set(dir(func)) - set(dir(C)))
    print(set(dir(c)) - set(dir(func)))
    print(set(dir(func)) - set(dir(c)))
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t_5.6.py
    {'__weakref__'}
    {'__annotations__', '__get__', '__globals__', '__call__', '__name__', '__qualname__', '__code__', '__kwdefaults__', '__defaults__', '__closure__'}
    {'__weakref__'}
    {'__annotations__', '__get__', '__globals__', '__call__', '__name__', '__qualname__', '__code__', '__kwdefaults__', '__defaults__', '__closure__'}
    Hello sidian
    
    Process finished with exit code 0
    

     从中可以看出来,对象或者类比函数对了一个属性__weakref__,我查了一些资料好像是关于垃圾回收相关,具体资料很少。

    https://stackoverflow.com/questions/36787603/what-exactly-is-weakref-in-python这个链接有一些英文的答案。

    但函数比对象多了很多属性。

    __closure_是闭包函数里面取值的。
    __kwdefaults__是查看*后面的关键字默认参数

    5.7从实际参数到仅限关键字参数。

    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(f'<{name}{attr_str}>{c}</{name}>' for c in content)
        else:
            return "<%s%s />" % (name, attr_str)
    
    
    if __name__ == '__main__':
        print(tag('br'))
        print(tag('p', 'hello', 'world'))
        print(tag('p', 'hello', 'world', cls='sidebar'))
        print(tag(content='testing', name='img'))
        print(tag(**{'name': 'img', 'title': 'Sunset Boulevard', "src": 'sunset.jpg', 'cls': 'framed'}))
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t5-7.py
    <br />
    <p>hello</p>
    <p>world</p>
    <p class="sidebar" >hello</p>
    <p class="sidebar" >world</p>
    <img content="testing"  />
    <img class="framed"  src="sunset.jpg"  title="Sunset Boulevard"  />
    
    Process finished with exit code 0
    

     上面的代码还是比较简单的,就是cls成为了传参关键字参数,如果不用cls关键字传参,它永远都是默认值None

    如果不需要*后面的参数,可以直接写一个*

    In [836]: def f(a, *, b): 
         ...:     return a,b 
         ...:      
         ...:                                                                                                               
    
    In [837]: f(1,b=2)                                                                                                      
    Out[837]: (1, 2)
    
    In [838]: f(1, 2)                                                                                                       
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-838-c9c271413adf> in <module>
    ----> 1 f(1, 2)
    
    TypeError: f() takes 1 positional argument but 2 were given
    

     上面的代码,b成了关键字必填写参数。

    5.8获取关于参数信息。

    def clip(text, max_len=8):
        '''
        :param text: 在max_len前面或后面的第一个空格处截断文本
        '''
        end = None
        if len(text) > max_len:
            space_before = text.rfind(' ', 0, max_len)  # 从右向左找,rfind,对应max_len前面的第一个空格
            if space_before >= 0:
                end = space_before
            else:
                space_after = text.find(' ', max_len) # 找max_len的后面了
                if space_after >= 0:
                    end = space_after
        if end is None:   # 没有找到,很聪明定义了一个None的开关
            end = len(text)
        return text[:end].rstrip()
    
    
    string = '进入空格该文件的目录, 空格运行后 没有相关提示,报其他错了。'
    print(clip(string))
    print(clip.__code__.co_varnames)

    这个一个截取字段串的代码:

    ('text', 'max_len', 'end', 'space_before', 'space_after')
    In [6]: from t_5_8 import clip                                                                            
    
    In [7]: clip.__code__.co_varnames                                                                         
    Out[7]: ('text', 'max_len', 'end', 'space_before', 'space_after')
    
    In [8]: clip.__code__.co_argcount                                                                         
    Out[8]: 2
    
    In [9]: clip.__defaults__                                                                                 
    Out[9]: (8,)
    
    In [10]: clip.__code__.co_name                                                                            
    Out[10]: 'clip'
    
    In [11]:  
    

     这是通过一些函数方法取出来的参数,感觉不是很好,后面通过inspect 来取出函数的参数。

    In [16]: from inspect import signature                                                                    
    
    In [17]: sig = signature(clip)                                                                            
    
    In [18]: sig                                                                                              
    Out[18]: <Signature (text, max_len=8)>
    
    In [19]: str(sig)                                                                                         
    Out[19]: '(text, max_len=8)'
    
    In [20]: dir(sig)                                                                                         
    Out[20]: 
    ['__class__',
     '__delattr__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__gt__',
     '__hash__',
     '__init__',
     '__init_subclass__',
     '__le__',
     '__lt__',
     '__module__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__setattr__',
     '__setstate__',
     '__sizeof__',
     '__slots__',
     '__str__',
     '__subclasshook__',
     '_bind',
     '_bound_arguments_cls',
     '_hash_basis',
     '_parameter_cls',
     '_parameters',
     '_return_annotation',
     'bind',
     'bind_partial',
     'empty',
     'from_builtin',
     'from_callable',
     'from_function',
     'parameters',
     'replace',
     'return_annotation']
    
    In [21]: 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 = 8
    
    for name, param in tag.parameters.items(): 
        ...:     print(param.kind,':', name,'=',param.default) 
        ...:      
        ...:                                                                                                  
    POSITIONAL_OR_KEYWORD : name = <class 'inspect._empty'>
    VAR_POSITIONAL : content = <class 'inspect._empty'>
    KEYWORD_ONLY : cls = None
    VAR_KEYWORD : attrs = <class 'inspect._empty'>
    
    In [25]: tag.parameters.items()                                                                           
    Out[25]: odict_items([('name', <Parameter "name">), ('content', <Parameter "*content">), ('cls', <Parameter "cls=None">), ('attrs', <Parameter "**attrs">)])
    
    In [26]: tag.parameters['name']                                                                           
    Out[26]: <Parameter "name">
    
    In [27]: dir(tag.parameters['name'])                                                                      
    Out[27]: 
    ['KEYWORD_ONLY',
     'POSITIONAL_ONLY',
     'POSITIONAL_OR_KEYWORD',
     'VAR_KEYWORD',
     'VAR_POSITIONAL',
     '__class__',
     '__delattr__',
     '__dir__',
     '__doc__',
     '__eq__',
     '__format__',
     '__ge__',
     '__getattribute__',
     '__gt__',
     '__hash__',
     '__init__',
     '__init_subclass__',
     '__le__',
     '__lt__',
     '__module__',
     '__ne__',
     '__new__',
     '__reduce__',
     '__reduce_ex__',
     '__repr__',
     '__setattr__',
     '__setstate__',
     '__sizeof__',
     '__slots__',
     '__str__',
     '__subclasshook__',
     '_annotation',
     '_default',
     '_kind',
     '_name',
     'annotation',
     'default',
     'empty',
     'kind',
     'name',
     'replace']
    
    In [28]: tag.parameters['name'].name                                                                      
    Out[28]: 'name'
    
    In [29]: tag.parameters['content'].name                                                                   
    Out[29]: 'content'
    
    In [30]: tag.parameters.annotation                                                                        
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-30-ca6845388021> in <module>
    ----> 1 tag.parameters.annotation
    
    AttributeError: 'mappingproxy' object has no attribute 'annotation'
    
    In [31]: tag.parameters                                                                                   
    Out[31]: 
    mappingproxy({'name': <Parameter "name">,
                  'content': <Parameter "*content">,
                  'cls': <Parameter "cls=None">,
                  'attrs': <Parameter "**attrs">})
    
    In [32]: tag.parameters['content'].annotation                                                             
    Out[32]: inspect._empty
    

     从inspect.signature对象中我们可以方便的看到函数的行参,通过str可以

    从对象的parameters字典中,里面的value有下面这些普通的属性。

    'annotation',
     'default',
     'empty',
     'kind',
     'name',
     'replace'

    通过调用kind属性获取不同的值。

    POSITIONAL_OR_KEYWORD : name = <class 'inspect._empty'> 可以通过定位参数和关键字参数传入的行参
    VAR_POSITIONAL : content = <class 'inspect._empty'> 可以定位参数的元祖
    KEYWORD_ONLY : cls = None 仅限关键字参数
    VAR_KEYWORD : attrs = <class 'inspect._empty'> 关键字参数字典


    inspect.signature其实还有一个bind的方法,它可以模拟实参传递的过程,如果参数不对,会报错。
    In [36]: tag                                                                                              
    Out[36]: <Signature (name, *content, cls=None, **attrs)>
    
    In [37]: my_tag = {'name': 'img', 'title': 'Sunset Boulevard', "src": 'sunset.jpg', 'cls': 'framed'}      
    
    In [38]: bound_args = tag.bind(**my_tag)                                                                  
    
    In [39]: bound_args                                                                                       
    Out[39]: <BoundArguments (name='img', cls='framed', attrs={'title': 'Sunset Boulevard', 'src': 'sunset.jpg'})>
    
    In [40]: for name, value in bound_args.arguments.items(): 
        ...:     print(name,'=',value) 
        ...:                                                                                                  
    name = img
    cls = framed
    attrs = {'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}
    
    In [41]: del my_tag['name']                                                                               
    
    In [42]: bound_args = tag.bind(**my_tag)                                                                  
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-42-137b62789a71> in <module>
    ----> 1 bound_args = tag.bind(**my_tag)
    
    /usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/inspect.py in bind(*args, **kwargs)
       3013         if the passed arguments can not be bound.
       3014         """
    -> 3015         return args[0]._bind(args[1:], kwargs)
       3016 
       3017     def bind_partial(*args, **kwargs):
    
    /usr/local/Cellar/python/3.7.4/Frameworks/Python.framework/Versions/3.7/lib/python3.7/inspect.py in _bind(self, args, kwargs, partial)
       2928                             msg = 'missing a required argument: {arg!r}'
       2929                             msg = msg.format(arg=param.name)
    -> 2930                             raise TypeError(msg) from None
       2931             else:
       2932                 # We have a positional argument to process
    
    TypeError: missing a required argument: 'name'
    

     上面通过对象的bind属性,模拟传参的的过程,当参数中缺少必要参数时,报错。

    5.9 函数注解。

    def clip(text:str, max_len:'int' =8) -> str:
        '''
        :param text: 在max_len前面或后面的第一个空格处截断文本
        '''
        end = None
        if len(text) > max_len:
            space_before = text.rfind(' ', 0, max_len)  # 从右向左找,rfind,对应max_len前面的第一个空格
            if space_before >= 0:
                end = space_before
            else:
                space_after = text.find(' ', max_len) # 找max_len的后面了
                if space_after >= 0:
                    end = space_after
        if end is None:   # 没有找到,很聪明定义了一个None的开关
            end = len(text)
        return text[:end].rstrip()
    
    In [63]: from t_5_8 import clip                                                                           
    
    In [64]: clip.__annotations__                                                                             
    Out[64]: {'text': str, 'max_len': 'int', 'return': str}
    
    In [65]:                   
    

     还是蛮有意思的,这样的写法感觉非常便于维护。

    在参数后面添加:后面就是注释,然后在)于:之间通过->和一个表达式,添加返回的内容形式。

    当然你也可以从inspect.signature获取annotation

    In [65]: clip = signature(clip)                                                                           
    
    In [66]: clip                                                                                             
    Out[66]: <Signature (text: str, max_len: 'int' = 8) -> str>
    
    In [67]: clip.return_annotation                                                                           
    Out[67]: str
    
    In [68]: str(clip)                                                                                        
    Out[68]: "(text: str, max_len: 'int' = 8) -> str"
    
    In [74]: for param in sig.parameters.values(): 
        ...:     note = repr(param.annotation).ljust(13) 
        ...:     print(note, ':', param.name, '=', param.default) 
        ...:                                                                                                  
    <class 'inspect._empty'> : text = <class 'inspect._empty'>
    <class 'inspect._empty'> : max_len = 8
    

    5.10 支持函数式编程的包

    主要讲了一些operator以及functools里面的一些实用模块,确实非常实用。

    from functools import reduce
    from operator import mul
    
    def fact0(n):
        return reduce(lambda a, b: a* b, range(1, n))
    
    def fact1(n):
        return reduce(mul, range(1, n))
    
    print(fact0(100), fact1(100),sep='
    ')
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t_5_10.py
    933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
    933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
    

     operator里面会让你就连写lambda的机会都没有。

    from functools import reduce
    from operator import mul
    from operator import itemgetter
    
    def fact0(n):
        return reduce(lambda a, b: a* b, range(1, n))
    
    def fact1(n):
        return reduce(mul, range(1, n))
    
    print(fact0(100), fact1(100),sep='
    ')
    print()
    
    metro_data = [
        ('Tokyo', 'JP', 36.933, (35.33453, 13.93434)),
        ('Delhi NCR', 'IN', 21.935, (28.34345345, 88.123523)),
        ('Mexico City', 'MX', 20.142, (19.4546456, -99.34132343))
    ]
    
    for city in sorted(metro_data, key=itemgetter(1)): # 从对象中[]取出1号参数
        print(city)
    
    print()
    cc_name = itemgetter(1,0)    # 从一个对象中通过[]取出里面的参数
    for city in metro_data:
        print(cc_name(city))
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第五章/t_5_10.py
    933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
    933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000
    
    ('Delhi NCR', 'IN', 21.935, (28.34345345, 88.123523))
    ('Tokyo', 'JP', 36.933, (35.33453, 13.93434))
    ('Mexico City', 'MX', 20.142, (19.4546456, -99.34132343))
    
    ('JP', 'Tokyo')
    ('IN', 'Delhi NCR')
    ('MX', 'Mexico City')
    
    Process finished with exit code 0
    

     itemgetter也是不给你任何机会写lambda

    其实itemgetter(1) 跟lamdba x:x[1]一样,当然itrmgetter(1, 0 )于lambda x: (x[0],x[1])效果也一样。

    还有一个attrgetter效果其实也差不多,只不过这个是通过.来获取属性。

    from operator import attrgetter
    
    LatLong = namedtuple('LatLong', 'lat long')
    Metropolis = namedtuple('Metropolis', 'name cc pop coord')
    metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long))
                   for name, cc, pop, (lat, long) in metro_data]
    print(metro_areas[0])
    print(metro_areas[0].coord.lat)
    
    # for city in sorted(metro_areas, key=attrgetter('coord.lat')):    # 通过对象里面的coord对象的lat进行排序
    for city in sorted(metro_areas, key= lambda x: x.coord.lat):    # 效果一样。
        print(city)
    

     本节还介绍了一个methodcaller的函数,有点意思。

    它可以把对象的方法变成函数

    In [82]: from operator import methodcaller                                                                
    
    In [83]: upper_call = methodcaller('upper')                                                               
    
    In [84]: upper_call('sdfghjk')                                                                            
    Out[84]: 'SDFGHJK'
    
    In [85]: replcae_call = methodcaller('replace', ' ', '_')                                                 
    
    In [86]: replcae_call('123 456 789')                                                                      
    Out[86]: '123_456_789'
    

     非常有意思的一个工具。

    5.10.2实用functools.partial冻结参数。

    In [87]: from operator import mul                                                                         
    
    In [88]: from functools import partial                                                                    
    
    In [89]: triple = partial(mul,3)                                                                          
    
    In [90]: triple(7)                                                                                        
    Out[90]: 21
    
    In [91]: [triple(i) for i in range(10)]                                                                   
    Out[91]: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
    

     非常实用的一个函数,返回的也是一个函数,但这个函数已经带有默认值了,默认值就是你后面的参数。我尝试把默认值填满试试。

    In [92]: triple = partial(mul,1,2)                                                                        
    
    In [93]: triple()                                                                                         
    Out[93]: 2
    

     果然可以

    In [94]: from t5_7 import tag                                                                             
    
    In [95]: tag                                                                                              
    Out[95]: <function t5_7.tag(name, *content, cls=None, **attrs)>
    
    In [96]: picture = partial(tag,'img',cls='pic-frame')                                                     
    
    In [97]: picture(src='wumpus.jpg')                                                                        
    Out[97]: '<img class="pic-frame"  src="wumpus.jpg"  />'
    
    In [98]: picture.func()                                                                                   
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-98-c94e1f1add40> in <module>
    ----> 1 picture.func()
    
    TypeError: tag() missing 1 required positional argument: 'name'
    
    In [99]: picture.func                                                                                     
    Out[99]: <function t5_7.tag(name, *content, cls=None, **attrs)>
    
    In [100]: picture.args                                                                                    
    Out[100]: ('img',)
    
    In [101]: picture.keywords                                                                                
    Out[101]: {'cls': 'pic-frame'}
    

     从partial返回的函数对象中可以看到,它的属性中含有原函数,感觉它更像一个简单的装饰器。

    还有一个functoos.partialmethod函数用法于partail一样,但是用在方法上的。



  • 相关阅读:
    量子算法抛转(以及Oracle函数初步)
    量子计算中的幺正操作符和干涉现象
    2个qubit的量子门
    量子计算提前填坑
    单qubit量子门
    入职互联网公司工作以后的一些思考
    善用string.Format
    iOS开发学习笔记 self和super的区别
    当姿态估计算法遇上《本草纲目》,看“刘畊宏男孩”如何驱动虚拟人
    AliAGC 自动增益控制算法:解决复杂场景下的音量问题
  • 原文地址:https://www.cnblogs.com/sidianok/p/12044154.html
Copyright © 2020-2023  润新知