Python入门篇(下)

4 函数

4.1 正规函数

Python 的函数具有非常灵活多样的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。从简到繁的参数形态如下:

  • 1.位置参数 (positional argument)
  • 2.默认参数 (default argument)
  • 3.可变参数 (variable argument)
  • 4.关键字参数 (keyword argument)
  • 5.命名关键字参数 (name keyword argument)
  • 6.参数组合

位置参数

在这里插入图片描述

  • def - 定义正规函数要写 def 关键词。
  • function_name - 函数名,起名最好有意义。
  • arg1 - 位置参数 ,这些参数在调用函数 (call function) 时位置要固定。
  • :- 冒号,在第一行最后要加个冒号。
  • “”“docstring”"" - 函数说明,给使用函数这介绍该它是做什么的。
  • statement - 函数内容。

用金融产品举例,每个产品都有自己的 ID,定义 instrument 函数,它只有一个「位置参数」。

def instrument( id ):
    print( 'id:', id )
instrument( 'MM1001' )
id: MM1001

「位置参数」可以是多个,比如 id 和 ntl (代表 notional,本金,例如债券的本金是一亿,期权的本金是一百万等等)

# 「位置参数」可以是多个,比如 id 和 ntl 
def instrument1( id, ntl ):
    print( 'id:', id )
    print( 'notional:', ntl )
instrument1( 'MM1001', 1000000 )
id: MM1001
notional: 1000000

位置参数特性:

  • 这些参数在调用函数 (call function) 时位置要固定,顺序不能打乱;
  • 任意一个位置参数没有赋值,程序会报错。

默认参数

在这里插入图片描述

  • arg2 = v - 默认参数 = 默认值,调用函数的时候,默认参数已经有值,就可以不用再传值了。

在对金融产品估值时,通常对一个单位的产品先估值,再乘以产品具体的本金。比如 1 美元的债券现值为 0.98 美元,那么 1 亿美元的债券现值为 98,000,000 美元。

# 让ntl默认等于1亿,这样可以不用再给ntl赋值
def instrument2(id, ntl=100000000):
    print( 'id:', id )
    print( 'notional:', ntl )
instrument2('MM1001')
id: MM1001
notional: 100000000
instrument2('MM1001',10000)
id: MM1001
notional: 10000

默认参数特性:

  • 默认函数一定要放在位置参数后面,不然程序会报错;
  • 默认参数可以赋值,也可以不赋值
# 如果默认参数放前面,那么就会报错
def instrument2( ntl=1, id ):
    print( 'id:', id )
    print( 'notional:', ntl )
  File "<ipython-input-34-057ffd7eae81>", line 2
    def instrument2( ntl=1, id ):
                    ^
SyntaxError: non-default argument follows default argument
# 默认参数可以有很多个
def instrument3( id, ntl=10000, curR='CNY' ):
    print( 'id:', id )
    print( 'notional:', ntl )
    print( 'reporting currency:', curR)
instrument3( 'MM1001', 100, 'USD' )
id: MM1001
notional: 100
reporting currency: USD

如果在调用函数时,如果将参数的顺序打乱了,如 ntl 和 curR 的位置写反了,得到的结果毫无意义。

instrument3( 'MM1001', 'USD' , 100)
id: MM1001
notional: USD
reporting currency: 100

在调用参数把它的「关键字」也带上,就可以随便调换参数的顺序。

instrument3( 'MM1001', curR='USD' , ntl=100)
id: MM1001
notional: 100
reporting currency: USD

可变参数

在 Python 函数中,还可以定义「可变参数」。可变参数就是传入的参数个数是可变的,可以是 0, 1, 2 到任意个。

在这里插入图片描述

  • *args - 可变参数,可以是从零个到任意个,自动组装成元组。

金融产品未来多个折现现金流 (discounted cash flow, DCF),但不知道具体多少个,这时我们可以用 *args 来表示不确定个数的 DCF。下面程序也对 DCF 加总得到产品现值 (present value, PV)

def instrument4( id, ntl=1, curR='CNY', *args ):
    PV = 0
    for n in args:
        PV = PV + n

    print( 'id:', id )
    print( 'notional:', ntl )
    print( 'reporting currency:', curR )
    print( 'present value:', PV*ntl )

如果一个产品 (单位本金) 在后 3 年的折现现金流为 1, 2, 3,将它们传入 args,计算出它的现值为 600 = 100(1+2+3)

instrument4( 'MM1001', 100, 'EUR', 1, 2, 3 )
id: MM1001
notional: 100
reporting currency: EUR
present value: 600

除了直接传入多个参数之外,还可以将所有参数先组装成元组 DCF,用以「*DCF」的形式传入函数 (DCF 是个元组,前面加个通配符 * 是拆散元组,把元组的元素传入函数中)

DCF = (1, 2, 3, 4, 5)
instrument4( 'MM1001', 100, 'EUR', *DCF )
id: MM1001
notional: 100
reporting currency: EUR
present value: 1500

可变参数特性:

  • 可以直接传入,func(x, 1, 2, 3)
  • 先组装列表或元组,再通过 *args 传入,func(x, *[1, 2, 3]) 或 func(x, *(1, 2, 3)),前面一定加个通配符 * 是拆散元组

关键字参数

在这里插入图片描述

  • **kw - 关键字参数,可以是从零个到任意个,自动组装成字典。

「可变参数」和「关键字参数」的对比:

  • 可变参数允许传入零个到任意个参数,它们在函数调用时自动组装为一个元组 (tuple)
  • 关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典 (dict)

在定义金融产品,有可能不断增加新的信息,比如交易对手、工作日惯例、工作日计数惯例等等。我们可以用「关键字参数」来满足这种需求,即用 **kw。

def instrument5( id, ntl=1, curR='CNY', *args, **kw ):
    PV = 0
    for n in args:
        PV = PV + n

    print( 'id:', id )
    print( 'notional:', ntl )
    print( 'reporting currency:', curR )
    print( 'present value:', PV*ntl )
    print( 'keyword:', kw)

如果不传入任何「关键字参数」,kw 为空集。

instrument5( 'MM1001', 100, 'EUR', 1, 2, 3 )
id: MM1001
notional: 100
reporting currency: EUR
present value: 600
keyword: {}

当知道交易对手 (counterparty) 是高盛时,给函数传入一个「关键字参数」,ctp = ‘GS’。

instrument5( 'MM1001', 100, 'EUR', 1, 2, 3, ctp='GS' )
id: MM1001
notional: 100
reporting currency: EUR
present value: 600
keyword: {'ctp': 'GS'}

当知道日期计数 (daycount) 是 act/365 时,再给函数传入一个「关键字参数」,dc = ‘act/365’。

instrument5( 'MM1001', 100, 'EUR', 1, 2, 3, 
             dc='act/365', ctp='GS' )
id: MM1001
notional: 100
reporting currency: EUR
present value: 600
keyword: {'dc': 'act/365', 'ctp': 'GS'}

除了直接传入多个参数之外,还可以将所有参数先组装成字典 Conv,用以「** Conv」的形式传入函数 (Conv 是个字典,前面加个通配符 ** 是拆散字典,把字典的键值对传入函数中)

DCF = (1, 2, 3, 4, 5)
Conv = {'dc':'act/365', 'bdc':'following'}
instrument5( 'MM1001', 10, 'EUR', *DCF, **Conv )
id: MM1001
notional: 10
reporting currency: EUR
present value: 150
keyword: {'dc': 'act/365', 'bdc': 'following'}

关键字参数特性:

  • 在函数内部自动组装为一个字典
  • 可以直接传入ctp=‘GS’;
  • 也可以传入字典Conv = {‘dc’:‘act/365’, ‘bdc’:‘following’},再通过** Conv,拆散字典

命名关键字参数

在这里插入图片描述

  • *, nkw - 命名关键字参数,用户想要输入的关键字参数,定义方式是在nkw 前面加个分隔符 *。

如果要限制关键字参数的名字,就可以用「命名关键字参数」,例如,用户希望交易对手 ctp 是个关键字参数。

def instrument6( id, ntl=1, curR='CNY', *, ctp, **kw ):
    print( 'id:', id )
    print( 'notional:', ntl )
    print( 'reporting currency:', curR )
    print( 'counterparty:', ctp )
    print( 'keyword:', kw)
instrument6( 'MM1001', 100, 'EUR', dc='act/365', ctp='GS' )
id: MM1001
notional: 100
reporting currency: EUR
counterparty: GS
keyword: {'dc': 'act/365'}

使用命名关键字参数时,要特别注意不能缺少参数名。

# 没有写参数名 ctp,因此 'GS' 被当成「位置参数」,而原函数只有 3 个位置函数,现在调用了 4 个,因此程序会报错:
instrument6( 'MM1001', 100, 'EUR', 'GS', dc='act/365' )
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-50-92dff9c9b5dd> in <module>
      1 # 没有写参数名 ctp,因此 'GS' 被当成「位置参数」,而原函数只有 3 个位置函数,现在调用了 4 个,因此程序会报错:
----> 2 instrument6( 'MM1001', 100, 'EUR', 'GS', dc='act/365' )


TypeError: instrument6() takes from 1 to 3 positional arguments but 4 were given

参数组合

在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:

  • 位置参数、默认参数、可变参数和关键字参数。
  • 位置参数、默认参数、命名关键字参数和关键字参数。

要注意定义可变参数和关键字参数的语法:

  • *args 是可变参数,args 接收的是一个 tuple
  • **kw 是关键字参数,kw 接收的是一个 dict

命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名关键字参数不要忘了写分隔符" *,"否则定义的是位置参数。

4.2 匿名函数

在 Python 里有两种函数

  • 用 def 关键词的正规函数
  • 用 lambda 关键词的匿名函数

在这里插入图片描述

  • lambda - 定义匿名函数的关键词。
  • argument_list - 函数参数,它们可以是位置参数、默认参数、关键字参数,和正规函数里的参数类型一样。
  • :- 冒号,在函数参数和表达式中间要加个冒号。
  • expression - 函数表达式,输入函数参数,输出一些值。

lambda 函数没有所谓的函数名 (function_header),这也是它为什么叫匿名函数。

func = lambda x, y: x*y# 函数输入是 x 和 y,输出是它们的积 x*y
func(2, 3)
6
func = lambda *args: sum(args) # 输入是任意个数的参数,输出是它们的和
func( 1, 2, 3, 4, 5 )
15
func = lambda **kwargs: 1 # 输入是任意键值对参数,输出是 1
func( name='Steven', age='36' )
1

匿名函数与正规函数的区别

lbd_sqr = lambda x: x ** 2
lbd_sqr
<function __main__.<lambda>(x)>
def sqr(x):
    return x ** 2

sqr
<function __main__.sqr(x)>
print(sqr(9))
print(lbd_sqr(9))
81
81

4.3 高阶函数

高阶函数 (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
lst = [1, 2, 3, 4, 5]
map_iter = map( lambda x: x**2, lst )  # 返回结果是一个map对象
# 第一个参数是一个计算平方的「匿名函数」
# 第二个参数是列表,即该「匿名函数」作用的对象
print( map_iter ) 
print( list(map_iter) )
<map object at 0x0000020CBF569B38>
[1, 4, 9, 16, 25]

注意 map_iter 是 map 函数的返回对象 (它是一个迭代器),想要将其内容显示出来,需要用 list 将其转换成「列表」形式。

惰性求值

惰性求值 (lazy evaluation) 也称为传需求调用 (call-by-need),目的是最小化计算机要做的工作。

在上例中,map 函数作用到列表,并不会立即进行求平方,而是当你用到其中某些元素时才去求平方。惰性是指,你不主动去遍历它,就不会计算其中元素的值。

为什么要有 「惰性求值」呢?在本例看起来毫无必要,但试想大规模数据时,一次性处理往往抵消而且不方便,而惰性求值解决了这个问题,它把计算的具体步骤延迟到了要实际用该数据的时候。

filter
filter_iter = filter(lambda n: n % 2 == 1, lst)
# 第一个参数是一个识别奇数的「匿名函数」
# 第二个参数是列表,即该「匿名函数」作用的对象
print( filter_iter )
print( list(filter_iter) )
<filter object at 0x0000020CBF5699E8>
[1, 3, 5]
reduce
from functools import reduce
reduce( lambda x,y: x+y, lst )
# 第一个参数是一个求和相邻两个元素的「匿名函数」
# 第二个参数是列表,即该「匿名函数」作用的对象
15
# 在 reduce 函数的第三个参数还可以赋予一个初始值,
reduce(lambda x,y: x+y, lst, 100)
115

除了 Python 这些内置函数,我们也可以自己定义高阶函数,

def apply_to_list( fun, some_list ):
    return fun(some_list)
lst = [1, 2, 3, 4, 5]
print( apply_to_list( sum, lst ) )
print( apply_to_list( len, lst ) )
print( apply_to_list( lambda x:sum(x)/len(x), lst ) )
15
5
3.0

这个 apply_to_list 函数和上面的 map, filter 和 reduce 的格式类型,第一个参数 fun 是可以作用到列表的函数,第二个参数是一个列表。

闭包

Python 里面的闭包 (closure) 属于第二种高阶函数,返回值是函数。

在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,那么就将这个函数以及用到的一些变量称之为闭包。 闭包中的return返回的是内部函数名,并且用到了外部函数的变量。

def make_counter(init):
    counter = [init] 
    # counter是外部函数的变量,在内部函数中可以调用,但是不能修改。所以这里要先把init改写成一个列表,因为列表是可修改的
    
    def inc(): counter[0] += 1 # 用增加子函数 inc() 续一秒
    def dec(): counter[0] -= 1 # 用减少子函数 dec() 废一秒
    def get(): return counter[0] # 用获取子函数 get() 看秒数 
    def reset(): counter[0] = init # 用重置子函数 reset() 回原点
        
    return inc, dec, get, reset
inc, dec, get, reset = make_counter(0)
inc()
inc()
inc()
get()
3
dec()
get()
2
def make_counter2(init):
    counter = init
    # counter是外部函数的变量,在内部函数中可以调用,但是不能修改。
    
    def inc(): counter += 1 # 内部函数不能直接修改外部函数的变量,所以调用这个函数的时候会报错
    def dec(): counter -= 1 
    def get(): return counter 
    def reset(): counter = init 
        
    return inc, dec, get, reset
inc, dec, get, reset = make_counter2(100)
inc()
---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

<ipython-input-97-52d53d3893be> in <module>
      1 inc, dec, get, reset = make_counter2(100)
----> 2 inc()


<ipython-input-96-3c5ac6bf2a7e> in inc()
      3     # counter是外部函数的变量,在内部函数中可以调用,但是不能修改。
      4 
----> 5     def inc(): counter += 1 # 内部函数不能直接修改外部函数的变量,所以调用的时候会报错
      6     def dec(): counter -= 1 #
      7     def get(): return counter #


UnboundLocalError: local variable 'counter' referenced before assignment
def make_counter2(init):
    counter = init
    # counter是外部函数的变量,在内部函数中可以调用,但是不能修改。
    
    def inc(): 
        nonlocal counter # 标明counter是外部函数的变量
        counter += 1 # 外部变量counter  +1
        
    def dec(): 
        nonlocal counter # 标明counter是外部函数的变量
        counter -= 1 # 外部变量counter  +1
    def get(): 
        return counter # 直接调用外部变量counter,没有修改,所以不需要标注
    def reset(): 
        nonlocal counter # 标明counter是外部函数的变量
        counter = init # 对外部变量counter进行重新赋值
        
    return inc, dec, get, reset
inc, dec, get, reset = make_counter2(100) # 创建4个函数
inc() 
get()
101

4.4 偏函数

偏函数 (paritial function) 主要是把一个函数的参数 (一个或多个) 固定下来,用于专门的应用上 (specialized application)。要用偏函数用从 functools 中导入 partial 包。

from functools import partial
lst = [3, 1, 2, 5, 4]
sorted( lst )
[1, 2, 3, 4, 5]
sorted( lst, reverse=True )
[5, 4, 3, 2, 1]

这样每次设定参数很麻烦,可以专门为「降序排列」的应用定义一个函数,比如叫 sorted_dec,用偏函数 partial 把内置的 sorted 函数里的 reverse 固定住

sorted_dec = partial( sorted, reverse=True ) # 第一个参数是sorted函数,第二个参数是sorted函数的一个参数“reverse=True”
sorted_dec
functools.partial(<built-in function sorted>, reverse=True)
sorted_dec(lst)
[5, 4, 3, 2, 1]

当函数的参数个数太多,需要简化时,使用 functools.partial 可以创建一个新的函数,即偏函数,它可以固定住原函数的部分参数,从而在调用时更简单。

4.5 柯里化

最简单的柯里化 (currying) 指的是将原来接收 2 个参数的函数 f(x, y) 变成新的接收 1 个参数的函数 g(x) 的过程,其中新函数 g = f(y)。

def add1(x, y):
    return x + y
def add2(x):
    def add(y):
        return x + y
    return add
  • add1:参数是 x 和 y,输出 x + y
  • add2:参数是 x,输出 x + y
  • g = add2(2):参数是 y,输出 2 + y
g = add2(2) # g是一个函数,一个输入y,输出2+y的函数
g(1) #在函数g()中输入1,然后输出2+1
3
def minus2(x):
    def minus(y):
        return x - y
    return minus
g2 = minus2(5) # g是一个函数,一个输入y,输出x-y的函数
g2(1) #在函数g2()中输入1,然后输出5-1
4
add1
<function __main__.add1(x, y)>
add2
<function __main__.add2(x)>
add2(2)
<function __main__.add2.<locals>.add(y)>

5 解析式

5.1 大框架

解析式 (comprehension) 是将一个可迭代对象转换成另一个可迭代对象的工具。

  • 第一个可迭代对象:可以是任何容器类型数据。
  • 第二个可迭代对象:看是什么类型解析式:
    • 列表解析式:可迭代对象是 list
    • 字典解析式:可迭代对象是 dict
    • 集合解析式:可迭代对象是 set

列表、字典和集合解析式的伪代码 (pseudo code)如下:

  • 列表解析式(list comprehension)
    • [值 for 元素 in 可迭代对象 if 条件]
  • 字典解析式(dict comprehension)
    • {键值对 for 元素 in 可迭代对象 if 条件}
  • 集合解析式(set comprehension)
    • {值 for 元素 in 可迭代对象 if 条件}

解析式 input-operation-output 这个过程总结:

  • input:任何「可迭代数据 A」
  • operation:用 for 循环来遍历 A 中的每个元素,用 if 来筛选满足条件的元素 Agood
  • output:将 Agood 打包成「可迭代数据」,生成列表用 [],生成字典和集合用 {}

5.2 列表解析式

问题1:如何从一个含整数列表中把奇数 (odd number) 挑出来?

lst = [1, 2, 3, 4, 5] # 现在有一个列表lst
odds = [] # 定义一个空列表odds,用来装奇数
for n in lst: 
    # 循环遍历整个列表
    if n % 2 == 1:
        # 如果列表中的某一个数n,除以2的余数是1
        odds.append(n)
        # 那么就将这个数放到列表odds中
odds
[1, 3, 5]
odds = [n for n in lst if n % 2 == 1] # 列表解析式:对于列表lst中的每一个元素,如果是奇数,则返回这个数,并最终组合成一个新的列表
odds
[1, 3, 5]

在这里插入图片描述

可以把「for 循环」到「解析式」的过程想像成一个「复制-粘贴」的过程:

  • 1.将「for 循环」的新列表复制到「解析式」里
  • 2.将 append 里面的表达式 n * 2 复制到新列表里
  • 3.复制循环语句 for n in lst 到新列表里,不要最后的冒号
  • 4.复制条件语句 if n%2 == 1 到新列表里,不要最后的冒号

问题2:如何用「列表解析式」将一个二维列表中的元素按行一个个展平?

m = [[1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]]
flattened = [] # 创建一个空列表,用来装展开后的列表
for row in m: 
    # 对列表中的每一行(子列表)进行循环
    for n in row:
        # 对每一行(子列表)中的每一个元素进行循环
        flattened.append(n)
        # 把子列表中的每一个元素都装到新列表flattered中
flattened
[1, 2, 3, 4, 5, 6, 7, 8, 9]
flattened2 = [n for row in m for n in row]
flattened2
[1, 2, 3, 4, 5, 6, 7, 8, 9]

在这里插入图片描述

两点需要注意:

  • 该例没有「if 条件」条件,或者认为有,写成「if True」。如果有「if 条件」那么直接加在「内 for 循环」后面。
  • 「外 for 循环」写在「内 for 循环」前面。

我们把「列表解析式」那一套举一反三的用到其他解析式上,用下面两图理解一下「字典解析式」和「集合解析式」。

字典解析式

在这里插入图片描述

集合解析式

在这里插入图片描述

回顾三种解析式,我们发现其实它们都可以实现上节提到的 filter 和 map 函数的功能,用专业计算机的语言说,解析式可以看成是 filter 和 map 函数的语法糖。

  • 语法糖 (syntactic sugar):指计算机语言中添加的某种语法,对语言的功能没有影响,但是让程序员更方便地使用。
  • 语法盐 (syntactic salt):指计算机语言中添加的某种语法,使得程序员更难写出坏的代码。
  • 语法糖浆 (syntactic syrup):指计算机语言中添加的某种语法,没能让编程更加方便。

在这里插入图片描述

两者都是把原列表根据某些条件转换成新列表

  • 「列表解析式」用 if 条件来做筛选得到 item,再用 f 函数作用到 item 上。
  • 「map/filter」用 filter 函数来做筛选,再用 map 函数作用在筛选出来的元素。

为了达到相同目的,明显「列表解析式」是种更简洁的方式。

用「在列表中先找出奇数再乘以 2」的例子,对于列表 lst = [1, 2, 3, 4, 5],我们先看「列表解析式」的实现:

lst = [1, 2, 3, 4, 5]
[ n*2 for n in lst if n%2 == 1]
[2, 6, 10]
list(map(lambda n : n*2, filter(lambda n: n%2 == 1, lst)))
[2, 6, 10]

5.3 实例

问题1:用解析式将二维元组里每个元素提取出来并存储到一个列表中。

tup = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
[n for t in tup for n in t]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

把上述列表改成一个二位列表

lst = [] # 创建一个大列表
for t in tup:
    # 对元组里面的每一个子元组进行循环
    lst2 = [] # 创建一个子列表,用来装子元组里面的元素
    for n in t:
        # 对子元组里面的每一个元素进行循环
        lst2.append(n)
        # 将子元组里面的每一个元素转到子列表里面
    lst.append(lst2)
    # 将子列表装到大列表里面
lst
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[n for n in t] for t in tup]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

问题2:用解析式把以下这个不规则的列表 a 打平 (flatten)

a = [1, 2, [3, 4], [[5, 6], [7, 8]]]

用解析式一步到位解决上面问题有点难,特别是列表 a 不规则,每个元素还可以是 n 层列表,因此我们需要递推函数 (recursive function),即一个函数里面又调用自己。

def f(x):
    if type(x) is list:
        return [y for l in x for y in f(l)]
    else:
        return [x]

f(a)
[1, 2, 3, 4, 5, 6, 7, 8]
def f(x):
    if type(x) is list:
        a = []
        for l in x:
            for y in f(l):
                a.append(y)
        return a
    else:
        return [x]
f(a)
[1, 2, 3, 4, 5, 6, 7, 8]
a = [1, 2, [3, 4], [[5, 6], [7, 8]]]
f = lambda x: [y for l in x for y in f(l)] \
                if type(x) is list else [x]
f(a) 
[1, 2, 3, 4, 5, 6, 7, 8]

6 总结

本节讨论了函数和解析式。优雅清晰是 python 的核心价值观,高阶函数和解析式都符合这个价值观。

函数包括正规函数 (用 def) 和匿名函数 (用 lambda),函数的参数形态也多种多样,有位置参数、默认参数、可变参数、关键字参数、命名关键字参数。匿名函数主要用在高阶函数中,高阶函数的参数可以是函数 (Python 里面内置 map/filter/reduce 函数),返回值也可以是参数 (闭包、偏函数、柯里化函数)。

解析式并没有解决新的问题,只是以一种更加简洁,可读性更高的方式解决老的问题。解析式可以把「带 if 条件的 for 循环」用一行程序表达出来,也可以实现 map 加 filter 的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值