自学Python二 Python中的屠龙刀(续)

函数

秉承着一切皆对象的理念,函数作为对象,可以为其赋值新的对象名,也可以作为参数传递给其他函数!

  正常的诸如空函数,默认参数等等我们就不提了,在这里着重提一下默认参数里面的坑和lambda函数。

  当默认参数为空list时的坑:定义一个函数,传入一个list,添加一个end后返回

1 >>> def func(l=[]):
2 ...     l.append('end')
3 ...     return l
4 ...
5 >>>

  正常调用是没什么问题的:

1 >>> func([1,2,3])
2 [1, 2, 3, 'end']
3 >>> func(['x','a','d'])
4 ['x', 'a', 'd', 'end']

  但是如果使用默认参数的话:

>>> func()
['end']
>>> func()
['end', 'end']
>>> func()
['end', 'end', 'end']

  每次传入的都为上一个函数加了end之后的list,并不是一个空列表。原因是,函数定义之初,默认函数l已经开辟的空间为[],l是指向该list的变量,每次调用函数,传入的都为该l。所以如果改变了l的内容,下次调用时默认参数的内容也就变了,不是当初了[]

  所以默认参数——>必须指向不可变对象!

  下面让我们来优化下这个函数:

 1 >>> def func(L=None):
 2 ...     if L is None:
 3 ...             L=[]
 4 ...     L.append('End')
 5 ...     return L
 6 ...
 7 >>> func()
 8 ['End']
 9 >>> func()
10 ['End']
11 >>> func()
12 ['End']
13 >>>

  可变参数(传入的参数数目是可变的,可以是0个,1个2个多个):

  假如我们要求:给定一组数字a,b,c...请计算a+b+c+...。由于参数不固定,一般的我们会传入一个list或者tuple。在python中我们可以这样定义函数:

1 >>> def func(*args):
2 ...     sum = 0
3 ...     for i in num:
4 ...             sum = sum + i
5 ...     return sum
6 ...
7 >>> func(1,2,3,4,5)
8 15

  其实所谓的可变参数就是这些参数在函数调用的时候自动组装成了一个tuple,本质上是一样的,但是用起来方便多了!

  关键字参数(可变参数在函数调用时组装成了tuple,而关键字参数在函数调用时组装成了dict,当然你也可以直接传入一个dict):

1 >>> def func(name,age,**kw):
2 ...     print('name:',name, 'age:',age, 'other:',kw)
3 ...
4 >>> func('Jack',39)
5 ('name:', 'Jack', 'age:', 39, 'other:', {})
6 >>> func('Jack',39,gender='M',city='Bj')
7 ('name:', 'Jack', 'age:', 39, 'other:', {'gender': 'M', 'city': 'Bj'})

  在3.0+python中还可以限制关键字的名字,可以用命名关键字参数,例如只接收city作为关键字参数,则函数定义如下:

1 def person(name, age, *, city='Beijing', job):
2    print(name, age, city, job)

  接下来就是我们的重头戏了!

匿名函数:

现在要求我们要定义一个函数,返回x+y的值。我们可以这样:

1 >>> def func(x,y):
2 ...     return x+y
3 ...
4 >>> func(1,2)
5 3
6 >>> func = lambda x,y:x+y
7 >>> func(1,2)
8 3

   lambda生成一个函数对象。

  我们之前提到了,函数可以作为一个对象,作为参数传递或者作为结果输出。这是我们可以直接传入匿名函数:

1 >>> def test(f,a,b):
2 ...     print 'f is a function'
3 ...     print f(a,b)
4 ...
5 >>> test((lambda x,y:x+y), 6, 9)
6 f is a function
7 15

  上篇我们讲到了list,list的排序方法有list.sort(func=None,key=None,reverse=False)

1 >>> L = [('a',1),('c',3),('d',4),('b',2)]
2 >>> L.sort(lambda x,y:cmp(x[1],y[1]))#根据L中元素的第二个关键字排序
3 >>> L
4 [('a', 1), ('b', 2), ('c', 3), ('d', 4)]

  还有Python中内置的一些高阶函数如map()函数,reduce()函数,他们当中也可以有匿名函数一席之地:

1 >>> map((lambda x:x*x),[1,2,3,4,5])#map函数,依次对list中每个元素进行处理
2 [1, 4, 9, 16, 25]
3 >>> map(str,[1,2,3])
4 ['1', '2', '3']
5 >>> reduce(lambda x,y:x*10+y,[1,4,8,3])#reduce函数,累进的将函数作用于list的每个元素。3.0中需要引入functools包
6 1483

闭包:

  看下面这个例子:

 1 >>> def line_conf():
 2 ...     b = 15
 3 ...     def line(x):
 4 ...             return 2*x+b
 5 ...     return line
 6 ...
 7 >>> b = 5
 8 >>> my_line = line_conf()
 9 >>> print(my_line(5))
10 25

  当一个函数跟它的环境变量合在一起,就构成了一个闭包,上面的例子中,函数line所需要的b的值是函数对象定义时提供的b值而不是使用时的b值。在python中,所谓的闭包就是一个包含有环境变量取值的函数对象,环境变量的取值被保存在函数对象的__closure__属性中!

1 >>> print(my_line.__closure__[0].cell_contents)
2 15

  返回闭包时要牢记的一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。如下例所示:

 1 >>> def count():
 2 ...     fs = []
 3 ...     for i in range(1,4):
 4 ...             def f():
 5 ...                     return i * i
 6 ...             fs.append(f)
 7 ...     return fs
 8 ...
 9 >>> f1,f2,f3 = count()
10 >>> f1()
11 9
12 >>> f2()
13 9
14 >>> f3()
15 9

  我们本来想得到 f1() = 1, f2() = 4, f3() = 9。可是得到的结果全部为9.这是因为这几个函数都用到了循环变量i。当返回的时候i的值已经变成了3。现在我们把第一个例子修改一下:

 1 >>> def line_conf():
 2 ...     b = 15
 3 ...     def line(x):
 4 ...             return 2*x+b
 5 ...     b = 100
 6 ...     return line
 7 ...
 8 >>> my_line = line_conf()
 9 >>> print(my_line(5))
10 110

  所以,再提一句:返回函数不要引用任何循环变量,或者后续会发生变化的变量!

如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论循环变量后续如何更改,已绑定到函数的变量不变!

 1 >>> def count():
 2 ...     fs = []
 3 ...     for i in range(1,4):
 4 ...             def f(j):
 5 ...                     def g():
 6 ...                             return j*j
 7 ...                     return g
 8 ...             fs.append(f(i))
 9 ...     return fs
10 ...
11 >>> f1,f2,f3 = count()
12 >>> f1()
13 1
14 >>> f2()
15 4
16 >>> f3()
17 9

未完待续

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏idba

Python内置函数介绍

一 前言 在编写Python 程序或者工具脚本时,需要完成某个功能,可以选择编写一个具体的函数达到目的,当然也可以通过匿名/Python 内建函数来完成。本...

702
来自专栏星汉技术

Scala语法介绍

3265
来自专栏C语言及其他语言

C语句和程序流

1、 表达式和语句 在C中,表达式代表值,而语句代表给计算机的指令。 表达式 表达式由运算符和操作数组成。最简单的表达式只是一个不带运算符的常量或者变...

3366
来自专栏深度学习自然语言处理

Python学习——collections系列

一 ,计数器(counter) Counter是对字典类型的补充,用于追踪值得出现次数 ps:具备字典的所有功能 + 自己的功能 例: >>> from ...

36414
来自专栏Python爬虫实战

Python数据类型之列表(后续)

如图所示,有list1和list2两个列表,我们可以发现,原来列表竟然可以比较大小,在这里肯定有读者会说,123肯定小于234,但是如果我们往列表里面多添加几个...

1022
来自专栏黑泽君的专栏

c语言基础学习06_函数

============================================================================= 涉及...

3402
来自专栏土豆专栏

Java面试之关键字

finalize()是Object的protected方法,子类可以覆盖该方法来实现资源清理工作,GC在回收对象之前调用该方法。

22510
来自专栏我的技术专栏

java与C++变量初始化的对比

1063
来自专栏深度学习之tensorflow实战篇

mongodb11天之屠龙宝刀(九)js函数入门:MongoDB基于js的数据类型修改

mongodb11天之屠龙宝刀(九)js函数入门:MongoDB基于js的数据类型修改 Mongodb并不提供Alter table这样的语句或者工具修...

3354
来自专栏河湾欢儿的专栏

第九节 js里的new方法

要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4 个步骤: (1) 创建一个新对象; (2) 将构造函...

1371

扫码关注云+社区

领取腾讯云代金券