《Fluent Python》第02章 数据结构

容器序列和扁平序列 容器序列list, tuple, collections.deque这些容器内能存放不同数据类型的数据 扁平序列str, bytes, bytearray, memoryview和array.array只能存放同一数据类型 容器内容的可变与不可变 可变: list, bytearray, array.array, collections.deque, memoryview 不可变tuple, str, bytes 列表推导 列表推导(list comprehension)简称为listcomps 1 2 3 4 L = [x for x in range(100)] M = [x*y for x in range(10) for y in range(11, 20) ] print(L) print(M) 虽然老生常谈了,但是额外说一句从后往前读非常容易理解 使用原则:用列表推导来创建新的列表,并且尽量保持简短 python会忽略代码里的[], {}, ()中的换行 列表推导不一定比map()和filter()的组合速度慢 生成器表达式 生成器表达式的语法跟列表推导差不多,只不过是把方括号[]变成() 生成器逐个产生元素, 不会像列表推导一样一次性占用内存, 因此使用生成器表达式可以一定程度上节省内存 *运算符用于拆包 *运算符可以把一个可迭代对象拆开为函数的参数, 用*args来获取不确定数量的参数也是一种经典的写法...

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

《Fluent Python》第03章 字典和集合

前言 python对字典dict的实现做了高度优化,散列表是字典类型性能出众的根本原因 集合set的实现也依赖于散列表 泛映射类型 映射是一种关联式的容器类型,它存储了对象与对象之间的映射关系 collections.abc模块中有Mapping和MutableMapping这两个抽象基类, 它们的作用是为dict和其他类似的类型定义形式接口。非抽象映射类型一般不会直接继承这些抽象基类,它们会直接对dict或是collections.User.Dict进行扩展。这些基类的作用是作为形式化的文档,它们定义了构建一个映射类型所需要的最基本的接口 它们可以和isinstance()一起用于判断某个数据是不是广义上的映射类型: 1 2 3 4 from collections import abc t = dict() print(isinstance(t, abc.Mapping)) >>>True 可散列的数据类型 标准库里的所有映射类型都是利用dict实现的,因此它们有个共同的限制,即只有可散列的数据类型才能作为这些映射里的键key 如果一个对象是可以散列的,那么在这个对象的生命周期中,它的散列值是不变的,需要实现__hash__()方法以及__eq__() python里可散列的类型有str, bytes, frozenset 注意list是不可散列的 元组tuple当其包含的元素都是可散列的情况下,它才可以散列 下面这个列子: 1 2 3 4 5 6 7 8 L = [x for x in range(10)] print(hash(L)) >>>TypeError: unhashable type: 'list' # 列表[30, 40]被冻结后可以被散列 t = (1, 2, frozenset([30, 40])) print(hash(t)) >>>985328935373711578 字典的多种构造方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 a = dict(one=1, two=2, three=3) b = {'one':1, 'two':2, 'three':3} c = dict(zip(['one', 'two', 'three'], [1, 2, 3])) d = dict([('one', 1), ('two', 2), ('three', 3)]) e = dict({'three':3, 'one':1, 'two':2}) print(a) print(b) print(c) print(d) print(e) >>>{'one': 1, 'two': 2, 'three': 3} >>>{'one': 1, 'two': 2, 'three': 3} >>>{'one': 1, 'two': 2, 'three': 3} >>>{'one': 1, 'two': 2, 'three': 3} >>>{'three': 3, 'one': 1, 'two': 2} print(a==b==c==d==e) >>>True 字典推导 注意这个例子的输出…...

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

《Fluent Python》第04章 文本和字节序列

前言 python3的str对象中的元素是Unicode字符,Python3默认使用UTF-8编码源码 编码与解码 字符的标识,即码位,范围为0 ~ 1114111的数字(十进制),(可以理解为不同的数对应不同的字符),在Unicode中以4 ~ 6个十六进制数表示,而且加前缀U+。(注:1byte = 8bit = 两个十六进制数,即以2byte ~ 3byte表示一个字符,相比而言,ASCII码一个字符用1byte表示) 字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法(可以理解为映射方法)。在UTF-8编码中,A(U+0041)的码位编码成单个字节\x41(注:x表示十进制),而在UTF-16LE编码中编码成两个字节\x41\x00 把码位转换成字节序列的过程是编码 把字节序列转换成码位的过程是解码 1 2 3 4 5 6 7 8 9 10 11 s = 'café' print(len(s)) >>>4 b_s = s.encode('utf8') print(b_s) >>>b'caf\xc3\xa9' r = b_s.decode('utf8') print(r) >>>café 字符串café有4个Unicode字符,使用encode()方法将str对象转换为bytes对象b_s,b_s有5个字节(é) 字节概要 bytes和bytearray bytes和bytearray对象的各个元素是区间[0, 255]里的整数 1 2 3 4 5 6 7 8 9 10 11 12 13 cafe = bytes('café', encoding='utf-8') print(cafe) >>>b'caf\xc3\xa9' print(cafe[0]) >>>99 print(cafe[:1]) >>>b'c' cafe_arr = bytearray(cafe) print(cafe_arr) >>>bytearray(b'caf\xc3\xa9') print(cafe_arr[-1:]) >>>bytearray(b'\xa9') bytes对象可以从str对象使用给定的编码构建,bytearray对象可以使用bytes对象构建 bytes对象的切片仍旧是bytes对象,bytearray对象的切片仍旧是bytearray对象,s[0] == s[:1]只对str这个序列类 型成立 bytearray对象没有字面量句法,而是以bytearray()和字节序列字面量参数的形式显示 如何找出字节序列的编码 一般需要明确的标出 使用统一字符编码侦测包Chardet进行猜测 处理文本文件 Unicode三明治:要尽早把输入的字节序列(bytes)解码(decode)成字符串(str);在其他处理过程中,一定不能编码或解码;对输出来说,则要尽量晚地把字符串编码成字节序列。...

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

《Fluent Python》第05章 一等函数

一等对象 编程语言理论家把“一等对象”定义为满足下述条件的程序实体: 在运行是创建 能赋值给变量或数据结构中的元素 能作为参数传给函数 能作为函数的返回结果 把函数当作对象 函数都是function类的实例,类的方法是method类的实例(虽然经常会把方法称为函数) 1 2 3 4 5 6 7 8 9 10 11 def fun(): pass class Foo: def bar(self): pass print(type(fun)) >>><class 'function'> print(type(Foo().bar)) >>><class 'method'> 高阶函数 接受函数为参数或者把函数作为结果返回的函数称为高阶函数。 在函数式编程范式中,最为人熟知的高阶函数有map(), filter(), reduce() all()和any() all(iterable), 如果iterable的每个值都为True,返回True any(iterable), 如果iterable存在一个值为True,返回True 匿名函数 使用lambda关键字创建匿名函数,格式为lambda [arg1 [,arg2,.....argn]]:expression,匿名函数的定义体中不能赋值,不能使用while和try语句。 我十分不喜欢用匿名函数,基本没有用过,即使是用高阶函数需要传递函数作为参数时,也是额外用def语句编写函数,所以一直对lambda理解不到位。 匿名函数这样理解:将arg x作为参数传递给expression,得到结果后返回。 Lundh提出的lambda表达式重构秘笈: (1)编写注释,说明lambda表达式的作用 (2)研究注释,用一个名称来概括注释 (3)把lambda表达式改写成def,使用那个名称作为函数名 (4)删除注释 可调用对象 使用callable()函数可判断是否为可调用对象。python中有7种可调用对象: 用户使用def和lambda创建的函数 内置函数,使用C语言(CPython)实现的函数,如len或time.strftime 使用C语言实现的方法,如dict.get 方法,在类的定义体中定义的函数 类,调用类时会运行__new__方法创建一个实例,python没有new运算符,所以调用类相当于调用函数 定义了__call__()类的实例 生成器函数,使用yield关键字的函数或方法 1 2 3 L = [abs, str, 13] print([callable(x) for x in L]) >>>[True, True, False] 函数内省 函数内省(function introspection)是在运行时进行的一种对象检测机制,用来查看某个对象内部信息,如类的type类型、属性、方法、事件等等。 (内省: 文言,意思是查看内部情况。华夏特色的翻译,恕我直言,把第一个叫这个名字的人拉出去斩了)...

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

《Fluent Python》第06章 使用一等函数实现设计模式

策略模式 在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。来源菜鸟教程 翻译成人话就是:每个策略(解决问题的方法)都写成一个类,它们有一个共同的接口,供一个主对象调用。 主要解决:在有多种算法相似的情况下,使用if...else所带来的复杂和难以维护。 PS:王德发?! 书上举了个根据客户属性或订单中的商品计算折扣的例子: 策略一: 积分>=1000的顾客,每个订单享5%折扣 策略二: 同一订单中,单个商品的数量>=20,享10%折扣 策略三: 订单中的商品种类>=10,享7%折扣 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 from abc import ABC, abstractmethod class Customer: def __init__(self, name, fidelity=0): self....

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

《Fluent Python》第07章 函数装饰器和闭包

前言 函数装饰器用于在代码中“标记”函数,以某种方式增强函数的行为。 想理解与掌握这一功能必须先理解闭包;除此之外,闭包还是回调式异步编程和函数式编程风格的基础 装饰器 装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把装饰后的函数返回,或者将其替换成另一个函数或可调用对象,然后返回。python也支持类装饰器。 1 2 3 4 5 6 7 8 9 10 11 12 def deco(func): def inner(): print('running inner()...') return inner @deco def target(): print('running target()...') target() >>>running inner()... print(target) >>><function deco.<locals>.inner at 0x7f63096898c8> 可见target现在是对inner的引用 python何时执行装饰器 装饰器的一个关键特性是,它们在被装饰的函数定义后立即执行。这通常是在导入时(即python加载模块时) 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 registry = list() def register(func): print('running register(%s)' % func) registry....

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

《Fluent Python》第08章 对象引用、可变性和垃圾回收

前言 元组是不可变的,但是其中的值可以改变 is和== 1 2 3 4 5 6 7 d1 = {'one':1, 'two':2} d2 = {'one':1, 'two':2} print(d1 == d2) >>>True print(d1 is d2) >>>False 可见==是判断内容相等(使用__eq__()来判断),is判断是否为同一个引用(使用id())来判断 元组的相对不可变性 元组的不可变性指的是保存的对象的引用不可变,若保存的对象的内容是可变的(但元组保存的引用不变),元组也是某种意义上的可变 1 2 3 4 5 6 7 8 9 t1 = (1, 2, [3, 4]) t2 = (1, 2, [3, 4]) print(t1 == t2) >>>True t1[-1].append(5) print(t1) >>>(1, 2, [3, 4, 5]) print(t1 == t2) >>>False 默认做浅复制 复制list最简单的方式是使用内置的类型构造方法(书上的意思感觉是要说这是深复制,但是测试了还是浅复制(python3....

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

《Fluent Python》第09章 符合Python风格的对象

前言 得益于Python 数据模型,自定义类型的行为可以像内置类型那样自然。实现如此自然的行为,靠的不是继承,而是鸭子类型(duck typing) 对象的表示形式 python提供了两种对象表示形式 repr()以便于开发者理解的方式返回对象的字符串表示形式 str()以便于用户理解的方式返回对象的字符串表现形式 这一章的精华就在下面的例子里: 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from array import array from math import sqrt class Vector2d: __slots__ = ('__x', '__y') typecode = 'd' def __init__(self, x:float, y:float): self....

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