Intro
本篇主要 follow 书籍 Fluent Python ,对python的现代优雅实现进行汇总学习,由于语法检查器和python本身的发展,有些书籍中的不足之处或是不到位的地方都会在笔者的Elegant python中进行补充。
None 的静态类型警告
你可能会写出类似以下的代码
def step(context: Context = None):
context = context or Context.new()
"""
code snippet
"""
程序顺利运行了
但是LSP会毫不留情地给你一个错:”Expression of type “None” cannot be assigned to parameter of type [Context]”
你愤懑不平,抱怨LSP是如何迂腐而不智能,连这样简单的代码逻辑都无法理清
但是我们可以
from typing import Union
def step(context: Union[Context, None] = None):
context = context or Context.new()
"""
code snippet
"""
或者
from typing import Optional
def step(context: Optional[Context] = None):
context = context or Context.new()
"""
code snippet
"""
是的,it’s boring but this is the way
Fluent Python
abstract
I don’t like meta class in Python,these shit makes your code unexplicit,which I despise a lot. 除了一些 asyncio 的场景下会对异步元编程进行简单总结,其余的相关内容将跳过。
ch2 序列组成的数组
摘要
作者会将主要精力放在第五、七、八、九章中,运算符的重载等内容对于本人来说实在不算一种优秀的编程习惯。有些人可能很喜欢元编程,但是本人更加青睐函数化,这样更加直观,通过不同的函数命名也会使得代码更加易于维护,而不是通过 tree sitter
去给自己没事找事。观后感不会明确区分章节,只会在思路和概念上进行区分。
列表
Numpy的技巧
在NumPy 中,...
用作多维数组切片的快捷方式。如果 x
是四维数组,那么 x[i, ...]
就是 x[i, :, :, :]
的缩写。
用 *
来处理多余的元素
*
运算符可以把一个可迭代对象拆开作为函数的参数
>>> divmod(20, 8)
(2, 4)
>>> t = (20, 8)
>>> divmod(*t)
(2, 4)
>>> quotient, remainder = divmod(*t)
>>> quotient, remainder
(2, 4)
用 *
来处理剩下的元素
在 Python
中,函数用 *args
来获取不确定数量的参数是一种经典写法。
于是 Python 3 里,这个概念被扩展到了平行赋值中:
>>> a, b, *rest = range(5)
>>> a, b, rest
(0, 1, [2, 3, 4])
>>> a, b, *rest = range(3)
>>> a, b, rest
(0, 1, [2])
>>> a, b, *rest = range(2)
>>> a, b, rest
(0, 1, [])
在平行赋值中,*
前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置:
>>> a, *body, c, d = range(5)
>>> a, body, c, d
(0, [1, 2], 3, 4)
>>> *head, b, c, d = range(5)
>>> head, b, c, d
([0, 1], 2, 3, 4)
嵌套结构的元组拆包
metro_areas = [
('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)),
]
print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name, cc, pop, (latitude, longitude) in metro_areas:
if longitude <= 0:
print(fmt.format(name, latitude, longitude))
namedtuple
>>> from collections import namedtuple
>>> City = namedtuple('City', 'name country population coordinates')
>>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
>>> tokyo
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,
139.691667))
>>> tokyo.population
36.933
>>> tokyo.coordinates
(35.689722, 139.691667)
>>> tokyo[1]
'JP'
创建一个具名元组需要两个参数,一个是类名,另一个是类的各个字段的名字。后者可以是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串。
存放在对应字段里的数据要以一串参数的形式传入到构造函数中(注意,元组的构造函数却只接受单一的可迭代对象)
可以通过字段名或者位置来获取一个字段的信息。除了从普通元组那里继承来的属性之外,具名元组还有一些自己专有的属性。下面的示例中就展示了几个最有用的:_fields
类属性、类方法
_make(iterable)
和实例方法 _asdict()
。
>>> City._fields
('name', 'country', 'population', 'coordinates')
>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data)
>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population',
21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
>>> for key, value in delhi._asdict().items():
print(key + ':', value)
name: Delhi NCR
country: IN
population: 21.935
coordinates: LatLong(lat=28.613889, long=77.208889)
bisect
bisect(haystack, needle)
在 haystack
(干草垛)里搜索 needle
(针)的位置,该位置满足的条件是,把 needle
插入这个位置之后,haystack
还能保持升序。也就是在说这个函数返回的位置前面
的值,都小于或等于 needle
的值。其中 haystack
必须是一个有序的序列。你可以先用 bisect(haystack, needle)
查找位置 index
,再用 haystack.insert(index, needle)
来插入新值。但你也可用 insort
来一步到位,并且后者的速度更快一些。
数组保存与读取的优雅实现
如果我们需要一个只包含数字的列表,那么 array.array
比 list
更高效。数组支持所有跟可变序列有关的操作,包括 .pop
、.insert
和 .extend
。另外,数组还提供从文件读取和存入文件的更快的方法,如 .frombytes
和 .tofile
。
Python
数组跟 C
语言数组一样精简。创建数组需要一个类型码,这个类型码用来表示在底层的 C 语言应该存放怎样的数据类型。比如 b 类型码代表的是有符号的字符(signed char),因此 array('b')
创建出的数组就只能存放一个字节大小的整数,范围从 -128 到 127,这样在序列很大的时候,我们能节省很多空间。而且 Python
不会允许你在数组里存放除指定类型之外的数据。
>>> from array import array
>>> from random import random
>>> floats = array('d', (random() for i in range(10**7)))
>>> floats[-1]
0.07802343889111107
>>> fp = open('floats.bin', 'wb')
>>> floats.tofile(fp)
>>> fp.close()
>>> floats2 = array('d')
>>> fp = open('floats.bin', 'rb')
>>> floats2.fromfile(fp, 10**7)
>>> fp.close()
>>> floats2[-1]
0.07802343889111107
>>> floats2 == floats
True
高阶函数相关
map
、filter
和reduce
,all
和 any
Decorator
Just fully read chapter 7.
Coroutine & asyncio
Fully read chapter 14, 16, 17 and 18