函数——Python学习笔记03

函数

Python不但非常灵活地定义函数,而且本身内置了很多有用的函数,可以直接调用。
函数是最基本的一种代码抽象的方式。

调用函数
要调用一个函数,需要知道它的名称和参数,可以通过Python的官网查看文档.
也可以在交互命令行用help()查看要调用的函数的帮助信息。
调用函数时,如果传入的参数数量或参数类型不对,会报TypeError的错误,Python会显示错误的原因。
abs()有且只有一个参数,返回参数的绝对值。max()函数可以接受多个参数,并返回最大的一个。

数据类型转换
Python内置的常用函数还包括数据类型转换函数,比如int()函数可以把其他数据类型抓换为整数:

>>>int(12.3)
12
>>>int(‘123’)
123
>>>float(’12.34’)
12.34
>>>str(123)
‘123’
>>>bool(1)
True
>>>bool(‘')
False

函数名是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给函数起了别名:

>>>a = abs           #变量a指向abs函数
>>>a(-1)               #所以也可以通过a调用abs函数
1

利用Python内置的hex()函数把一个整数转换成十六进制的字符串:

n = int(input(‘请输入一个整数:’))
a = hex(n)
print('转换成的十六进制字符串:%s' % a) 

定义函数

定义函数时,需要确定函数名和参数个数。
在Python中,定义一个函数需要用def()语句,依次写出函数名、括号、括号中的参数和冒号:,然后在缩进块中编写函数整体,函数的返回。
函数体内部可以用return随时返回函数结果,如果执行完毕也没有return语句时,自动return None。
如定义一个求绝对值的my_abs()函数:

def my_abs(x):
    if x >=0:
        return x
    else:
        return -x

保存为py文件后可以直接在IDLE中运行,或者在py文件目录下启动python,用from 定义函数文件名(无需py后缀) import my_abs() 导入my_abs()函数。
也可以直接在Python交互环境中定义,然后直接使用函数。

空函数
可以用pass语句定义一个什么也不做的空函数:

def nop():
    pass

pass语句什么也不做,但是可以充当占位符,比如现在还没想好怎么写函数代码,就可以先放一个pass能让代码运行起来。

参数检查
当传入了不恰当的参数时,函数会检查出参数错误,如果定义函数没有参数检查,那么这个函数是不完整的。
修改之前自定义的my_abs()函数的定义:对参数类型做检查,只允许整数和浮点数类型的参数。数据类型检查可以用内置函数isinstance()实现:

def my_abs(x):
        if not isinstance(x,(int,float)):
            raise TypeError(‘bad operand type’)
        if x >= 0:
            return x
        else:
            return -x

添加了参数检查后,如果传入错误的参数类型,函数就能抛出一个错误了。

返回多个值
Python的函数返回多值时就是返回一个tuple,在语法上,返回一个tuple可以省略括号,而多个变量可以同时接收一个tuple,按位置赋给对应的值。

练习
请定义一个函数quadratic(a,b,c),接收3个参数,返回一元二次方程:
ax2 +bx+c=0
的两个解(计算平方根可以调用math.sqrt()函数)。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import math
def quadratic(a,b,c):
    if not (isinstance(a,(int,float)) and isinstance(b,(int,float)) and isinstance(c,(int, float))):
        raise TypeError('bad operand type')
    if b*b - 4*a < 0:
        return '方程式无解'
    elif a !=0 :
        x = (-b + math.sqrt(b*b - 4*a*c)) / (2*a)
        y = (-b - math.sqrt(b*b - 4*a*c)) / (2*a)
        return x,y
    elif b !=0:
        return -c / b
    elif c != 0:
        return 'c也需为0'
    else:
        return None

函数的参数

定义函数时,我们把参数的名字和位置确定下来,函数的接口定义就完成了。函数调用者只需知道如何传递正确的参数和返回值,无需了解函数逻辑。

位置函数
当调用一个函数时,传入的参数必须和定义的参数个数,类型一致。传入的值按照位置依次赋给参数。

默认参数
默认函数可以简化函数的调用。
定义函数中一个参数的默认值后,不传入此参数的值,则使用定义的默认值。如:

def power(x,n = 2):
    s = 1
    while n >0:
        n = n-1
        s = s * x
    return s

定义n = 2后,就可以把n当成默认参数了:

>>>power(5)
25
>>>power(5,2)
25

对于n>2的情况,就可以明确传入n的值。
设置默认参数时,需要注意几点:
1.必选参数在前,默认参数在后,否则会报错
2.当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。
有多个默认参数时,调用时,既可以按顺序提供默认参数,也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需把参数名写上。
默认参数需牢记:默认参数必须指向不可变对象

可变对象
在python中,还可以定义可变参数,可变参数就是传入的参数是可变的。
可变参数允许你传入0个或多个参数,这些参数在函数调用时会自动组装成一个tuple。
如:给定一组数字a,b,c……,请计算a2 +b2 +c2 +……。
要定义出这个函数,我们必须确定输入的参数。由于传入的参数个数不确定,我们可以把a,b,c……作为一个list或tuple传进来,函数定义如下:

def calc(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n*n
    return sum

但是调用的时候需要组装出一个tuple或者list:

>>>calc[1,2,3]
14
>>>calc((1,3,5,7))
14

我们可以把函数参数改成可变参数,定义可变参数和定义一个list或tuple相比,仅仅在参数numbers前面加了一个*号。但是调用该函数时,可以传入任意个参数包括0个:

>>>calc(1,2,3)
14
>>>calc()
0

如果已经有一个list或tuple,要调用一个可变参数时,可以这样做:

>>>num = [1,2,3]
>>>calc(*num)
14

*num表示把num这个list的所有元素作为可变参数传进去。

关键字参数
关键字参数允许你传入0个或多个含参数名的参数,这些关键字参数在函数内部自动组装成一个dict。如:

def person(name,age,**kw):
    print('name:',name,'age:',age,'other:',kw)

在调用该函数时,可以只传入必选参数:

>>>person(‘mojian’,25)
name: mojian age: 25 other: {}

也可以传入任意个数的关键字参数:

>>> person('mojian',26,city = 'shanghai',country ='chain')
name: mojian age: 26 other: {'country': 'chain', 'city': 'shanghai’}

关键函数可以起到扩展函数功能的作用。

我们也可以组装出一个dict,然后把该dict转为关键字参数传进去:

>>>extra = {'city':'beijing','job':'Engineer'}
>>> person('jack',21,**extra)
name: jack age: 21 other: {'job': 'Engineer', 'city': 'beijing'}

**extra 表示把extra这个dict的所有key-value用关键字参数传入到函数的**kw参数,kw获得一个dict,这个dict是extra的一份拷贝,对kw的改动不会影响函数外的extra。

命名关键字参数
对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。如果要限制关键字参数的名字,就可以用命名关键字参数:

命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数,如果缺少*,后面的参数将被视为位置参数。
def person(name, age,*, city = ‘Beijing’ , job):
    print(name, age, city, job)

命名关键字参数必须传入参数名,这和位置参数不同,如果没有传入参数名,调用将报错。
命名关键字参数也可以有缺省值(默认值)
如果函数定义中已有一个可变参数kebian,后面跟着的命名关键字参数就不需特殊分隔符

参数组合
在Python中定义函数,可以用5种参数组合,但是顺序必须是:
必选参数——>默认参数——>可变参数——>命名关键字参数——>关键字参数
如定义一个包括上述类型的函数:

def f1(a, b, c = 0, *args, **kw):
    print('a =', a,'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c = 0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =',kw)

在调用函数时,Python解释器自动按照参数位置和参数名把对应的参数传进去。
另外,还可以通过一个tuple和list调用函数:

>>>num = (1,2,3)
>>>cw = {‘d’: 88, ‘x’: ‘#’}
>>>f1(*num, **cw)
a = 1b = 2 c = 3 args = () kw = {‘d’:88, ‘x’:'#’}
>>>f2(*num, **cw)
a = 1 b = 2 c = 3 d = 88 k = {‘x’:’#'}

所以,对于任意函数,无论它的参数是如何定义的,都可以通过类似func(*args,**kw)的形式调用它。

注意
默认参数一定要用不可变对象。
定义可变参数和关键字参数的语法:

*args是可变参数,args接收的是一个tuple或list;
**kw是关键字参数,kw接收的是一个dict。

调用函数时传入可变参数和关键字参数的语法:
可变参数既可直接传入:func(1, 2, 3),又可先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可直接传入:func(a = 1, b = 2),又可先组装dict,再通过**kw传入func(**{‘a’: 1, ‘b’: 2});
使用*args和**kw是Python的习惯写法,也可使用其他参数名。
命名的关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。
定义命名的关键字参数在没有可变参数时,不要忘记写分隔符*,否则定义的是位置参数。


递归函数

在函数内部,可以调用其他函数,如果调用的是本身,那么这个函数就是递归函数。

递归函数的优点是定义简单,逻辑清晰;
缺点是过深的调用会导致栈溢出:

在计算机中,函数调用是通过栈(stack)这种结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。但是栈不是大小无限的,当递归的次数过多时,会导致栈溢出。

解决递归调用栈溢出的方法是:

通过尾递归优化。尾递归是指,在函数返回时,调用自身,且,return语句不能包含表达式。

针对尾递归优化的语言可以通过尾递归防止栈溢出,尾递归和循环是等价的,可以把循环看成特殊的尾递归。Python标准的解释器并没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

递归练习题:
汉诺塔的移动可以用递归函数非常简单地实现:
请编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A、B、C中第一个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的方法:

def move(n, a, b, c):
    if n == 1:
        print(a , ‘->’, c)#只有一个盘子时,把A上的盘子移动到C上
        return
    move(n-1, a, c, b)#第一步,把A上面除最大的盘子n-1个,通过空的C移动到B上
    move(1, a, b, c)#第二步,把最大的盘子移动到C上
    move(n-1, b, a, c)#第三步,把B上的盘子通过空的A移动到C上
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值