流畅的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 '\n'.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='\n')

 

/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='\n')
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一样,但是用在方法上的。

 

 

 



posted @ 2019-12-15 14:42  就是想学习  阅读(299)  评论(0编辑  收藏  举报