发布时间: 2018-08-10 14:15:26
5.1 实验介绍
5.1.1 关于本实验
本实验主要介绍了 Python 函数的相关知识点和简单操作。
5.1.2 实验目的
1.理解 Python函数的含义。
2.掌握和 Python函数相关的操作。
5.2 实验任务配置
5.2.1 概念知识
函数可以提高应用的模块性和代码的重复利用率。在Python 中,string、tuple和 number 是不可变对象,list、dict是可变对象。对于不可变类型,如整数、字符串、元组,函数调用传递的只是值,没有影响到对象本身。对于可变类型,如列表、字典,函数调用时真的将对象传过去,修改之后外部的对象也会受到影响。
5.2.2 实验内容步骤
1 常用内置函数
比如 int 函数可以把其他类型的数据转化为整数:
>>> int('123')
123
>>> int(12.34)
12
>>> float('12.34')
12.34
>>> str(1.23)
'1.23'
>>> str(100)
'100'
>>> bool(1)
True
>>>bool('')
False
步骤 2 函数名
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
>>>a = abs # 变量 a 指向 abs 函数
>>> a(-1) # 所以也可以通过 a 调用 abs 函数1
步骤 3 定义函数
在 Python 中,定义一个函数要使用 def 语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用 return 语句返回。我们以自定义一个求绝对值的 my_abs函数为例:
>>> def my_abs(x):
ifx>=0:return x
else:return–x
如果想定义一个什么事也不做的空函数,可以用 pass语句, 可以用来作为占位符。修改一下my_abs 的定义,对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数 isinstance()实现:
def my_abs(x):
ifnot isinstance(x, (int, float)):
raise TypeError('bad operand type')
ifx >= 0:return x
else:return –x
步骤 4 关键字参数
可变参数允许你传入0 个或任意个参数,这些可变参数在函数调用时自动组装为一个 tuple。而关键字参数允许你传入0 个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个 dict。
defperson(name, age, **kw):
print('name:', name, 'age:',age, 'other:', kw)
函数 person 除了必选参数 name 和 age 外,还接受关键字参数 kw。在调用该函数时,可以只传入必选参数:
>>>person('Michael', 30) name:
Michael age:30 other: {}
也可以传入任意个数的关键字参数:
>>>person('Bob', 35, city='Beijing')name:
Bob age: 35 other: {'city': 'Beijing'}
>>>person('Adam', 45, gender='M', job='Engineer')
name:Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
和可变参数类似,也可以先组装出一个 dict,然后,把该 dict 转换为关键字参数传进去:
>>>extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack',24, city=extra['city'], job=extra['job'])
name: Jack age: 24 other: {'city':'Beijing', 'job': 'Engineer'}
当然,上面复杂的调用可以用简化的写法:
>>>extra = {'city': 'Beijing', 'job': 'Engineer'}
>>>person('Jack', 24, **extra)
name: Jack age: 24 other: {'city':'Beijing', 'job': 'Engineer'}
**extra 表示把 extra 这个 dict 的所有 key-value 用关键字参数传入到函数的**kw 参数,kw 将获得一个 dict,注意 kw 获得的 dict 是 extra 的一份拷贝,对kw 的改动不会影响到函数外的extra。
步骤 5 命名关键字参数
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city 和 job 作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city,job):
print(name, age, city, job)
和关键字参数**kw 不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数。调用方式如下:
>>> person('Jack', 24, city='Beijing',job='Engineer')
Jack24 Beijing Engineer
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:
defperson(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错命名关键字参数可以有缺省值,从而简化调用:
def person(name, age, *,city='Beijing', job):
print(name, age, city, job)
由于命名关键字参数 city 具有默认值,调用时,可不传入city 参数:>>>person('Jack', 24, job='Engineer')
Jack 24 Beijing Engineer
使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*作为特殊分隔符。如果缺少*,Python 解释器将无法识别位置参数和命名关键字参数。
步骤 6 参数组合
在 Python 中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这 5 种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
比如定义一个函数,包含上述若干种参数:
deff1(a, b, c=0, *args, **kw):
print('a=', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)
deff2(a, b, c=0, *, d, **kw):
print('a =', a, 'b =', b, 'c=', c, 'd =', d, 'kw =', kw)
在函数调用的时候,Python 解释器自动按照参数位置和参数名把对应的参数传进去。
>>>f1(1, 2)
a= 1 b = 2 c = 0 args = () kw = {
>>>f1(1, 2, c=3)
a =1 b = 2 c = 3 args = () kw = {}
>>>f1(1, 2, 3, 'a', 'b')
a= 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>>f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a','b') kw = {'x': 99}
>>>f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw ={'ext': None}
最神奇的是通过一个 tuple 和 dict,你也可以调用上述函数:
>>>args = (1, 2, 3, 4)
>>>kw = {'d': 99, 'x': '#'}
>>>f1(*args, **kw)
a= 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}
>>> args = (1, 2, 3)
>>>kw = {'d': 88, 'x': '#'}
>>>f2(*args, **kw)
a = 1 b = 2 c = 3 d = 88 kw ={'x': '#'}
所以,对于任意函数,都可以通过类似 func(*args, **kw)的形式调用它,无论它的参数是如何定义的。
步骤 7 递归函数
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。栈溢出解决方法:
尾递归优化:解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以, 把循环看成是一种特殊的尾递归函数也是可以的。尾递归是指,在函数返回的时候,调用自身本身,并且,return 语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。上面的 fact(n)函数由于returnn * fact(n - 1)引入了乘法表达式,所以就不是尾递归了。要改成尾递归方式,需要多一点代码,主要是要把每一步的乘积传入到递归函数中:
deffact(n):
return fact_iter(n, 1)
deffact_iter(num, product):
if num == 1:
return product
return fact_iter(num - 1, num* product)
可以看到,return fact_iter(num - 1, num* product)仅返回递归函数本身,num - 1 和num* product 在函数调用前就会被计算,不影响函数调用。
上一篇: {人工智能}python之循环语句
下一篇: {人工智能}python編程之字符串