6. Python 函数

1. 函数的写法,函数的调用
【函数的一般形式】定义一个函数要有以下规则:
(1)函数代码块以 def 关键词开头,后接函数标识符名称和小括号();
(2)任何传入参数和自变量必须放在小括号内,小括号内可以用于定义参数;
(3)函数的第一行语句可以选择性的使用文档字符串----用于函数说明;
(4)函数内容以冒号:起始,并且 "缩进"。

例子:
def sum(x, y): # x, y 是形参,函数名称自定义,但是不要和系统函数、特殊关键字重复。
prnit ('x = {0}'.format(x))
print ('y = {0}'.format(y))
return x + y # 返回值

#下面调用函数
sum(1,3) 或者 sum(y=6, x=10)


2. 函数参数的几种不同方法
【函数的参数】
(1)给b变量设定一个默认值
def funcA(a, b=0):
print ('a = {0}'.format(a))
print ('b = {0}'.format(b))

#调用函数
funcA(1)
返回结果:
a = 1
b = 0

(2)如果实参(即10和20)传入的时候指定b的值,优先选择传入的实参;
当b没有值传入时,才会使用默认值。
def funcA(a, b=0):
print ('a = {0}'.format(a))
print ('b = {0}'.format(b))

#调用函数
funcA(10, 20)
返回结果:
a = 10
b = 20

(3)参数为元组(tuple)
def funcA(a, b, *c):
print ('a = {0}'.format(a))
print ('b = {0}'.format(b))
print "length of c is: {0}".format(len(c))
print ('c = {0}'.format(c))

#调用函数
funcA(1, 2, 3, 4, 5, 6)
#解释:1对应a, 2对应b,*c代表元组,将3,4,5,6添加到元组中去
返回结果:
a = 1
b = 2
length of c is: 4
c = (3, 4, 5, 6)

(4)参数为字典(dict)
def funcA(a, **c):
print (a)
print (c)
for i in c:
print i + ":" + str(c[i])

#调用函数
funcA(100, x="hello",y="world")
print ('#' * 20)
cs = {'1':'aaaa','2':'bbbb'} #定义一个字典
funcA(a = 99, **cs)

返回结果:
100
{'y': 'world', 'x': 'hello'}
y:world
x:hello
####################
99
{'1': 'aaaa', '2': 'bbbb'}
1:aaaa
2:bbbb

1. 高阶函数
高阶函数就是把函数当成参数传递的一种函数
举例:
def add(x, y, f):
return f(x) + f(y)
print (add(-8, 11, abs))

返回结果:
19
① map() 函数
map() 函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。


举例:
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def f(x):
return x * x
map(f, lst)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

map(str, lst)
['1', '2', '3', '4', '5', '6', '7', '8', '9']

注释:
map()传入的第一个参数是f,即函数对象本身

② reduce() 函数
reduce把一个函数作用在一个序列[x1, x2, x3...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算。
格式:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

#传入的函数必须接受两个参数;
#把可迭代对象的前两个参数作为函数的实参,传入到f函数中;
#把每次f运算的结果作为第一个实参,可迭代对象的下一个元素作为另一个实参,传入函数f中;
#以此类推,最终得到结果。

举例:
def add(x, y):
return x + y

reduce(add, [1, 3, 5, 7, 9])
结果:
25

③ filter() 函数
Python内建的filter()函数用于过滤序列。
和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

例1,在一个list中,删掉偶数,只保留奇数:
def is_odd(n):
return n % 2 == 1

filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])
结果:
[1, 5, 9, 15]

例2,把一个序列中的空字符串删掉:
def not_empty(s):
return s and s.strip()
filter(not_empty, ['A', '', 'B', None, 'C', ' '])
结果:
['A', 'B', 'C']

④ sorted() 函数
排序算法
排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。
通常规定,对于两个元素x和y,如果认为x < y,则返回-1,如果认为x == y,则返回0,如果认为x > y,则返回1,
这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。
Python内置的sorted()函数
可以对list进行排序:
sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]

可以对dict进行排序:
mm = dict( a=1, b=2, c=3, d=4)
print sorted(mm.iteritems(), key = lambda d:d[0], reverse = True) #python2.*
print sorted(mm.items(), key = lambda d:d[0], reverse = True) #python3.*
【关于iteritems:
在Python2.x中
items( )用于返回一个字典的拷贝列表【Returns a copy of the list of all items (key/value pairs) in D】,占额外的内存。
iteritems() 用于返回本身字典列表操作后的迭代【Returns an iterator on all items(key/value pairs) in D】,不占用额外的内存。
Python 3.x 中
iteritems() 和 viewitems() 这两个方法都已经废除了,而 items() 得到的结果是和 2.x 里面 viewitems() 一致的。在3.x 里 用 items()替换iteritems() ,可以用于 for 来循环遍历。

此外,sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。
比如,如果要倒序排序,我们就可以自定义一个reversed_cmp函数:
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0

传入自定义的比较函数reversed_cmp,就可以实现倒序排序:
sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
【python2和3的sorted有作改动:
python2

python3
Python3.x和Python2.x的sorted函数有点不太一样,少了cmp参数
为了保证代码通用性,不建议大家在今后的编程中使用cmp参数。

我们再看一个字符串排序的例子:
sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
现在,我们提出排序应该忽略大小写,按照字母序排序。
要实现这个算法,不必对现有代码大加改动,只要我们能定义出忽略大小写的比较算法就可以:
def cmp_ignore_case(s1, s2):
u1 = s1.upper()
u2 = s2.upper()
if u1 < u2:
return -1
if u1 > u2:
return 1
return 0

忽略大小写来比较两个字符串,实际上就是先把字符串都变成大写(或者都变成小写),再比较。
这样,我们给sorted传入上述比较函数,即可实现忽略大小写的排序:
sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)
结果:
['about', 'bob', 'Credit', 'Zoo']
从上述例子可以看出,高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。

2. 匿名函数
顾名思义---> 没有名字的函数

形式1:
def sum(x, y):
return x+y
print (sum(4, 5))

形式2:
m = lambda x,y:x+y
print (m(4, 5))

形式1 和 形式2 结果一样


1. 生成式和生成器
列表生成式是python受欢迎的一种语法之一,通过一句简洁的语法,就能对元组元素进行过滤,还可以对得到的元素进行转换处理。
语法格式:
[exp for val in collection if condition]
相当于
result = []
for val in collection:
if (condition):
result.append(exp)

例子:
a = [x*x for x in xrange(10) if x%2 == 0]
print (type(a))
print (a)
结果:
<type 'list'>
[0, 4, 16, 36, 64]
解释:
① 由此取出xrange(10)从0到9
② 判断 x*x 是偶数,就保留,存在新的字典中
③ 把所有符合x*x是偶数的元素都放到新的列表中返回。

通过列表生成式,我们可以创建一个列表,但是,受到内存限制,列表容量肯定是有限的;
如果创建一个包含100万个元素的的列表,不仅占用很大的存储空间,当我们仅仅需要访问前面的几个元素,后面绝大多数元素占的空间都浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素?
这样一来就不必创建完整的list了,从而节省大量的空间。
在python中,这种一边循环一边计算的机制,称为"生成器"(Generator)

生成器是一次生成一个值的特殊类型函数,可以将其视为可恢复函数,调用该函数将返回一个可用于生成连续 x 的值的生成器;
简单的说就是在函数执行过程中,yield 语句会把你需要的的值返回给调用生成器的地方,然后退出函数,下次调用生成器函数
的时候,又从上次中断的地方开始执行,而生成器内的所有变量参数会被保存下来供下一次使用。

生成式和生成器类似于range和xrange,注意xrange可不是生成器,只是意思相似。

要创建生成器有好几种方法:
第一种方法
把一个列表生成式的[],改成(),这就创建了一个生成器。
例子:
lst = (x*x for x in xrange(1,101) if x%2 == 0)
print (lst)
print (type(lst))
print (lst.next())
print (lst.next())
print (lst.next())
print (lst.next())
print (lst.next())
print (lst.next())
结果:
<generator object <genexpr> at 0x02E72508>
<type 'generator'>
4
16
36
64
100
144

解释:generator 保存的是算法,每次调用next(),就计算出下一个元素的值,直到计算到最后一个元素为止。

第二种方法:
函数中定义列表生成器,即如果函数中包含yield关键字,那么这个函数不再是一个普通函数,而是一个generator。
普通函数:
def func(n):
sum = 0
i = 0
while(i<n):
sum = sum + i
i += 1
print (sum)
func(10)
结果:
0
1
3
6
10
15
21
28
36
45

列表生成器:
def func(n):
sum = 0
i = 0
while(i<n):
sum = sum + i
i += 1
yield (sum)
for x in func(10):
print x
print (type(func(10)))
结果:
0
1
3
6
10
15
21
28
36
45
解释:
① 以上函数有关键字 yield ,所以生成的是一个生成器;
② 通过for 循环调用生成器,当执行到yield的时候,返回sum值,sum为0,此时暂停并记录sum的值;
③ 打印出sum的值,然后继续往下去执行,跳入下一个循环 while(1<10)
④ 直到遇到yield的时候,返回sum的值
⑤ 反复执行3,4的步骤,直到循环结束,最终退出程序。

两个函数的区别:
一个直接反回了表达式的结果列表,另一个是一个对象,该对象包含了对表达式结果的计算引用,通过循环可以直接输出。
生成器不会一次性列出所有数据,当你用到的时候,再列出来,更加节约内存使用率。

普通函数和列表生成器的区别:
结果虽然相同,但是包含yield语句的函数会特地编译成生成器,当函数被调用的时候,他们返回一个生成器对象,这个对象支持迭代器接口,
每当遇到yield关键字的时候,可以理解成函数的return语句,yield后面的值,就是返回值。但是不像一般函数在return后退出,生成器函数在生成
值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效,下次从yield下面的部分开始执行。
比如说上一次执行到3,下次开始时,找到3的位置,从6开始执行,(不会从头开始执行),以此类推。

生成式:一次性生成所有数据,然后保存在内存中,适合小量的数据。
生成器:返回一个可迭代的对象,即"generator"对象,必须通过循环才可以一一列出所有结果。


2. 迭代器
iterable (可迭代对象) 和 iterator(迭代器)主要区别:
凡是可以用 for 循环的都是iterable(可迭代对象),可以通过循环调用出来的都是,比如:[],(),{},生成式....
凡是要通过 next()函数调用并获得值的可迭代对象都是iterator(迭代器)
所以生成器可以被next()函数调用并不断返回下一个值的对象称为迭代器
可以简单理解为 生成器 就是迭代器的可迭代对象。

凡是可作用于 for 循环的对象都是iterable 类
凡是可作用于 next()函数的对象都是iterator类型,他们表示一个惰性计算的序列。

作业:
九九乘法表
def func(n):
return ["{0}*{1}={2}".format(x,n,x*n) for x in xrange(1,n+1) ]

for i in xrange(1,10):
print " ".join(func(i))

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页