python123第五章_流畅的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'{c}{name}>' for c in content)

else:

return "" % (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

hello

world

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)

in

----> 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=Noneif len(text) >max_len:

space_before= text.rfind(' ', 0, max_len) #从右向左找,rfind,对应max_len前面的第一个空格

if space_before >=0:

end=space_beforeelse:

space_after= text.find(' ', max_len) #找max_len的后面了

if space_after >=0:

end=space_afterif end is None: #没有找到,很聪明定义了一个None的开关

end =len(text)returntext[: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]:

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 =

POSITIONAL_OR_KEYWORD : max_len = 8

for name, param in tag.parameters.items():

...: print(param.kind,':', name,'=',param.default)

...:

...:

POSITIONAL_OR_KEYWORD : name =

VAR_POSITIONAL : content =

KEYWORD_ONLY : cls = None

VAR_KEYWORD : attrs =

In [25]: tag.parameters.items()

Out[25]: odict_items([('name', ), ('content', ), ('cls', ), ('attrs', )])

In [26]: tag.parameters['name']

Out[26]:

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)

in

----> 1 tag.parameters.annotation

AttributeError: 'mappingproxy' object has no attribute 'annotation'

In [31]: tag.parameters

Out[31]:

mappingproxy({'name': ,

'content': ,

'cls': ,

'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 = 可以通过定位参数和关键字参数传入的行参

VAR_POSITIONAL : content = 可以定位参数的元祖

KEYWORD_ONLY : cls = None 仅限关键字参数

VAR_KEYWORD : attrs = 关键字参数字典

inspect.signature其实还有一个bind的方法,它可以模拟实参传递的过程,如果参数不对,会报错。

In [36]: tag

Out[36]:

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]:

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)

in

----> 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]: 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)

...:

: text =

: 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]:

In [96]: picture = partial(tag,'img',cls='pic-frame')

In [97]: picture(src='wumpus.jpg')

Out[97]: ''

In [98]: picture.func()

---------------------------------------------------------------------------

TypeError Traceback (most recent call last)

in

----> 1 picture.func()

TypeError: tag() missing 1 required positional argument: 'name'

In [99]: picture.func

Out[99]:

In [100]: picture.args

Out[100]: ('img',)

In [101]: picture.keywords

Out[101]: {'cls': 'pic-frame'}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值