流畅的python学习笔记(四):把函数视作对象(1:一等函数)

  • 在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程序实体:
    • 在运行时创建
    • 能赋值给变量或数据结构中的元素
    • 能作为参数传给函数
    • 能作为函数的返回结果
  • 在 Python 中,整数、字符串和字典都是一等对象——没什么特别的。如果在 Python 之前,你使用的语言并未把函数当作一等公民,那么本章以及第三部分余下的内容将重点讨论把函数作为对象的影响和实际应用。
1. 把函数视作对象
  • 创建了一个函数,然后调用它,读取它的 __doc__属性,并且确定函数对象本身是 function 类的实例。
"""
	n阶递归函数
"""
def factorial(n):
    """:return n!"""
    return 1 if n < 2 else n * factorial(n - 1)


if __name__ == '__main__':
    print(factorial(42))  # 
    print(factorial.__doc__)
    print(type(factorial))
    # 1405006117752879898543142606244511569936384000000000
    # :return n!
    # <class 'function'>
  • 示例说明 factorialfunction 类的实例。延续上例继续说明展示函数的一等本性:
	'''
		我们可以把 factorial 函数赋值给变量 fact,然后通过变量名调用。
		我们还能把它作为参数传给map 函数。map 函数返回一个可迭代对象,
		里面的元素是把第一个参数(一个函数)应用到第二个参数(一个可迭代对象,
		这里是range(11))中各个元素上得到的结果。
	'''
	fact = factorial
    print(fact)
    print(fact(5))
    print(map(fact,range(11)))
    print(list(map(fact, range(11))))
    # <function factorial at 0x000001BA104104C0>
    # 120
    # <map object at 0x000001BA102C8400>
    # [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
  • 有了一等函数,就可以使用函数式风格编程。函数式编程的特点之一是使用高阶函数。
2. 高阶函数
  • 接受函数为参数,或者把函数作为结果返回的函数是高阶函数(higher-order function)。map 函数就是一例。此外,内置函数 sorted 也是:可选的 key 参数用于提供一个函数,它会应用到各个元素上进行排序。
  • 例如,若想根据单词的长度排序,只需把 len 函数传给 key 参数:
"""
	根据单词长度给一个列表排序
"""
if __name__ == '__main__':
    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    print(sorted(fruits, key=len))
    # ['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']
  • 任何单参数函数都能作为 key 参数的值。例如,为了创建押韵词典,可以把各个单词反过来拼写,然后排序:
'''
	注意:列表里的单词没有变,我们只是把反向拼写当作排序条件
'''
def reversed(word):
    return word[::-1]


if __name__ == '__main__':
    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    print(reversed("testing"))
    print(sorted(fruits, key=reversed))
    # gnitset
    # ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
  • 在函数式编程范式中,最为人熟知的高阶函数有map、filter、reduce。不过多数使用场景下都有更好的替代品。
  • map、filter和reduce的现代替代品:在 Python 3 中,map 和 filter 还是内置函数,但是由于引入了列表推导生成器表达式,它们变得没那么重要了。列表推导或生成器表达式具有 map 和 filter 两个函数的功能,而且更易于阅读:
'''
	计算阶乘列表:map 和 filter 与列表推导比较
'''
def fact(n):
    return 1 if n < 2 else n * fact(n - 1)


if __name__ == '__main__':
    print(list(map(fact, range(6))))
    print([fact(n) for n in range(6)])
    # [1, 1, 2, 6, 24, 120]
    # [1, 1, 2, 6, 24, 120]

    print(list(map(fact, filter(lambda n: n % 2, range(6)))))
    print([fact(n) for n in range(6) if n % 2])
    # [1, 6, 120]
    # [1, 6, 120]
  • 示例说明使用列表推导式替换 map 和 filter 后代码变得更加易于理解和阅读。
  • 在 Python 2 中,reduce 是内置函数,但是在 Python 3 中放到functools 模块里了。这个函数最常用于求和,自 2003 年发布的Python 2.3 开始,最好使用内置的 sum 函数。在可读性和性能方面,这是一项重大改善:
"""
	使用 reduce 和 sum 计算 0~99 之和
"""
from functools import reduce
from operator import add

if __name__ == '__main__':
	# 导入 add,以免创建一个专求两数之和的函数。
    print(reduce(add, range(100)))
    print(sum(range(100)))
    # 4950
    # 4950
  • all 和 any 也是内置的归约函数。
  • all(iterable)
    如果 iterable 的每个元素都是真值,返回 True;all([]) 返回 True。
  • any(iterable)
    只要 iterable 中有元素是真值,就返回 True;any([]) 返回False。
  • 为了使用高阶函数,有时创建一次性的小型函数更便利。这便是匿名函数存在的原因。
3. 匿名函数
  • lambda 关键字在 Python 表达式内创建匿名函数。然而,Python 简单的句法限制了 lambda 函数的定义体只能使用纯表达式。换句话说,lambda 函数的定义体中不能赋值,也不能使用 while和 try 等 Python 语句。
  • 在参数列表中最适合使用匿名函数。示例如下:
'''
	使用 lambda 表达式反转拼写,然后依此给单词列表排序
	使用 lambda 表达式重写了上面示例中排序押韵单词的示例,这样就省掉了 reverse 函数。
'''
if __name__ == '__main__':
    fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
    print(sorted(fruits, key=lambda word: word[::-1]))
    # ['banana', 'apple', 'fig', 'raspberry', 'strawberry', 'cherry']
  • 除了作为参数传给高阶函数之外,Python 很少使用匿名函数。由于句法上的限制,非平凡的 lambda 表达式要么难以阅读,要么无法写出。
  • lambda 句法只是语法糖:与 def 语句一样,lambda 表达式会创建函数对象。这是 Python 中几种可调用对象的一种。下一节会说明所有可调用对象。
4. 可调用对象
  • 除了用户定义的函数,调用运算符(即 ())还可以应用到其他对象上。如果想判断对象能否调用,可以使用内置的 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() 函数:
if __name__ == '__main__':
    print([callable(obj) for obj in (abs, str, 13)])
    # [True, True, False]
  • 接下来说明如何把类的实例变成可调用的对象。
5. 用户定义的可调用类型
  • 不仅 Python 函数是真正的对象,任何 Python 对象都可以表现得像函数。为此,只需实现实例方法 __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, *args, **kwargs):
        return self.pick()


if __name__ == '__main__':
    bingo = BingoCage(range(3))
    print(bingo())
    print(bingo.pick())
    print(callable(bingo))
    # 0
    # 2
    # True
  • 实现 __call__方法的类是创建函数类对象的简便方式,此时必须在内部维护一个状态,让它在调用之间可用,例如 BingoCage 中的剩余元素。装饰器就是这样。装饰器必须是函数,而且有时要在多次调用之间“记住”某些事 [ 例如备忘(memoization),即缓存消耗大的计算结果,供后面使用 ]。
  • 下面讨论把函数视作对象处理的另一方面:运行时内省
6. 函数内省
  • 除了 __doc__,函数对象还有很多属性。使用 dir 函数可以探知函数的所有属性。本节讨论与把函数视作对象相关的几个属性,下面重点说明函数专有而用户定义的一般对象没有的属性。计算两个属性集合的差集便能得到函数专有属性列表:
class C:
    pass

def func():
    pass

if __name__ == '__main__':
    obj = C()
    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函数的限定名称,如 Random.choice
  • 为了深入了解这些属性,我们要先探讨 Python 为声明函数形参和传入实参所提供的强大句法。
7. 从定位参数到仅限关键字参数
  • Python 最好的特性之一是提供了极为灵活的参数处理机制,而且 Python3 进一步提供了仅限关键字参数(keyword-only argument)。与之密切相关的是,调用函数时使用 * 和 **“展开”可迭代对象,映射到单个参数。如下示例:
'''
	tag 函数用于生成 HTML 标签;使用名为 cls 的关键字参数传入“class”属性,
	这是一种变通方法,因为“ class”是 Python的关键字
'''
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('<%s%s>%s</%s>' % (name, attr_str, c, name) for c in content)
    else:
        return '<%s%s />' % (name, attr_str)

if __name__ == '__main__':
    print(tag('br'))
    # <br />
    print(tag('p', 'hello'))
    # <p>hello</p>
    print(tag('p', 'hello', 'world'))
    # <p>hello</p>
    # <p>world</p>
    print(tag('p', 'hello', id=33))
    # <p id=33>hello</p>
    print(tag('p', 'hello', 'world', cls="sidebar"))
    # <p class=sidebar>hello</p>
    # <p class=sidebar>world</p>
    print(tag(content="testing", name="img"))
    # <img content=testing />
    kwargs = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'cls': 'framed'}
    print(tag(**kwargs))
    # <img class=framed src=sunset.jpg title=Sunset Boulevard />
  • 仅限关键字参数是 Python 3 新增的特性。在上例中,cls 参数只能通过关键字参数指定,它一定不会捕获未命名的定位参数。定义函数时若想指定仅限关键字参数,要把它们放到前面有 * 的参数后面。如果不想支持数量不定的定位参数,但是想支持仅限关键字参数,在签名中放一个 *,如下所示:
def f(a, *, b):
    return a, b

print(f(1, b=2))
# (1, 2)
  • 注意,仅限关键字参数不一定要有默认值,可以像上例中 b 那样,强制必须传入实参。
  • 下面说明函数参数的内省。
8. 获取关于参数的信息
  • HTTP 微框架 Bobo 中有个使用函数内省的好例子。下例是对 Bobo教程中“Hello world”应用的改编,说明了内省怎么使用。
'''
	Bobo 知道 hello 需要 person 参数,并且从 HTTP 请求中获取它
'''
import bobo

@bobo.query('/')
def hello(person):
    return 'Hello {}'.format(person)
  • bobo.query 装饰器把一个普通的函数(如 hello)与框架的请求处理机制集成起来了。这里的关键是,Bobo 会内省 hello 函数,发现它需要一个名为person 的参数,然后从请求中获取那个名称对应的参数,将其传给hello 函数,因此程序根本不用触碰请求对象。
  • 执行 bobo -f hello.py命令,访问http://localhost:8080/看到的消息是Missing form variable person,HTTP 状态码是 403:

  • 这是因为,Bobo 知道调用 hello 函数必须传入 person 参数,但是在请求中找不到同名参数。
  • 传入person参数示例:

  • Bobo 是怎么知道函数需要哪个参数的呢?它又是怎么知道参数有没有默认值呢?函数对象有个 __defaults__属性,它的值是一个元组,里面保存着定位参数和关键字参数的默认值。仅限关键字参数的默认值在__kwdefaults__属性中。然而,参数的名称在__code__ 属性中,它的值是一个 code 对象引用,自身也有很多属性。
  • 为了说明这些属性的用途,下面在 clip.py 模块中定义 clip 函数:
def clip(text, max_len=80):
    """在max_len前面或后面的第一个空格处截断文本"""
    end = None
    if len(text) > max_len:
        space_before = text.rfind(' ', 0, max_len)
        if space_before >= 0:
            end = space_before
        space_after = text.rfind(' ', max_len)
        if space_after >= 0:
            end = space_after

    if end is None:
        end = len(text)

    return text[:end].rstrip()


if __name__ == '__main__':
    print(clip.__defaults__)
    print(clip.__code__)
    print(clip.__code__.co_varnames)
    print(clip.__code__.co_argcount)
    # (80,)
    # <code object clip at ...>
    # ('text', 'max_len', 'end', 'space_before', 'space_after')
    # 2
  • 可以看出,这种组织信息的方式并不是最便利的。参数名称在__code__.co_varnames中,不过里面还有函数定义体中创建的局部变量。因此,参数名称是前 N 个字符串,N 的值由__code__.co_argcount确定。这里不包含前缀为 *或 ** 的变长参数。参数的默认值只能通过它们在 __defaults__ 元组中的位置确定,因此要从后向前扫描才能把参数和默认值对应起来。clip 函数有两个参数,text 和 max_len,其中一个有默认值,即 80,因此它必然属于最后一个参数,即 max_len。这有违常理。
  • 我们有更好的方式——使用 inspect模块。
from inspect import signature
from clip import clip

if __name__ == '__main__':
	sig = signature(clip)
    print(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 是有效的默认值(也经常这么做),而且这么做是合理的。
  • 除了 name、default 和 kind,inspect.Parameter 对象还有一个annotation(注解)属性,它的值通常是 inspect._empty,但是可能包含 Python 3 新的注解句法提供的函数签名元数据。
  • inspect.Signature 对象有个 bind 方法,它可以把任意个参数绑 定到签名中的形参上,所用的规则与实参到形参的匹配方式一样。框架可以使用这个方法在真正调用函数前验证参数。示例如下:
from inspect import signature
from tag import tag

if __name__ == '__main__':
	sig = signature(tag)
    my_tag = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'cls': 'framed'}
    bound_args = sig.bind(**my_tag)
    print(bound_args)
    # <BoundArguments (name='img', cls='framed', attrs={'title': 'Sunset Boulevard', 'src': 'sunset.jpg'})>
    for name, value in bound_args.arguments.items():
        print(name, '=', value)
        # name = img
        # cls = framed
        # attrs = {'title': 'Sunset Boulevard', 'src': 'sunset.jpg'}

    del my_tag['name']
    try:
        bound_args = sig.bind(**my_tag)
        print(bound_args)
    except TypeError as e:
        print(e)
        # missing a required argument: 'name'
  • 这个示例在 inspect 模块的帮助下,展示了 Python 数据模型把实参绑定给函数调用中的形参的机制,这与解释器使用的机制相同。
  • 框架和 IDE 等工具可以使用这些信息验证代码。Python 3 的另一个特性——函数注解——增进了这些信息的用途
9. 函数注解
  • Python 3 提供了一种句法,用于为函数声明中的参数和返回值附加元数据。修改前面的clip函数,让其支持支持注解:
def clip(text:str, max_len:'int > 0'=80) -> str: 
  • 函数声明中的各个参数可以在 : 之后增加注解表达式。如果参数有默认值,注解放在参数名和 = 号之间。如果想注解返回值,在 ) 和函数声明末尾的 : 之间添加 -> 和一个表达式。那个表达式可以是任何类型。注解中最常用的类型是类(如 str 或 int)和字符串(如 ‘int >0’)。
  • 注解不会做任何处理,只是存储在函数的 __annotations__ 属性(一个字典)中
    print(clip.__annotations__)
    # {'text': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}
  • Python 对注解所做的唯一的事情是,把它们存储在函数的__annotations__属性里。仅此而已,Python 不做检查、不做强制、不做验证,什么操作都不做。换句话说,注解对 Python 解释器没有任何意义。注解只是元数据,可以供 IDE、框架和装饰器等工具使用。
  • inspect.signature函数可以直接查看参数以及注解。示例如下:
    sig = signature(clip)
    print(sig)
    # (text: str, max_len: 'int > 0' = 80) -> str
  • 这样在框架中就可以提取绑定想要的参数,并获取相应的注解。
  • 函数注解的最大影响或许不是让 Bobo、flask 等框架自动设置,而是为 IDE 和 lint 程序等工具中的静态类型检查功能提供额外的类型信息。
  • 深入分析函数之后,本章余下的内容介绍标准库中为函数式编程提供支持的常用包。
10. 支持函数式编程的包。
  • 虽然 Guido 明确表明,Python 的目标不是变成函数式编程语言,但是得益于 operatorfunctools等包的支持,函数式编程风格也可以信手拈来。
10.1 operator模块
  • 在函数式编程中,经常需要把算术运算符当作函数使用。例如,不使用递归计算阶乘。求和可以使用 sum 函数,但是求积则没有这样的函数。我们可以使用 reduce 函数,但是需要一个函数计算序列中两个元素之积。示例 展示如何使用 lambda 表达式解决这个问题。
from functools import reduce
'''
	使用 reduce 函数和一个匿名函数计算阶乘
'''
def fact(n):
    """n!"""
    return reduce(lambda a, b: a * b, range(1, n + 1))
  • operator 模块为多个算术运算符提供了对应的函数,从而避免编写lambda a, b: a*b 这种普通的匿名函数。如下示例:
from functools import reduce
from operator import mul


def fact(n):
    """n!"""
    return reduce(mul, range(1, n + 1))
  • operator 模块中还有一类函数,能替代从序列中取出元素或读取对象属性的 lambda 表达式:因此,itemgetterattrgetter 其实会自行构建函数。如下示例:
from operator import itemgetter
'''
	使用 itemgetter 排序一个元组列表。
	itemgetter(1) 的作用与 lambdafields: fields[1] 一样:
	创建一个接受集合的函数,返回索引位 1上的元素。
'''

if __name__ == '__main__':
    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)),
        ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
    ]
    # 以第二个元素作为key排序
    for city in sorted(metro_data, key=itemgetter(1)):
        print(city)
        # ('Sao 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))
  • 如果把多个参数传给 itemgetter,它构建的函数会返回提取的值构成的元组:
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', 'Sao Paulo')
  • itemgetter 使用 [] 运算符,因此它不仅支持序列,还支持映射和任何实现 __getitem__ 方法的类。
  • attrgetter 与 itemgetter 作用类似,它创建的函数根据名称提取对象的属性。如果把多个属性名传给 attrgetter,它也会返回提取的值构成的元组。此外,如果参数名中包含 .(点号),attrgetter会深入嵌套对象,获取指定的属性。示例:
from collections import namedtuple
from operator import attrgetter

if __name__ == '__main__':
    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)),
        ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
    ]

    lat_long = namedtuple("LatLong", 'lat long')
    Metropolis = namedtuple("Metropolis", 'name cc pop coord')
    metro_areas = [Metropolis(name, cc, pop, lat_long(lat, long)) for name, cc, pop, (lat, long) in metro_data]
    print(metro_areas[0])
    # Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.691667))
    print(metro_areas[0].coord.lat)  # 深入metro_areas获取纬度
    # 35.689722

    name_lat = attrgetter('name', 'coord.lat')
    # 使用嵌套属性 纬度 进行排序
    for city in sorted(metro_areas, key=attrgetter('coord.lat')):
        print(name_lat(city))
        # ('Sao Paulo', -23.547778)
        # ('Mexico City', 19.433333)
        # ('Delhi NCR', 28.613889)
        # ('Tokyo', 35.689722)
        # ('New York-Newark', 40.808611)
  • 在 operator 模块余下的函数中,我们最后介绍一下methodcaller。它的作用与 attrgetter 和 itemgetter 类似,它会自行创建函数。methodcaller 创建的函数会在对象上调用参数指定的方法。示例如下:
from operator import methodcaller

if __name__ == '__main__':
    s = 'the time has come'
    upcase = methodcaller('upper')
    hiphenate = methodcaller('replace', ' ', '-')
    print(upcase(s))
    print(hiphenate(s))
    # THE TIME HAS COME
    # the-time-has-come
  • 结果表明,methodcaller 可以冻结某些参数,也就是部分应用(partial application),这与functools.partial 函数的作用类似。
10.2 使用functools.partial冻结参数
  • functools.partial 这个高阶函数用于部分应用一个函数。部分应用是指,基于一个函数创建一个新的可调用对象,把原函数的某些参数固定。使用这个函数可以把接受一个或多个参数的函数改编成需要回调的 API,这样参数更少。示例:
from operator import mul
from functools import partial
'''
	使用 partial 把一个两参数函数改编成需要单参数的可调用对象
'''
if __name__ == '__main__':
	# 使用 mul 创建 triple 函数,把第一个定位参数定为 3
    triple = partial(mul, 3)
    print(triple(5))
    print(list(map(triple, range(1, 10))))
    # 15
	# [3, 6, 9, 12, 15, 18, 21, 24, 27]
  • 把 partial 应用到之前定义的tag函数,冻结一个定位参数和一个关键字参数:
	picture = partial(tag, 'img', cls='pic-frame')
    print(tag)
    print(picture(src="xxx.jpg"))
    print(picture)
    print(picture.func)
    print(picture.args)
    print(picture.keywords)
    # <function tag at 0x000001E2E8341670>
    # <img class=pic-frame src=xxx.jpg />
    # functools.partial(<function tag at 0x0000025D40191670>, 'img', cls='pic-frame')
    # <function tag at 0x000001A830891670>
    # ('img',)
    # {'cls': 'pic-frame'}
小结
  • 本章的目标是探讨 Python 函数的一等本性。这意味着,我们可以把函数赋值给变量、传给其他函数、存储在数据结构中,以及访问函数的属性,供框架和一些工具使用。高阶函数是函数式编程的重要组成部分,即使现在不像以前那样经常使用 map、filter 和 reduce 函数了,但是还有列表推导以及 sum、all和 any 等内置的归约函数。
  • Python 有 7 种可调用对象,从 lambda 表达式创建的简单函数到实现__call__方法的类实例。这些可调用对象都能通过内置的callable() 函数检测。每一种可调用对象都支持使用相同的丰富句法声明形式参数,包括仅限关键字参数和注解。
  • Python 函数及其注解有丰富的属性,在 inspect 模块的帮助下,可以读取它们。例如,Signature.bind 方法使用灵活的规则把实参绑定到形参上,这与 Python 使用的规则一样。
  • 最后,本章介绍了 operator 模块中的一些函数,以及functools.partial 函数,有了这些函数,函数式编程就不太需要功能有限的 lambda 表达式了。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值