14 函数专题(一)

本章主要讨论函数形参里出现了信号在形参名前的语义。

14.1 形参名字前带一个星号*

在一些Python自带的函数里会发现函数的形参前有个星号(*)这是什么意思呢?首先来看一个str的format函数,看看这个函数的语法定义。

>>> help(str.format)
Help on method_descriptor:

format(...)
    S.format(*args, **kwargs) -> string

    Return a formatted version of S, using substitutions from args and kwargs.
    The substitutions are identified by braces ('{' and '}').

在这个函数里第一个参数是*args,第二个参数是** kwargs这个函数咋用?咋赋实参呢?

format使用示例

s = "hello {0} {1}, your name is {1} {2} ?"
t = s.format("xiao", "li", "ming")
print s
print t

程序执行结果:

hello {0} {1}, your name is {1} {2} ?
hello xiao li, your name is li ming ?

从程序的执行结果可以看出"xiao"字符串插入到了s字符串里的{0}这个地方,而“ming”插入到了{2}的地方了。{}被称之为占位符replacement fields,有花括号的地方需要format函数提供并填充,通常花括号里会有个索引,其值对应于format函数里的参数的位置信息,format参数位置顺序自左从0开始计。这样就很明白了format函数里有"xiao", "li", "ming"三个字符串作为该函数的参数,分别为0、1、2那么li", "ming"这两个字符串分别填入''your name is {1} {2}''的{1}和{2}。更多有关format的格式要求格式字可以参考在线文档。

回到format的第一个参数*args上,说了半天也没有说到这个*,是C语言的指针么?不是!这是Python里支持变长参数的一种表达方式。在函数声明里用一个形参名前加*星号,可以接收不同个数的数据,以一元组的形式在函数里体现。

def sum2(x, y):
    return x + y
def sum3(x, y , z):
    return x + y + z
def sumn(*x):
    s = 0
    #print type(x) #查看x的类型
    for z in x:
        s = s + z
    return s
print sumn(1, 2, 3)
print sumn(1, 2, 3, 4)
print sumn(1, 2, 3, 4, 5)

一般情况下一个*和不带*的形参可以共存,一般不带*的形参在前。

14.2 形参名字前带两个星号**

在理解了形参带一个星号的用途之后,在来看format的第二个参数是**kwargs这个形参前有两个*,它的用意是允许使用本函数传实参时可以采用赋值方式,即key = value,所有这样的赋值以字典的形式存在于本函数里。

还是以上边的例子为例,来看看**kwargs这样声明的形参如何使用?

s = "hello {ni} {xing}, your name is {xing} {ming} ?"
t = s.format(ni = "xiao", xing = "li", ming = "ming")
print s
print t

程序执行结果

hello {ni} {xing}, your name is {xing} {ming} ?
hello xiao li, your name is li ming ?

结果一样,只是由索引变成了变量名(键),format这样的方式实参方式数据以字典的形式存在于函数里,变量名作为字典里每项数据的键而等号后的作为该键的值。

def printd(**d):
    for k in d.keys():
        print d[k]
printd(a = 12, b = 13, c = 34)

14.3 函数预设默认值

用python内置的全局sorted()方法来对可迭代的序列排序生成新的序列,可以在Python的交互环境下查看一下sorted函数的语法定义。

>>> help(sorted)
Help on built-in function sorted in module __builtin__:

sorted(...)
    sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list

在sorted函数的形参里出现了cmp=None等字样,这个不是之前的两个星号**的问题,这个是参数预设值问题。这样设计函数的好处是,函数的使用具有多样性,可以根据实际情况使用不同的函数调用方式来完成任务。

14.3.1 先看看sorted怎么用吧!

li = [5,3,1,2,4]
ret = sorted(li)
print li
print ret

程序运行结果:

[5, 3, 1, 2, 4]
[1, 2, 3, 4, 5]

(1) sorted的reverse的默认值是False例子里sorted(li)并未对reverse设置任何值采用默认值False即升序,如果想逆序排序,在使用sorted函数时就需要明确对reverse进行赋值为True了,从而得到逆序排序,这样sorted函数的使用方式就需要传两个参数了,这样函数使用就有多样性了。

li = [5,3,1,2,4]
ret = sorted(li, reverse = True)
print li
print ret

程序执行结果:

[5, 3, 1, 2, 4]
[5, 4, 3, 2, 1]

(2)sorted的key形参,参数key官方解释如下"The value of the key parameter should be a function that takes a single argument and returns a key to use for sorting purposes. This technique is fast because the key function is called exactly once for each input record."。中文含义:key 可以接收函数,这个函数只有一个参数,函数本身会在比较排序之前作用于可迭代数据集合的每个元素项,函数的返回值作为排序依据,意思有点像数据库的键的概念。用大白话来说如果sorted所排序的集合的每个元素也是集合,sorted可以依据指定每个元素集合的某元素作为排序的依据。

li = "hello Python, life is short!".split()
print li
ret = sorted(li)
print "key = None", ret
ret = sorted(li, key = lambda x : x[0])
print "key = x[0]", ret
ret = sorted(li, key = lambda x : x[1])
print "key = x[1]", ret

程序执行结果:

['hello', 'Python,', 'life', 'is', 'short!']
key = None ['Python,', 'hello', 'is', 'life', 'short!']
key = x[0] ['Python,', 'hello', 'is', 'life', 'short!']
key = x[1] ['hello', 'short!', 'life', 'is', 'Python,']

第一次使用sorted没有用key,第二次使用sorted使用了key指定key = lambda x : x[0] 啥意思?对于li是个列表(集合),它的每个元素是字符串,也是集合类型,默认对li的排序是比较字符串首字母的大小进行排序,而key = lambda x : x[0] 的出现改变了排序规则,但还以第1个字母为基准进行排序,所以两次都是首字母排序,而第三次使用的sorted排序是以第2字母为机制排序所以后两次的排序结果不一样。

再来一个相似的例子:

li = [(5, "hello"), (3, "python"), (6, "good"), (1, "like")]
print li
ret = sorted(li)
print "key = None", ret
ret = sorted(li, key = lambda x : x[0])
print "key = x[0]", ret
ret = sorted(li, key = lambda x : x[1])
print "key = x[1]", ret

这个例子和上一个例子差别在于li的不同,但排序规则相同,程序执行结果如下,

[(5, 'hello'), (3, 'python'), (6, 'good'), (1, 'like')]
key = None [(1, 'like'), (3, 'python'), (5, 'hello'), (6, 'good')]
key = x[0] [(1, 'like'), (3, 'python'), (5, 'hello'), (6, 'good')]
key = x[1] [(6, 'good'), (5, 'hello'), (1, 'like'), (3, 'python')]

此时的li是元组组成的列表,元组有两个数据项,可以依据元组的第一项数据排序,也可依据字符串的大小关系ret = sorted(li, key = lambda x : x[1])进行排序。

(3)cmp形参,可以参考list.sort函数里对cmp的解析。其实和key差不多,key是指定元素某个位置上的值作为排序的依据,而cmp是给出一种自己定义的比较的规则,依据这个自定义的规则对要排序的对象进行排序。

一个简单的例子取理解一下cmp变量的意图。

def compare(x, y):
    return x - y
print sorted([52, 22, 14, 11, 3], cmp=compare)

结果是:

[3, 11, 14, 22, 52]

稍微复杂点儿的例子如下:

t = [(2, 1), (1, 2), (4, 9), (6, 5), (3, 9), (0, 3)]
print t
def cc(x, y):
    print x, y
    return x[1] - y[1]
x = sorted(t, cmp = cc)
print "t", t
print "x", x

这里sorted排序的是元组组成的列表t,规则为cc函数以列表t里每个元素的第2个作为比较判断的数据来排序即t[i][1],程序执行结果如下:

t [(2, 1), (1, 2), (4, 9), (6, 5), (3, 9), (0, 3)]
x [(2, 1), (1, 2), (0, 3), (6, 5), (4, 9), (3, 9)]

14.3.2 lambda表达式

在sorted函数里出现了lambda语句,这是一个表达式,更确切的说是函数,是一个一行函数,又称匿名函数,有些语言里称闭包。其语法结构如下:

lambda  传入参数 : 返回的计算表达式

lambda表达式冒号前为传入参数列表(可多个),冒号为计算表达式,通常只能跟一个表达式,可以使用if结构,如下所示。

 f = lambda x, y : x if x > y else y
 f(2, 3)

lambda表达式可以嵌套,即lambda冒号后的语句也是一个lambda表达式。

取二维列表每行最大值

li = []
li.append([11, 2, 4, 3])
li.append([12,22, 14, 30])
li.append([1, 12, 41, 34])
print li
f = lambda x : lambda y : max(y[x])
print f(0)(li)
print f(1)(li)
print f(2)(li)
lmax = [f(i)(li) for i in range(3)]
print lmax

程序执行结果:

[[11, 2, 4, 3], [12, 22, 14, 30], [1, 12, 41, 34]]
11
30
41
[11, 30, 41]

读者朋友们可以考虑一下如何通过lambda实现求每列的最大值?