Python学习笔记:函数

Python学习笔记:函数

学自廖雪峰巨佬的Python3教程:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143167832686474803d3d2b7d4d6499cfd093dc47efcd000

1.要调用一个函数,就需要知道函数的名称和参数,相关信息可以查看官方文档https://docs.python.org/3/library/functions.html,也可以使用help(function_name)在交互式命令行处查看函数的相关帮助信息。

2.自定义函数需要使用def语句,依次写出函数名、括号、括号中的参数和冒号,然后通过缩进编写函数体,函数的返回值用return语句返回,如果没有return语句,函数执行完毕后也会返回结果,但是结果为None,return None可以简写为return。

3.如果将自定义函数的定义保存为.py文件,那么可以在该文件的当前目录下启动Python解释器,用from [file name] import [function name]语句来导入自定义的函数,注意文件名不含.py扩展名。

4.如果想定义一个什么事都不做的空函数,则函数体可以用pass作为占位符,其他语句也可以使用pass作为占位符,比如if语句后面可以跟pass,如果缺少了pass又没有其他语句,则会报错。

5.数据类型检查可以用内置函数isinstance()实现,异常可以通过raise抛出

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

6.Python的函数可以有多个返回值,但本质上返回的是一个tuple,然后里面有多个元素而已

7.可以给Python函数的参数指定默认值,但必选参数要在前,默认参数要在后,否则Python的解释器会报错,当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面,变化小的参数就可以设定默认参数

#n为默认参数,默认值为2
def power(x, n=2):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

#如果有多个默认参数,而又想不按顺序指定某个参数的值时,需要用以下形式
enroll('Adam', 'M', city='Tianjin')

 8.定义默认参数要牢记一点,默认参数必须指定不变对象

默认参数很有用,但使用不当,也会掉坑里。默认参数有个最大的坑,演示如下:
先定义一个函数,传入一个list,添加一个END再返回:

def add_end(L=[]):
    L.append('END')
    return L
正常调用时,结果似乎不错:
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
当你使用默认参数调用时,一开始结果也是对的:
>>> add_end()
['END']
但是,再次调用 add_end()时,结果就不对了:
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

原因是: 
Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。

要修改上面的例子,可以用None来实现:

def add_end(L=None):
    if L is None:
        L = []
    L.append('END')
    return L

9.在Python函数中,还可以定义可变参数,即传入的参数个数是可变的。一般来说,这种情况可以用list或tuple加遍历来实现,但如果利用可变参数,也是可以的,只需要在参数名前加一个*号,就可以传入任意个参数,包括0个。可变参数本质上接收的是一个tuple

#使用list来实现
def calc(numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

#使用可变参数实现
def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

10.Python函数中,定义关键字参数,用以允许你传入0个或任意个含参数名的参数

#使用**进行关键字参数的定义
def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

#可以传入任意个数的关键字个数
>>> 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传参
>>> extra = {'city': 'Beijing', 'job': 'Engineer'}
>>> person('Jack', 24, **extra)
name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

如果需要知道传入了哪些参数,就需要在函数内部进行检查

def person(name, age, **kw):
    if 'city' in kw:
        # 有city参数
        pass
    if 'job' in kw:
        # 有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)

如果要限制关键字参数的名字,则在参数列表中添加一个*号,*后面的参数被视为命名关键字参数

#定义命名关键字参数
def person(name, age, *, city, job):
    print(name, age, city, job)

#调用方式如下
>>> person('Jack', 24, city='Beijing', job='Engineer')
Jack 24 Beijing Engineer

11.函数里可以写递归,只需要指定边界值和递归公式。使用递归函数需要防止栈溢出,在计算机中,函数调用是通过(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。因此可以使用尾递归的方式来进行优化(这只是算法层面的优化然而Python解释器压根没有做相关优化所以写了也没卵用该溢出还是溢出)

#阶乘递归实现
def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

#阶乘的尾递归实现
def fact(n):
    return fact_iter(n, 1)

def fact_iter(num, product):
    if num == 1:
        return product
    return fact_iter(num - 1, num * product)

#fact(5)对应的fact_iter(5,1)调用如下
===> fact_iter(5, 1)
===> fact_iter(4, 5)
===> fact_iter(3, 20)
===> fact_iter(2, 60)
===> fact_iter(1, 120)
===> 120

12.汉诺塔问题的递归实现

文章的最后,巨佬留下了一个问题,汉诺塔的递归实现,这里也想以自己的方式来阐述一下题解:

算法分析如下:

在n=1的情况下,只需要把盘子从柱A移动到C,A->C,这是递归的边界条件
在n=2的情况下,我们需要进行三步操作,A->B,A->C,B->C,那么这里可以仔细推敲一下
n=2时,有两个盘子,小盘和大盘
第一步,将小盘从A移动到B,如果将柱子顺序变为A-C-B,这一步就跟n=1的时候的移动是一样的,同样是小盘从第一个柱子移动到第三个柱子;
第二步,将大盘从A->C
第三步,将小盘从B->C,这一步,如果将柱子的顺序变为B-A-C,这一步也是跟n=1的时候的移动是一样的,同样是小盘从第一个柱子移动到第三个柱子。
因此本质上,2阶汉诺塔相当于执行了以下步骤:

(1)在A-C-B的柱子顺序下执行了一阶汉诺塔的操作
(2)移动最大盘到A->C
(3)在B-A-C的柱子顺序下执行了一阶汉诺塔的操作

推广到3阶的时候,小环和中环可以视为一个整体,实际上就变成了执行二阶汉诺塔的方法;推广到n阶的时候,我们可以将1到n-1个盘视为一个整体,n为最大盘,这就是汉诺塔的递归算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值