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

时间:2022-07-26 07:55:42

  函数

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

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

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

 >>> def func(l=[]):
... l.append('end')
... return l
...
>>>

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

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

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

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

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

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

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

 >>> def func(L=None):
... if L is None:
... L=[]
... L.append('End')
... return L
...
>>> func()
['End']
>>> func()
['End']
>>> func()
['End']
>>>

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

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

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

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

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

 >>> def func(name,age,**kw):
... print('name:',name, 'age:',age, 'other:',kw)
...
>>> func('Jack',39)
('name:', 'Jack', 'age:', 39, 'other:', {})
>>> func('Jack',39,gender='M',city='Bj')
('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的值。我们可以这样:

 >>> def func(x,y):
... return x+y
...
>>> func(1,2)
3
>>> func = lambda x,y:x+y
>>> func(1,2)
3

  lambda生成一个函数对象。

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

 >>> def test(f,a,b):
... print 'f is a function'
... print f(a,b)
...
>>> test((lambda x,y:x+y), 6, 9)
f is a function
15

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

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

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

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

  闭包:

  看下面这个例子:

 >>> def line_conf():
... b = 15
... def line(x):
... return 2*x+b
... return line
...
>>> b = 5
>>> my_line = line_conf()
>>> print(my_line(5))
25

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

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

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

 >>> def count():
... fs = []
... for i in range(1,4):
... def f():
... return i * i
... fs.append(f)
... return fs
...
>>> f1,f2,f3 = count()
>>> f1()
9
>>> f2()
9
>>> f3()
9

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

 >>> def line_conf():
... b = 15
... def line(x):
... return 2*x+b
... b = 100
... return line
...
>>> my_line = line_conf()
>>> print(my_line(5))
110

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

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

 >>> def count():
... fs = []
... for i in range(1,4):
... def f(j):
... def g():
... return j*j
... return g
... fs.append(f(i))
... return fs
...
>>> f1,f2,f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9

  未完待续