Chapter7 “Functions as First-Class Object“

函数也是对象,函数也有属性,例子:

>>> def factorial(n):  1
...     """returns n!"""
...     return 1 if n < 2 else n * factorial(n - 1)
...
>>> factorial(42)
1405006117752879898543142606244511569936384000000000
>>> factorial.__doc__  2
'returns n!'
>>> type(factorial)  3
<class 'function'>

这里展示了函数的对象性,可以讲引用赋值,可以当作参数

>>> fact = factorial
>>> fact
<function factorial at 0x...>
>>> fact(5)
120
>>> map(factorial, range(11))
<map object at 0x...>
>>> list(map(factorial, range(11)))
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]

高阶函数的定义是一个函数的返回或者参数是另外一个函数,比如 map,sorted:

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

像 map,filter 和 reduce 这些 函数目前基本上都被各种推导取代,推导更简单易读:

>>> list(map(factorial, range(6)))  
[1, 1, 2, 6, 24, 120]
>>> [factorial(n) for n 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]
>>>

在 python 3 中,map, fileter 返回的都是生成器,所以它们对应的是生成器表达式。

reduce的用法:

>>> from functools import reduce  
>>> from operator import add  
>>> reduce(add, range(100))  
4950
>>> sum(range(100))  
4950
>>>

另外有两个内置函数 all 和 any,都是接受一个可迭代的对象参数,然后all 返回 真仅当所有的 元素是真,any 返回真仅当一个元素为真。

匿名函数,也可以叫 lambda 函数,内部不能使用像 whlie try if = 这样的表达方式,但是可以使用 := 表达式进行赋值,基本上不会需要,因为这样lambda就会变得很复杂。

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

有九类可被调用的对象:

  • 用户自定义的函数,用 def 或者 lambda 表达式
  • 内置函数,比如 len 或者 time.strftime
  • 方法,定义在类内部的函数
  • 类,尤其是初始化的时候
  • 类的实例,当类内实现了 __call__ 方法的时候
  • 生成器函数,其实就是内部 含有 yield 的函数
  • 异步函数:async def
  • 异步生成器:async def 里有 yield 的函数

能不能调用可以通过 callable()  这个内置函数来判断,为真即为可以调用:

>>> abs, str, 'Ni!'
(<built-in function abs>, <class 'str'>, 'Ni!')
>>> [callable(obj) for obj in (abs, str, 'Ni!')]
[True, True, False]

这里详细介绍了一下类的实例里面的 __call__ 特殊方法,看例子:

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()

demo:

>>> bingo = BingoCage(range(3))
>>> bingo.pick()
1
>>> bingo()
0
>>> callable(bingo)
True

python 函数的参数定义也非常灵活,例子可以说明:

def tag(name, *content, class_=None, **attrs):
    """Generate one or more HTML tags"""
    if class_ is not None:
        attrs['class'] = class_
    attr_pairs = (f' {attr}="{value}"' for attr, value
                    in sorted(attrs.items()))
    attr_str = ''.join(attr_pairs)
    if content:
        elements = (f'<{name}{attr_str}>{c}</{name}>'
                    for c in content)
        return '\n'.join(elements)
    else:
        return f'<{name}{attr_str} />'

使用的时候:

>>> tag('br')  
'<br />'
>>> tag('p', 'hello')  
'<p>hello</p>'
>>> print(tag('p', 'hello', 'world'))
<p>hello</p>
<p>world</p>
>>> tag('p', 'hello', id=33)  
'<p id="33">hello</p>'
>>> print(tag('p', 'hello', 'world', class_='sidebar'))  
<p class="sidebar">hello</p>
<p class="sidebar">world</p>
>>> tag(content='testing', name="img")  
'<img content="testing" />'
>>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',
...           'src': 'sunset.jpg', 'class': 'framed'}
>>> tag(**my_tag)  
'<img class="framed" src="sunset.jpg" title="Sunset Boulevard" />'

python 3 还有一个新特性,就是 关键字参数,在传参的时候必须加上关键字,否则会报错:

>>> def f(a, *, b):
...     return a, b
...
>>> f(1, b=2)
(1, 2)
>>> f(1, 2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 1 positional argument but 2 were given

python 3.8 引入了另一个新特性,就是位置参数,在传参的不能加关键字:

 

def divmod(a, b, /):
    return (a // b, a % b)

在函数这章,还有两个模块也比较重要,一个是 operator, 另一个 是 functools

比较一下这两个用例就能明白:

from functools import reduce

def factorial(n):
    return reduce(lambda a, b: a*b, range(1, n+1))
from functools import reduce
from operator import mul

def factorial(n):
    return reduce(mul, range(1, n+1))

可以看到,主要是代替了lambda 函数一些繁琐的写法,itemgetter 也是这样:

>>> metro_data = [
...     ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
...     ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
...     ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
...     ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
...     ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
... ]
>>>
>>> from operator import itemgetter
>>> for city in sorted(metro_data, key=itemgetter(1)):
...     print(city)
...
('São Paulo', 'BR', 19.649, (-23.547778, -46.635833))
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))
('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
('Mexico City', 'MX', 20.142, (19.433333, -99.133333))
('New York-Newark', 'US', 20.104, (40.808611, -74.020386))
>>> cc_name = itemgetter(1, 0)
>>> for city in metro_data:
...     print(cc_name(city))
...
('JP', 'Tokyo')
('IN', 'Delhi NCR')
('MX', 'Mexico City')
('US', 'New York-Newark')
('BR', 'São Paulo')
>>>

itemgetter 是 通过 [] 调用 __getitem__ 来取值,还有一个是 attrgetter 是通过字段名来调用属性信息:

>>> from collections import namedtuple
>>> LatLon = namedtuple('LatLon', 'lat lon')  
>>> Metropolis = namedtuple('Metropolis', 'name cc pop coord')  
>>> metro_areas = [Metropolis(name, cc, pop, LatLon(lat, lon))  
...     for name, cc, pop, (lat, lon) in metro_data]
>>> metro_areas[0]
Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLon(lat=35.689722,
lon=139.691667))
>>> metro_areas[0].coord.lat  
35.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))  
...
('São Paulo', -23.547778)
('Mexico City', 19.433333)
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
('New York-Newark', 40.808611)

methodcaller 这个函数的作用不好解释,但是看用例应该可以理解:

>>> from operator import methodcaller
>>> s = 'The time has come'
>>> upcase = methodcaller('upper')
>>> upcase(s)
'THE TIME HAS COME'
>>> hyphenate = methodcaller('replace', ' ', '-')
>>> hyphenate(s)
'The-time-has-come'

functools 里有一个 partial 函数,会将传入的函数合并并生成一个新的函数:

>>> 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]
>>> from tagger import tag
>>> tag
<function tag at 0x10206d1e0>  
>>> from functools import partial
>>> picture = partial(tag, 'img', class_='pic-frame')  
>>> picture(src='wumpus.jpeg')
'<img class="pic-frame" src="wumpus.jpeg" />'  
>>> picture
functools.partial(<function tag at 0x10206d1e0>, 'img', class_='pic-frame')  
>>> picture.func  
<function tag at 0x10206d1e0>
>>> picture.args
('img',)
>>> picture.keywords
{'class_': 'pic-frame'}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值