《Fluent Python》第10章 序列的修改、散列和切片

前言 不要检查它是不是鸭子、它的叫声像不像鸭子、它的走路姿势像不像鸭子,等等。具体检查什么取决于你想使用语言的哪些行为。(comp.lang.python,2000 年 7月 26 日) ————Alex Martelli 多维向量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from array import array from math import sqrt import reprlib class Vector: typecode = 'd' def __init__(self, components): self....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第11章 接口,从协议到抽象基类

前言 抽象类表示接口 ————Bjarne Stroustrup(C++ 之父) 本章讨论的主题是“鸭子类型”:对象的类型无关紧要,只要实现了特定的协议即 可。 Python文化中的接口和协议 Python语言没有interface关键字,而且除了抽象基类,每个类都有接口:类实现或继承的公开属性(方法或数据属性),包括特殊方法,如__getitem__或__add__。 按照定义,受保护的属性和私有属性 不在 接口中,即使“受保护的”属性也只是采用命名约定实现的(单个前导下划线);私有属性可以轻松地访问。不要违背这些约定 关于接口,这里有个实用的补充定义:对象公开方法的子集,让对象在系统中扮演特定的角色。接口是实现特定角色的方法集合,这样理解正是Smalltalk程序员所说的协议,其他动态语言社区都借鉴了这个术语。协议与继承没有关系。一个类可能会实现多个接口,从而让实例扮演多个角色。 协议是接口,但不是正式的(只由文档和约定定义),因此协议不能像正式接口那样施加限制,但抽象基类对接口一致性是强制的。一个类可能只实现部分接口,这是允许的。 对Python程序员来说,X 类对象, X 协议和X 接口都是一个意思。 python喜欢序列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Foo: def __getitem__(self, pos): return range(0, 30, 10)[pos] f = Foo() print(f[1]) >>>10 for x in f: print(x) >>>0 >>>10 >>>20 print(20 in f) >>>True print(15 in f) >>>False 虽然没有__iter__方法,但是Foo实例是可迭代的对象,因为发现有__getitem__方法时,Python会调用它,传入从 0 开始的整数索引,尝试迭代对象(这是一种后备机制)。尽管没有实现__contains__方法,但是 Python足够智能,能迭代Foo实例,因此也能使用in运算符...

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第12章 继承的优缺点

前言 本章重点说明子类化内置类型的缺点,多重继承和方法解析顺序 子类化内置类型 python2.2之后,内置类型可以子类化了,但是内置类型(使用c语言编写)不会调用用户定义的类覆盖方法(即重写“Override”) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class MyDict(dict): def __setitem__(self, key, value): super().__setitem__(key, [value]*2) d = MyDict({'one':1, 'two':2}) print(d) >>>{'one': 1, 'two': 2} d['three'] = 3 print(d) >>>{'one': 1, 'two': 2, 'three': [3, 3]} d.update(four=4) print(d) >>>{'one': 1, 'two': 2, 'three': [3, 3], 'four': 4} 上述的例子的意思是,继承自内置类型的两个方法a()和b(),b()方法中调用了a(),我们重写了a(),此时b()仍然调用的是原来的a(), 而不是我们重写后的a() 原生类型的这种行为违背了面向对象编程的一个基本原则:始终应该从实例(self)所属的类开始搜索方法,即使在超类实现的类中调用也是如此。 这样的问题只发生在C语言实现的内置类型内部的方法委托上,解决这个问题的方法是,使用collections.UserDict, collections.UserList, collections.UserString 多重继承和方法解析顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class A: def ping(self): print('ping: ', self) class B(A): def pong(self): print('pong from B: ', self) class C(A): def pong(self): print('pong from C: ', self) class D(B, C): def ping(self): super()....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第13章 正确的重载运算符

引言 有些事情让我不安,比如运算符重载。我决定不支持运算符重载,这完全是个人选择,因为我见过太多 C++ 程序员滥用它——James Gosling(Java 之父) ps: 运算符重载它不香吗 写在前面 对于复利公式,在python中只需要 1 interest = principal * ((1 + rate) ** periods - 1) 其中periods是整数, rate、interest和principal是精确的数字(Python 中decimal.Decimal类的实例) 但是在Java中,如果把float换成精度不定的BigDecimal,就无法再使用中缀运算符,因为中缀运算符只支持基本类型。用过java大数类的都知道会写吐的: 1 BigDecimal interest = principal.multiply(BigDecimal.ONE.add(rate).pow(periods).subtract(BigDecimal.ONE)); Python禁止重载内置类型的运算符 一元运算符 - (__neg__), 取负运算符 + (__pos__), 取正运算符 ~ (__invert__), 取反运算符 一元运算符要遵守运算符的一个基本规则:始终返回一个新对象 x和+x何时不等 当x是decimal.Decimal实例子时,由于+运算返回新的实例,可能会导致偏差, 所以导致内容不等, 即x != +x ps一般比较两个浮点数都要设置一个精度来判别 重载加法运算符+和乘法运算符* 为了支持涉及不同类型的运算,Python为中缀运算符特殊方法提供了特殊的分派机制。对 表达式a + b来说,解释器会执行以下几步操作 如果a有__add__方法,而且返回值不是NotImplemented,调用a.__add__(b),然后返回结果。 如果a没有__add__方法,或者调用__add__方法返回NotImplemented,检查b有没有__radd__方法,如果有,而且没有返回NotImplemented,调用b.__radd__(a),然后返回结果。 如果b没有__radd__方法,或者调用__radd__方法返回NotImplemented,抛出TypeError,并在错误消息中指明操作数类型不支持。 注意 实现一元运算符和中缀运算符的特殊方法一定不能修改操作数。使用这些运算符的表达式期待结果是新对象 如果由于类型不兼容而导致运算符特殊方法无法返回有效的结果,那么应该返回NotImplemented,而不是抛出 TypeError。返回 NotImplemented 时,另一个操作数所属的类型还有机会执行运算,即Python会尝试调用反向方法。 为了遵守鸭子类型精神,我们不能测试other操作数(即右操作数)的类型,我们要捕获异常,然后返回NotImplemented。如果解释器还未反转操作数,那么它将尝试去做。如果反向方法返回NotImplemented,那么 Python会抛出TypeError,并返回一个标准的错误消息,例如“unsupported operand type(s) for +: Vector and str”。 python3....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第14章 可迭代的对象、迭代器和生成器

前言 迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern) 所有生成器都是迭代器,因为生成器完全实现了迭代器接口 在Python3 中,生成器有广泛的用途。现在,即使是内置的range()函数也返回一个类似生成器的对象,而以前则返回完整的列表 iter() 解释器需要迭代对象x时,会自动调用iter(x). 内置的iter函数有以下作用: 检查对象是否实现了__iter__(),如果实现了就调用它,从而获取一个迭代器 当__iter__()不存在时转而调用__getitem__(), python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素 抛出TypeError, 提示object is not iterable 可迭代的对象与迭代器的对比 下面是一个简单的 for 循环,迭代一个字符串。这里,字符串ABC 是可迭代的对象。背后是有迭代器的,只不过我们看不到: 1 2 3 4 5 6 s = 'ABC' for ch in s: print(s) >>> A >>> B >>> C 若使用while, 使用可迭代的对象构建迭代器it 1 2 3 4 5 6 7 8 9 10 11 s = 'ABC' it = iter(s) while True: try: print(next(it)) except StopIteration: del it break >>> A >>> B >>> C 标准的迭代器接口有两个方法:...

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第15章 上下文管理器和else块

前言 with 语句会设置一个临时的上下文,交给上下文管理器对象控制,并且负责清理上下文。这么做能避免错误并减少样板代码,因此 API 更安全,而且更易于使用。除了自动关闭文件之外,with 块还有很多用途。 else for/else 仅当for循环运行完毕时(即for循环没有被break语句中止)才运行else块。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for x in range(3): print(x) if x > 100: break else: print('x never greater than 100') >>> 0 >>> 1 >>> 2 >>> x never greater than 100 for x in range(5): print(x) if x > 2: break else: print('I am a dog') >>> 0 >>> 1 >>> 2 >>> 3 while/else 仅当while循环因为条件为假值而退出时(即while循环没有被break语句中止)才运行else块。...

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第16章 协程

前言 在理解协程时要从根本上把yield视作控制流程的方式, 这样就好理解协程了 生成器是如何进化成协程的 协程的底层架构在PEP 342—Coroutines via Enhanced Generators中定义。 在Python 2.5(2006年)实现了。自此之后, yield关键字可以在表达式中使用,而且生成器API中增加了send(value)方法。生成器的调用方可以使用send(...)方法发送数据, 发送的数据会成为生成器函数中yield表达式的值。因此,生成器可以作为协程使用。协程是指一个过程,这个过程与调用方协作, 产出由调用方提供的值。 除此之外,PEP 342还添加了throw(...)和close()方法:前者的作用是让调用方抛出异常,在生成器中处理;后者的作用是终止生成器。 PEP 380对生成器函数的句法做了两处改动: 生成器可以返回一个值;(在这之前,如果在生成器中给return语句提供值, 会抛出SyntaxError异常。 新引入了yield from句法, 使用它可以把复杂的生成器重构成小型的嵌套生成器,省去了之前把生成器的工作委托给子生成器所需的大量样板代码 用作协程的生成器的基本行为 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def simple_coroutine(): print('-> coroutine started...') x = yield print('-> coroutine received...', x) c = simple_coroutine() print(c) >>> <generator object simple_coroutine at 0x7f8d60c7d468> next(c) # c....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀

《Fluent Python》第17章 使用concurrent.futures处理并发

网络下载的三种风格 为了高效处理网络I/O, 需要使用并发, 因为网络有很高的延迟, 所以为了不浪费CPU周期去等待, 最好在收到网络响应之前做些其他的事。 按顺序下载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import os import time import sys import requests COUNTRIES = 'CN IN US ID BR PK NG BD RU JP MX PH VN ET EG DE IR TR CD FR'....

created: 2023-04-04  |  updated: 2023-04-04  |  阿秀