您的位置:首页 > 编程语言 > Python开发

python基础学习(二)---函数

2018-01-09 23:25 357 查看
函数的定义

函数的调用和参数

函数的参数
位置参数

默认参数

可变参数

关键字参数

参数组合

高阶函数
mapreduce

filter

sorted排序

返回函数

匿名函数

偏函数

函数的定义

在Python中,使用def来定义一个函数,如下:

def my_max(x, y):
if x > y:
return x
else:
return y


如果想定义一个空函数,则在函数体中使用
pass
语句即可。

函数的调用和参数

Python的函数在调用时可以返回不只一个结果,默认的结果都保存在一个tuple中直接返回,可以使用多个参数按照顺序接受数据,也可以使用一个参数直接接受整个tuple。

def my_max_min(x, y):
if x > y:
return x,y
else:
return y,x
b = my_max_min(11,22)
print(b)
----------
输出打印
(22, 11)


Python传入的参数分为四种,

函数的参数

位置参数

位置参数就是一般我们常见的python函数的传参方式,按照一定的顺序写入参数,

def my_max(x, y):
if x > y:
return x
else:
return y


默认参数

但是在一些情况下,特别是面向对象编程时,函数的一些参数一般使用的都是一样的值或是默认值,则是,如果在每次调用函数时都需要再写一遍的话,就会造成冗余或代码不整洁,此时可以使用默认参数,将默认的参数或重复使用的参数直接写入函数中,在调用时无需直接指定,也可以强制改变为别的值。

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def great_than(x, y = 100):
if x > y:
print("%d great_than %d" % (x,y))
else:
print("%d less than %d" % (x,y))

great_than(200)
great_than(30)
------
输出:
200 great_than 100
30 less than 100
30 less than 50


可变参数

可变参数允许函数传入参数的数目是可变的,可以是1个,2个甚至是0个。一般在输入多个参数时,要么使用位置参数指定个数,要么使用list或tuple来将参数打包传入,但是这样都会比较繁琐,使用可变参数可以不受限制的将任意多个参数传入函数,当然在取用时,函数需要知道传入可变参数的格式或规律。

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def my_sum(numbers):
sum = 0
for x in numbers:
sum = sum + x
return sum

a = my_sum((1,2,3,4,5,6,67))
print("get %d" % a)


上述函数的输入参数其实是一个tuple,使用可变参数后,在
number
前加
*
,即可使用可变参数,调用时直接输入函数,无需在组合成tuple或list再输入。其实可变参数只是将输入的多个参数,自动的组合成一个tuple,然后在函数内使用。

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def my_sum(*numbers):
sum = 0
for x in numbers:
sum = sum + x
return sum

a = my_sum(1,2,3,4,5,6,67)
print("get %d" % a)


关键字参数

与可变参数不同,关键字参数允许传入0个或任意个含参数名的参数,这些参数在函数中自动组合成一个dict供使用。关键字参数在参数名前面加
**
来表示,如
def human(name, age, **kw)
在函数中通过
if x in kw
语句来检查传入的关键字参数是否有想要的值。

def human(name, age, **kw):
print("name:%s" % name)
print("age:%d" % age)
if 'city' in kw:
print("city:%s" % kw['city'])
if 'job' in kw:
print("job:%s" % kw['job'])

human('lisa', 23, city='L.A', job='teacher')


如果想限定传入关键字参数的内容,可以使用命名关键字参数
def human(name, age, *, city, job):
,注意,这种用法只能在python3中使用,python2.7并不支持。而如果列表中已经有一个可变参数,那么后面的关键字参数就不需要加
*
了。

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def human2(name, age, *, city, job):
print("name:%s" % name)
print("age:%d" % age)
print("city:%s" % city)
print("job:%s" % job)
human2('lisa', 23, city='L.A', job='teacher')
————————————————————————
输出:
name:lisa
age:23
city:L.A
job:teacher


参数组合

以上四种参数类型可以在函数中同时调用,但是必须按照位置参数、默认参数、可变参数、命令关键字参数和关键字参数的顺序调用,python解释器会将传入的参数按照顺序填入函数中,并进行解析,
def human(name, age, country="China", *argv, city, job, **kw)


高阶函数

在python中,函数也可以作为参数传入另一个函数中,这样使用函数作为参数的函数就被称为高阶函数。函数也可以使用一个变量指向它,然后在之后的使用中,就可以使用该变量代替该函数的使用

def abs_sum(x, y, f):
return f(x) + f(y)

f = abs
a = abs_sum(-10, -20, f)
print("abs_sum:%d" % a)


map/reduce

python内建了一些高阶函数,其中就有大名鼎鼎的
map
reduce
,他们的都传入两个参数,

map中传入的第一个参数是一个函数
func
,该函数只传入一个参数,第二个参数是一个Iterable的序列,然后返回一个
Iterator
map
函数的作用是将函数
func
依次作用到序列的每个值,并将结果作为
Iterator
打包返回,比如使用
map
实现一些特殊规律的
list


>>> map(lambda x:x*x+x, range(10))
[0, 2, 6, 12, 20, 30, 42, 56, 72, 90]


在reduce函数中,传入的第一个参数也是函数
func
,但是该函数必须传入两个参数,第二个参数也是一个Iterable的序列,结果也是返回一个结果,该结果类型取决于hunc函数的返回类型,
reduce
首先将函数
func
作用与序列的前两个元素,再继续作用于上一步得到的结果和第三个元素,依此类推,直到序列全部的都被计算过,形象点的看有点像贪吃蛇,以典型的高阶函数来写就是
f(f(f(f(x1,x2),x3),x4),x5)
层层嵌套的函数执行。reduce简单的应用可以实现累加、str2num的转换等函数,而且只需要简单的代码即可实现

>>> a
[1, 2, 3, 6, 4, 3, 1]
>>> reduce(lambda x, y: str(x) + str(y), a)
'1236431'


filter

filter函数对序列的传入参数和处理过程与map一致,但是有两点不同,首先,
filter
根据func处理该元素的返回值决定是否保留该元素(为True则保留,为False则移除),而不是直接保留
func
的返回值,其次,根据上一步的要求,
filter
返回的值就是对序列进行过滤指定的序列,类似于原始序列的子序列,而不是得到一个新的序列,虽然在内存上来说这是一个新的序列。下面的例子过滤掉序列中的偶数,保留奇数。

>>> a
[1, 2, 3, 6, 4, 3, 1]
>>> filter(lambda x:x%2, a)
[1, 3, 3, 1]


另一个filter的应用是生成指定范围的素数,使用埃式筛法,比如500以内的素数,1和2是素数,从2开始,将把2以后所有的数除以2,如果余数为0的那么就不是素数,将他们从序列中移除,然后对保留序列的下一个值3再进行一次过滤,随后的数字可以被3整除的都不是素数,将他们从序列中移除,这样依次类推直到序列最后一个数字。注意这里使用的是python3,python2.7无法运行。

#!/usr/bin/python3
# -*- encoding:utf-8 -*-

def ori():
n = 1
while True:
n = n + 2
yield n

def _not_div(n):
return lambda x: x % n >0

def get_sushu():
it = ori()
while True:
n = next(it)
yield n
it = filter(_not_div(n), it)

g = get_sushu()
for n in g:
if n < 50:
print(n)
else:
break


sorted排序

sorted默认会将序列的元素按照升序排列,并输出一个新的序列。

>>> sorted([1, 4, -2, 3, -23, 45])
[-23, -2, 1, 3, 4, 45]


作为一个高阶函数,
sorted
也可以接收一个函数作为传入参数,通过一个
key
传入,接下来会将传入的函数依次作用给各个元素,并按照作用后的结果对元素进行排序,如下是将数字按照绝对值大小进行排序。

>>> sorted([1, 4, -2, 3, -23, 45], key=abs)
[1, -2, 3, 4, -23, 45]


字符串也可以进行排序,排序规则是按照首字母的ASCII进行排序,也可以传入函数,将字符串按照处理后的结果进行排序。

>>> sorted(['Apple', 'banana', 'dog', 'Fly'])
['Apple', 'Fly', 'banana', 'dog']
>>> import string
>>> sorted(['Apple', 'banana', 'dog', 'Fly'], key=string.lower)
['Apple', 'banana', 'dog', 'Fly']


如果需要降序排列,则将关键字
reverse
设为
True


>>> sorted(['Apple', 'banana', 'dog', 'Fly'], key=string.lower, reverse=True)
['Fly', 'dog', 'banana', 'Apple']


返回函数

既然一个函数可以作为参数传入另一个函数中,那么一个函数也可以作为一个函数的返回值,传递给函数外的变量。这里的返回函数与直接将一个变量指向函数有很大不同,
f = abs
这种其实只是创造了一个
abs
函数指针的副本,返回函数的情况如下:

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def lazy_multi(*argv):
def multi():
m = 1
for n in argv:
m = m * n
return m
return multi

f1 = lazy_multi(1,2,3,4,5)
print(f1)
print(f1())

f2 = lazy_multi(2,4,6,8)
print(f2)
print(f2())
————————————————————
输出:
<function multi at 0x7f95fe2cf668>
120
<function multi at 0x7f95fe2cf6e0>
384


注意到,
f1
获取返回函数后,不会立即执行,所以
print(f1)
只打印出了f的类型和指针地址,只有使用
f1()
时,函数才真的执行并返回结果,这一特性。而且
f1
f2
的内容互相不会干扰,各自参数的作用域只在各自的
lazy_multi
中,而各自执行的参数和返回值都保存在各自的返回函数中,这种特性被称为闭包(Closure)。

但是在使用闭包时需要注意,由于返回函数时在执行时才被计算,所以在变量获取返回函数时,实际上获取的是一系列的函数和变量,所以如果函数内部产生多个返回函数,并将这些函数赋给多个变量,需要注意函数内变量的最终输出,是在所有返回函数都被输出的时候。

#!/usr/bin/python
# -*- encoding:utf-8 -*-

def foo():
fs = []
for n in range(3):
def ff():
return n
fs.append(ff)
return fs

f1,f2,f3 = foo()
print(f1)
print(f1())
print(f2)
print(f2())
print(f3)
print(f3())
————————————————————————
输出:
<function ff at 0x7f455959f758>
2
<function ff at 0x7f455959f7d0>
2
<function ff at 0x7f455959f848>
2


可以看到,这里所有的输出都是2,而不是预计中的0,1,2,这是因为在执行的时候,三个函数的参数
n
都是执行到最后
f3
的时候的值。这里可以使用两层嵌套函数,将
n
的值准确的传递到内层函数,这样在执行的时候,内层函数的变量就不会随着代码逻辑自然改变。

def foo():
fs = []
def f_out(n):
def ff():
return n
return ff
for n in range(3):
fs.append(f_out(n))
return fs

f1,f2,f3 = foo()

print(f1)
print(f1())

print(f2)
print(f2())

print(f3)
print(f3())
——————————————————————
输出:
<function ff at 0x7feb7ce437d0>
0
<function ff at 0x7feb7ce43848>
1
<function ff at 0x7feb7ce438c0>
2


在使用返回函数,特别是返回多个函数时,需要特别注意这一点。

匿名函数

在执行简单运算时,如果不想要麻烦的单独写一个函数,就可以使用匿名函数
lambda
,直接使用表达式作为一个函数使用,这样的好处首先是不需要额外的动作去新建函数,代码很简洁,其次是不需要指定函数名,这在一些复杂的应用中可以避免函数名的重复。

在之前的内容有很多地方都运用了匿名函数
lambda
,它的基本格式是
lambda values:statement [condition1 [condition2 ..]]
lambda
表达式之后紧跟着变量名,变量可以是一个或多个变量,随后是
分隔符,之后是需要执行的语句
statement
,最后是语句执行的条件或循环范围,如
for
循环或
if
条件语句,整体结构和
for
循环一致,所以执行的顺序是,首先执行
for
循环,然后执行
if
等条件语句,最后执行
statement
,并返回值。
lambda
也是一个函数,所以可以使用变量直接指向它,获取的数据类型是一个
function


>>> f = lambda x:x * x * x
>>> f
<function <lambda> at 0x7fe5eb4a3668>
>>> f(10)
1000


偏函数

在介绍默认函数时,我们可以使用默认参数来设置函数执行时的默认变量,降低函数调用时的复杂度和难度,在一些内置函数中其实也有默认参数,在使用时可以通过指定内置函数的默认参数实现不同的函数功能。

>>> int('100')
100
>>> int('100', base=2)
4
>>> int('10000', base=2)
16


可以看到指定
base=2
之后,
int
函数会对传入的
str
数字字符串做二进制转换
int
类型。但是在重复调用时,如果每次都要指定
base
也比较麻烦,所以python提供了一个
functools
模块,其中的偏函数(partiial)功能,就可以指定一个函数的默认参数并固化下来,变成一个新的函数,这样一个函数就可以方便的衍生出很多不同功能的函数,大大提高了代码的复用率和coding的效率。

>>> import functools
>>> int('10000')
10000
>>> int8 = functools.partial(int, base=8)
>>> int8('10000')
4096
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 编程语言