学习笔记之盘一盘 Python 系列 1 & 2

盘一盘 Python 系列 1 & 2 - 入门篇


  • 通过 dir( X ) 和help( X ) 可看出 X 对应的对象里可用的属性和方法。
    • dir(int)  
  • getcontext() 显示了 Decimal 对象的默认精度值是 28 位 (prec=28)
    • decimal.getcontext()  
  • 那保留 4 位呢?用 getcontext().prec 来调整精度哦。
    • decimal.getcontext().prec = 4  
  • 高精度的 float 加上低精度的 float,保持了高精度,没毛病。
  • 确定bool(X) 的值是 True 还是 False,就看 X 是不是空,空的话就是 False,不空的话就是 True。
    • 对于数值变量,0, 0.0 都可认为是空的。  
    • 对于容器变量,里面没元素就是空的。  
  • 字符中常见的内置方法 (可以用 dir(str) 来查) 有
    • capitalize():大写句首的字母
    • split():把句子分成单词
    • find(x):找到给定词 x 在句中的索引,找不到返回 -1
    • replace(x, y):把句中 x 替代成 y
    • strip(x):删除句首或句末含 x 的部分
  • pattern = re.compile("'[0-9/:s]+'")
    • 这个抽象模式表达式 '[0-9/:s]+',里面符号的意思如下:
      • 最外面的两个单引号 ' 代表该模式以它们开始和结束
      • 中括号 [] 用来概括该模式涵盖的所有类型的字节
      • 0-9 代表数字类的字节
      • / 代表正斜线
      • : 代表分号
      • s 代表空格
      • [] 外面的加号 + 代表 [] 里面的字节出现至少 1 次
  • pattern.findall(input)
  • 创建元组可以用小括号 (),也可以什么都不用,为了可读性,建议还是用 ()。此外对于含单个元素的元组,务必记住要多加一个逗号,举例如下:
    • print( type( ('OK') ) ) # 没有逗号 , <class 'str'>
    • print( type( ('OK',) ) ) # 有逗号 , <class 'tuple'>
    • 看看,没加逗号来创建含单元素的元组,Python 认为它是字符。
  • 当然也可以创建二维元组:
    • nested = (1, 10.31, 'python'), ('data', 11) # ((1, 10.31, 'python'), ('data', 11))
  • 元组有不可更改 (immutable) 的性质,因此不能直接给元组的元素赋值
    • t = ('OK', [1, 2], True)  
    • t[2] = False # TypeError: 'tuple' object does not support item assignment
  • 但是只要元组中的元素可更改 (mutable),那么我们可以直接更改其元素,注意这跟赋值其元素不同。
    • t[1].append(3) # ('OK', [1, 2, 3], True)
  • 元组大小和内容都不可更改,因此只有 count 和 index 两种方法。
  • 元组拼接 (concatenate) 有两种方式,用「加号 +」和「乘号 *」,前者首尾拼接,后者复制拼接。
    • (1, 10.31, 'python') + ('data', 11) + ('OK',) # (1, 10.31, 'python', 'data', 11, 'OK')
    • (1, 10.31, 'python') * 2 # (1, 10.31, 'python', 1, 10.31, 'python')
  • 解压 (unpack) 一维元组 (有几个元素左边括号定义几个变量)
    • t = (1, 10.31, 'python')
    • (a, b, c) = t
    • print( a, b, c ) # 1 10.31 python
  • 解压二维元组 (按照元组里的元组结构来定义变量)
    • t = (1, 10.31, ('OK','python'))
    • (a, b, (c,d)) = t
    • print( a, b, c, d ) # 1 10.31 OK python
  • 如果你只想要元组其中几个元素,用通配符「*」,英文叫 wildcard,在计算机语言中代表一个或多个元素。下例就是把多个元素丢给了 rest 变量。
    • t = 1, 2, 3, 4, 5
    • a, b, *rest, c = t
    • print( a, b, c ) # 1 2 5
    • print( rest ) # [3, 4]
  • 如果你根本不在乎 rest 变量,那么就用通配符「*」加上下划线「_」,例子如下:
    • a, b, *_ = t
    • print( a, b ) # 1 2
  • tuple优点缺点
    • 优点:占内存小,安全,创建遍历速度比列表快,可一赋多值。
    • 缺点:不能添加和更改元素。
    • immutable 的好处实在是太多了:性能优化,多线程安全,不需要锁,不担心被恶意修改或者不小心修改。
  • tuple v.s. list
    • 创建速度,元组 (12.9ns) 碾压列表 (62ns)。
    • 遍历速度两者相当,元组 (498 µs) 险胜列表 (507 µs)。
    • 列表比元组稍微废点内存空间。
  • 不像元组,列表内容可更改 (mutable),因此附加 (append, extend)、插入 (insert)、删除 (remove, pop) 这些操作都可以用在它身上。
    • l.append([4, 3])
    • print( l ) # [1, 10.31, 'python', [4, 3]]
    • l.extend([1.5, 2.0, 'OK'])
    • print( l ) # [1, 10.31, 'python', [4, 3], 1.5, 2.0, 'OK']
      • 严格来说 append 是追加,把一个东西整体添加在列表后,而 extend 是扩展,把一个东西里的所有元素添加在列表后。对着上面结果感受一下区别。
    • insert(i, x) 在编号 i 位置前插入 x。
    • remove 和 pop 都可以删除元素
      • 前者是指定具体要删除的元素,比如 'python'
      • 后者是指定一个编号位置,比如 3,删除 l[3] 并返回出来
  • print( l[::-1] )
    • 注意最后把 step 设为 -1,相当于将列表反向排列。
  • 和元组拼接一样, 列表拼接也有两种方式,用「加号 +」和「乘号 *」,前者首尾拼接,后者复制拼接。
  • list优点缺点
    • 优点:灵活好用,可索引、可切片、可更改、可附加、可插入、可删除。
    • 缺点:相比 tuple 创建和遍历速度慢,占内存。此外查找和插入时间较慢。
  • 字典里最常用的三个内置方法就是 keys(), values() 和 items(),分别是获取字典的键、值、对。
  • 此外在字典上也有添加、获取、更新、删除等操作。
    • 比如想看看腾讯的股价是多少 (两种方法都可以)
      • print( d['Price'] )
      • print( d.get('Price') )
    • 比如去掉股票代码 (code)
      • del d['Code']  
    • 或像列表里的 pop() 函数,删除行业 (industry) 并返回出来。
      • print( d.pop('Industry') )  
  • 字典里的键是不可更改的,因此只有那些不可更改的数据类型才能当键,比如整数 (虽然怪怪的)、浮点数 (虽然怪怪的)、布尔 (虽然怪怪的)、字符、元组 (虽然怪怪的),而列表却不行,因为它可更改。
  • 虽然怪怪的,但这些 2, 10.31, True, ('OK', 3) 确实能当键。有个地方要注意下,True 其实和整数 1 是一样的,由于键不能重复,当你把 2 该成 1时,你会发现字典只会取其中一个键,示例如下:
    • d = {
    • 1 : 'integer key',
    • 10.31 : 'float key',
    • True : 'boolean key',
    • ('OK',3) : 'tuple key'
    • }
    • # d
    • {1: 'boolean key',
    • 10.31: 'float key',
    • ('OK', 3): 'tuple key'}
  • 那么如何快速判断一个数据类型 X 是不是可更改的呢?两种方法:
    • 麻烦方法:用 id(X) 函数,对 X 进行某种操作,比较操作前后的 id,如果不一样,则 X 不可更改,如果一样,则 X 可更改。
    • 便捷方法:用 hash(X),只要不报错,证明 X 可被哈希,即不可更改,反过来不可被哈希,即可更改。
  • dictionary优点缺点
    • 优点:查找和插入速度快
    • 缺点:占内存大
  • 集合有两种定义语法
    • 第一种是
      • {元素1, 元素2, ..., 元素n}
    • 第二种是用 set() 函数,把列表或元组转换成集合。
      • set( 列表 或 元组 )
  • 从A的结果发现集合的两个特点:无序(unordered)和唯一(unique)。由于set存储的是无序集合,所以我们没法通过索引来访问,但是可以判断一个元素是否在集合中。
  • 用 set 的内置方法就把它当成是数学上的集,那么并集、交集、差集都可以玩通了。
    • 并集 OR
      • print( A.union(B) ) # All unique elements in A or B
      • print( A | B ) # A OR B
    • 交集 AND
      • print( A.intersection(B) ) # All elements in both A and B
      • print( A & B ) # A AND B
    • 差集 A - B
      • print( A.difference(B) ) # Elements in A but not in B
      • print( A - B ) # A MINUS B
    • 差集 B - A
      • print( B.difference(A) ) # Elements in B but not in A  
      • print( B - A ) # B MINUS A  
    • 对称差集 XOR
      • print( A.symmetric_difference(B) ) # All elements in either A or B, but not both  
      • print( A ^ B ) # A XOR B  
  • set优点缺点
    • 优点:不用判断重复的元素  
    • 缺点:不能存储可变对象  
  • if-elif-else 语句
  • 对于迭代循环,Python 里面有「while 循环」和「for 循环」,没有「do-while 循环」。
  • 通用形式的 for loop 如下
    • for a in A
      • do something with a
    • 其中 for 和 in 是关键词,A 是个可迭代数据 (list, tuple, dic, set),a 是 A 里面的每个元素
  • 最后介绍一个稍微有点特殊的函数 enumerate(),和 for loop 一起用的语法如下
    • for i, a in enumerate(A)
      • do something with a  
    • 发现区别了没?用 enumerate(A) 不仅返回了 A 中的元素,还顺便给该元素一个索引值 (默认从 0 开始)。此外,用 enumerate(A, j) 还可以确定索引起始值为 j。
      • languages = ['Python', 'R', 'Matlab', 'C++']
      • for i, language in enumerate(languages, 1):
        • print( i, 'I love', language )  
      • print( 'Done!' )
      • # 1 I love Python
      • 2 I love R
      • 3 I love Matlab
      • 4 I love C++
      • Done!
  • 数据类型分两种:
    • 单独类型:整型、浮点型、布尔型  
    • 容器类型:字符、元组、列表、字典、集合  

  • Python 的函数具有非常灵活多样的参数形态。从简到繁的参数形态如下:
  1. 位置参数 (positional argument)
  2. 默认参数 (default argument)
  3. 可变参数 (variable argument)
  4. 关键字参数 (keyword argument)
  5. 命名关键字参数 (name keyword argument)
  6. 参数组合
  • 位置参数
    • arg1 - 位置参数 ,这些参数在调用函数 (call function) 时位置要固定。
  • 默认参数
    • arg2 = v - 默认参数 = 默认值,调用函数的时候,默认参数已经有值,就不用再传值了。
    • 默认函数一定要放在位置参数后面,不然程序会报错。
    • 在调用参数把它的「关键字」也带上,我们就可以随便调换参数的顺序。
  • 可变参数
    • *args - 可变参数,可以是从零个到任意个,自动组装成元组。
    • 可变参数用两种方式传入
    1. 直接传入,func(1, 2, 3)
    2. 先组装列表或元组,再通过 *args 传入,func(*[1, 2, 3]) 或 func(*(1, 2, 3))
  • 关键字参数
    • **kw - 关键字参数,可以是从零个到任意个,自动组装成字典。
    • 「可变参数」和「关键字参数」的同异总结如下:
      • 可变参数允许传入零个到任意个参数,它们在函数调用时自动组装为一个元组 (tuple)
      • 关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典 (dict)
    • 如果不传入任何「关键字参数」,kw 为空集。
    • 除了直接传入多个参数之外,还可以将所有参数先组装成字典 Conv,用以「**Conv」的形式传入函数 (Conv 是个字典,前面加个通配符 ** 是拆散字典,把字典的键值对传入函数中)
 1 # =============================================================================
 2 # id: MM1001
 3 # notional: 10
 4 # reporting currency: EUR
 5 # present value: 150
 6 # keyword: {'dc': 'act/365', 'bdc': 'following'}
 7 # =============================================================================
 8 DCF = (1, 2, 3, 4, 5)
 9 Conv = {'dc':'act/365', 'bdc':'following'}
10 instrument5( 'MM1001', 10, 'EUR', *DCF, **Conv )
  • 命名关键字参数
    • *, nkw - 命名关键字参数,用户想要输入的关键字参数,定义方式是在nkw 前面加个分隔符 *。
    • 如果要限制关键字参数的名字,就可以用「命名关键字参数」
    • 使用命名关键字参数时,要特别注意不能缺少参数名。
 1 def instrument6( id, ntl=1, curR='CNY', *, ctp, **kw ):
 2     print( 'id:', id )
 3     print( 'notional:', ntl )
 4     print( 'reporting currency:', curR )
 5     print( 'counterparty:', ctp )
 6     print( 'keyword:', kw)
 7     
 8 # =============================================================================
 9 # id: MM1001
10 # notional: 100
11 # reporting currency: EUR
12 # counterparty: GS
13 # keyword: {'dc': 'act/365'}
14 # =============================================================================
15 instrument6( 'MM1001', 100, 'EUR',
16               dc='act/365', ctp='GS' )
17 
18 # =============================================================================
19 # TypeError: instrument6() takes from 1 to 3
20 # positional arguments but 4 were given
21 # =============================================================================
22 instrument6( 'MM1001', 100, 'EUR',
23               'GS', dc='act/365' )

  • 参数组合
    • 在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:
      • 位置参数、默认参数、可变参数和关键字参数。
      • 位置参数、默认参数、命名关键字参数和关键字参数。
    • 要注意定义可变参数和关键字参数的语法:
      • *args 是可变参数,args 接收的是一个 tuple
      • **kw 是关键字参数,kw 接收的是一个 dict
    • 命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符 *,否则定义的是位置参数。
    • 警告:虽然可以组合多达 5 种参数,但不要同时使用太多的组合,否则函数很难懂。
  • 匿名函数
    • 在 Python 里有两种函数
      • 用 def 关键词的正规函数
      • 用 lambda 关键词的匿名函数
    • lambda *args: sum(args);输入是任意个数的参数,输出是它们的和
    • lambda **kwargs: 1;输入是任意键值对参数,输出是 1
    • 对于 lambda 函数,有时我们会过用 (overuse) 它或误用 (misuse) 它。
    • Misuse
      • 误用情况:如果用 lambda 函数只是为了赋值给一个变量,用 def 的正规函数。
        • <function <lambda> at 0x000001997AA721E0>
        • <function sqr at 0x000001997AA72268>
        • lbd_sqr 的返回值是以 <lambda> 标识的函数,而 sqr 的返回时是以 sqr 为标识的函数,明显后者一看就知道该函数是「计算平方」用的。
    • Overuse
      • 过用情况:如果一个函数很重要,它需要一个正规的名字。
        • sorted(colors, key=lambda c: (len(c), c.casefold()))
        • 坦白的说,这样用 lambda 函数看起来是很酷,但是增加了使用者的「思考成本」,用 def 显性定义个函数可读性会好很多。
        • 用正规函数还能加个函数说明 (docstring),再起个描述性强的函数名,让人一看就知道该函数做什么。
  • 高阶函数
    • 高阶函数 (high-order function) 在函数化编程 (functional programming) 很常见,主要有两种形式:
      • 参数是函数 (map, filter, reduce)
      • 返回值是函数 (closure, partial, currying)
    • Map, Filter, Reduce
      • Python 里面的 map, filter 和 reduce 属于第一种高阶函数,参数是函数。
      • map(函数 f, 序列 x):对序列 x 中每个元素依次执行函数 f,将 f(x) 组成一个「map 对象」返回 (可以将其转换成 list 或 set)
      • filter(函数 f, 序列 x):对序列 x 中每个元素依次执行函数 f,将 f(x) 为 True 的结果组成一个「filter 对象」返回 (可以将其转换成 list 或 set)
      • reduce(函数 f, 序列 x):对序列 x 的第一个和第二个元素执行函数 f,得到的结果和序列 x 的下一个元素执行函数 f,一直遍历完的序列 x 所有元素。
      • map_iter = map( lambda x: x**2, lst ) # <map object at 0x0000018C83E72390>
        • 注意 map_iter 是 map 函数的返回对象 (它是一个迭代器),想要将其内容显示出来,需要用 list 将其转换成「列表」形式。
      • 惰性求值 (lazy evaluation) 也称为传需求调用 (call-by-need),目的是最小化计算机要做的工作。
      • 在上例中,map 函数作用到列表,并不会立即进行求平方,而是当你用到其中某些元素时才去求平方。惰性是指,你不主动去遍历它,就不会计算其中元素的值。
      • 为什么要有 「惰性求值」呢?在本例看起来毫无必要,但试想大规模数据时,一次性处理往往抵消而且不方便,而惰性求值解决了这个问题,它把计算的具体步骤延迟到了要实际用该数据的时候。
      • 惰性序列可以看作是一个流 (flow),需要的时候从其中取一滴水。
      • filter_iter = filter(lambda n: n % 2 == 1, lst)
        • 同样,filter_iter 作为 filter 函数的返回对象,也是一个迭代器,想要将其内容显示出来,需要用 list 将其转换成「列表」形式。
      • 最后来看看 reduce 函数,顾名思义就是累积函数,把一组数减少 (reduce) 到一个数。
      • 在 reduce 函数的第三个参数还可以赋予一个初始值,这是累积从 100 和列表 lst = [1,2,3,4,5] 的第一个元素 1 开始,一直加到整个 lst 元素遍历完,因此最后求和为 115。
1 from functools import reduce
2 
3 lst = [1, 2, 3, 4, 5]
4 
5 reduce( lambda x,y: x+y, lst ) # 15
6 
7 reduce( lambda x,y: x+y, lst, 100 ) # 115
      • 小结一下,对于 map, filter 和 reduce,好消息是,Python 支持这些基本的操作;而坏消息是,Python 不建议你使用它们。下节的「解析式」可以优雅的替代 map 和 filter。 
      • 除了 Python 这些内置函数,我们也可以自己定义高阶函数,如下:
 1 def apply_to_list( fun, some_list ):
 2     return fun(some_list)
 3 
 4 lst = [1, 2, 3, 4, 5]
 5 
 6 print( apply_to_list( sum, lst ) ) # 15
 7 
 8 print( apply_to_list( len, lst ) ) # 5
 9 
10 print( apply_to_list( lambda x:sum(x)/len(x), lst ) ) # 3.0    
  • 闭包
    • Python 里面的闭包 (closure) 属于第二种高阶函数,返回值是函数。下面是一个闭包函数。
      • 此函数的作用是做一个计数器,可以
      • 用增加子函数 inc() 续一秒
      • 用减少子函数 dec() 废一秒
      • 用获取子函数 get() 看秒数
      • 用重置子函数 reset() 回原点
    • 属于第二类 (返回值是函数) 的高阶函数还有「偏函数」和「柯里化」
 1 def make_counter(init):
 2     counter = [init]
 3     
 4     def inc(): counter[0] += 1
 5     def dec(): counter[0] -= 1
 6     def get(): return counter[0]
 7     def reset(): counter[0] = init
 8         
 9     return inc, dec, get, reset
10 
11 inc, dec, get, reset = make_counter(0)
12 
13 inc()
14 inc()
15 inc()
16 
17 get() # 3
  • 偏函数
    • 偏函数 (paritial function) 主要是把一个函数的参数 (一个或多个) 固定下来,用于专门的应用上 (specialized application)。要用偏函数用从 functools 中导入 partial 包
    • 我们知道 sort 函数默认是按升序排列,假设在你的应用中是按降序排列,你可以把函数里的 reverse 参数设置为 True。这样每次设定参数很麻烦,你可以专门为「降序排列」的应用定义一个函数,比如叫 sorted_dec,用偏函数 partial 把内置的 sort 函数里的 reverse 固定住,代码如下:
 1 from functools import partial
 2 
 3 lst = [3, 1, 2, 5, 4]
 4 
 5 sorted( lst ) # [1, 2, 3, 4, 5]
 6 
 7 sorted( lst, reverse=True ) # [5, 4, 3, 2, 1]
 8 
 9 sorted_dec = partial( sorted, reverse=True )
10 
11 sorted_dec # functools.partial(<built-in function sorted>, reverse=True)
12 
13 sorted_dec( lst ) # [5, 4, 3, 2, 1]
    • 小结,当函数的参数个数太多,需要简化时,使用 functools.partial 可以创建一个新的函数,即偏函数,它可以固定住原函数的部分参数,从而在调用时更简单。
  • 柯里化
    • 最简单的柯里化 (currying) 指的是将原来接收 2 个参数的函数 f(x, y) 变成新的接收 1 个参数的函数 g(x) 的过程,其中新函数 g = f(y)。
    • 通过嵌套函数可以把函数 add1 转换成柯里化函数 add2。
    • 仔细看看函数 add1 和 add2 的参数
      • add1:参数是 x 和 y,输出 x + y
      • add2:参数是 x,输出 x + y
      • g = add2(2):参数是 y,输出 2 + y
    • 比较「普通函数 add1」和「柯里化函数 add2」的调用,结果都一样。 
 1 def add1(x, y):
 2     return x + y
 3     
 4 def add2(x):
 5     def add(y):
 6         return x + y
 7     return add    
 8 
 9 add1 # <function __main__.add1(x, y)>
10 
11 add2 # <function __main__.add2(x)>
12 
13 g = add2(2)
14 g # <function __main__.add2.<locals>.add(y)>
15 
16 print( add1(2, 3) ) # 5
17 
18 print( add2(2)(3) ) # 5
19 
20 print( g(3) ) # 5
  • 解析式
    • 大框架
      • 解析式 (comprehension) 是将一个可迭代对象转换成另一个可迭代对象的工具。
      • 上面出现了两个可迭代对象 (iterable),不严谨地说,容器类型数据 (str, tuple, list, dict, set) 都是可迭代对象。
        • 第一个可迭代对象:可以是任何容器类型数据。
        • 第二个可迭代对象:看是什么类型解析式:
          • 列表解析式:可迭代对象是 list
          • 字典解析式:可迭代对象是 dict
          • 集合解析式:可迭代对象是 set
1 # list comprehension
2 [值 for 元素 in 可迭代对象 if 条件]
3 
4 # dict comprehension
5 {键值对 for 元素 in 可迭代对象 if 条件}
6 
7 # set comprehension
8 {值 for 元素 in 可迭代对象 if 条件} 
    • 列表解析式

 

    • 其他解析式

      • 再回顾下三种解析式,我们发现其实它们都可以实现上节提到的 filter 和 map 函数的功能,用专业计算机的语言说,解析式可以看成是 filter 和 map 函数的语法糖。
        • 语法糖 (syntactic sugar):指计算机语言中添加的某种语法,对语言的功能没有影响,但是让程序员更方便地使用。
        • 语法盐 (syntactic salt):指计算机语言中添加的某种语法,使得程序员更难写出坏的代码。
        • 语法糖浆 (syntactic syrup):指计算机语言中添加的某种语法,没能让编程更加方便。
      • 首先发现两者都是把原列表根据某些条件转换成新列表,再者
        • 「列表解析式」用 if 条件来做筛选得到 item,再用 f 函数作用到 item 上。
        • 「map/filter」用 filter 函数来做筛选,再用 map 函数作用在筛选出来的元素。
      • 为了达到相同目的,明显「列表解析式」是种更简洁的方式。
    • 小例子
      • 问题:用解析式将二维元组里每个元素提取出来并存储到一个列表中。如果我们想把上面「二维元组」转换成「二维列表」呢?
1 tup = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
2 
3 flattened = [x for t in tup for x in t]
4 
5 flattened # [1, 2, 3, 4, 5, 6, 7, 8, 9]
6 
7 [ [x for x in t] for t in tup ] # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
      • 问题:用解析式把以下这个不规则的列表 a 打平 (flatten)?
        • 用解析式一步到位解决上面问题有点难,特别是列表 a 不规则,每个元素还可以是 n 层列表,因此我们需要递推函数 (recursive function),即一个函数里面又调用自己。
        • 正规 (递推) 函数写好了,把它写成匿名函数也很简单了。
 1 a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
 2 
 3 def f(x):
 4     if type(x) is list:
 5         return [y for l in x for y in f(l)]
 6     else:
 7         return [x]
 8 
 9 f(a) # [1, 2, 3, 4, 5, 6, 7, 8]
10 
11 
12 a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
13 
14 f = lambda x: [y for l in x for y in f(l)] if type(y) is list else [x]
15 
16 f(a) # [1, 2, 3, 4, 5, 6, 7, 8]
      • 优雅清晰是 python 的核心价值观,高阶函数和解析式都符合这个价值观。
      • 函数包括正规函数 (用 def) 和匿名函数 (用 lambda),函数的参数形态也多种多样,有位置参数、默认参数、可变参数、关键字参数、命名关键字参数。匿名函数主要用在高阶函数中,高阶函数的参数可以是函数 (Python 里面内置 map/filter/reduce 函数),返回值也可以是参数 (闭包、偏函数、柯里化函数)。
      • 解析式并没有解决新的问题,只是以一种更加简洁,可读性更高的方式解决老的问题。解析式可以把「带 if 条件的 for 循环」用一行程序表达出来,也可以实现 map 加 filter 的功能。
      • 最后用 Tim Peters 的 The Zen of Python 结尾。
        • Beautiful is better than ugly.
        • Explicit is better than implicit.
        • Simple is better than complex.
        • Complex is better than complicated.
        • Flat is better than nested.
        • Sparse is better than dense.
        • Readability counts.
        • Special cases aren't special enough to break the rules.
        • Although practicality beats purity.
        • Errors should never pass silently.
        • Unless explicitly silenced.
        • In the face of ambiguity, refuse the temptation to guess.
        • There should be one -- and preferably only one -- obvious way to do it.
        • Although that way may not be obvious at first unless you're Dutch.
        • Now is better than never.
        • Although never is often better than right now.
        • If the implementation is hard to explain, it's a bad idea.
        • If the implementation is easy to explain, it may be a good idea.
        • Namespaces are one honking great idea -- let's do more of those!
原文地址:https://www.cnblogs.com/pegasus923/p/11289321.html