017~022 函数 课后练习题

本文深入探讨Python函数的使用,强调DRY原则,讲解为何使用函数而非简单复制代码,以及函数参数的运用。讨论了函数的参数数量、元组参数的错误用法、函数调用的输出、以及编写power()和gcd()函数的方法。此外,介绍了函数的文档字符串、默认参数与关键字参数的区别,并通过实际例子展示了内嵌函数和闭包的概念。最后,提到了递归函数的基本条件及其优缺点,并给出了递归实现阶乘和欧几里得算法的例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

17 函数:Python的乐高积木



测试题:

0.你有听说过DRY吗?

      DRY是程序员们公认的指导原则:Don’t Repeat Yourself.
      快快武装你的思维吧,拿起函数,不要再去重复拷贝一段代码了!

1.都是重复一段代码,为什么我要使用函数(而不使用简单的拷贝黏贴)呢?

     0) 可以降低代码量(调用函数只需要一行,而拷贝黏贴需要N倍代码)
     1) 可以降低维护成本(函数只需修改def部分内容,而拷贝黏贴则需要每一处出现的地方都作修改)
     2) 使程序更容易阅读(没有人会希望看到一个程序重复一万行“I love FishC.com”)

2.函数可以有多个参数吗?

        可以的,理论上你想要有多少个就可以有多少个,只不过如果函数的参数过多,在调用的时候出错的机率就会大大提高,因而写这个函数的程序员也会被相应的问候祖宗,所以,尽量精简吧,在Python的世界里,精简才是王道!

4.请问这个函数有多少个参数?

def MyFun((x, y), (a, b)):
    return x * y - a * b

【答案】 

        如果你回答两个,那么恭喜你错啦,答案是0,因为类似于这样的写法是错误的
        我们分析下,函数的参数需要的是变量,而这里你试图用“元祖”的形式来传递是不可行的。
        我想你如果这么写,你应该是要表达这么个意思:

>>> def MyFun(x, y):
        return x[0] * x[1] - y[0] * y[1]

>>> MyFun((3, 4), (1, 2))
10

5.请问调用以下这个函数会打印什么内容?

>>> def hello():
        print('Hello World!')
        return
        print('Welcome To FishC.com!')

【答案】

>>> hello()
Hello World!

【答案解析】因为当Python执行到return语句的时候,Python认为函数到此结束,需要返回了(尽管没有任何返回值)。


动动手:

0.编写一个函数power()模拟内建函数pow(),即power(x, y)为计算并返回x的y次幂的值。

【自己写的!!】

def power(x, y):
    return x**y

【参考答案】

def power(x, y):
    result = 1
    for i in range(y):
        result *= x

    return result

 【回顾】对于该题【result *= x】

        1.对于【-=】或【+=】,首先要提前规定一个量【sum = 0】
           对于【*=】,首先要规定一个量【sum = 1】

        2.对于【for i in range(y):】,后面不用出现【i】,系统自动会计算i次:

>>> sum = 0
>>> for i in range(10):
	sum += 1
	print(sum)

	
1
2
3
4
5
6
7
8
9
10
>>> result = 1
>>> for i in range(3):
        result *= 4
        print(result)

        
4
16
64

1.编写一个函数,利用欧几里得算法(脑补链接)求最大公约数,例如gcd(x, y)返回值为参数x和参数y的最大公约数。

def gcd(x, y):
    while y:
        t = x % y
        x = y
        y = t

    return x
   
print(gcd(4, 6))

2.编写一个将十进制转换为二进制的函数,要求采用“除2取余”(脑补链接)的方式,结果与调用bin()一样返回字符串形式。

def Dec2Bin(dec):
    temp = []
    result = ''
   
    while dec:
        quo = dec % 2
        dec = dec // 2
        temp.append(quo)

    while temp:
        result += str(temp.pop())
   
    return result

print(Dec2Bin(62))

018 函数:灵活即强大



测试题:

1.函数写注释有什么不同?

      给函数写文档是为了让别人可以更好的理解你的函数,所以这是一个好习惯:

>>> def MyFirstFunction(name):
        '函数文档在函数定义的最开头部分,用不记名字符串表示'
        print('I love FishC.com!')

 我们看到在函数开头写下的字符串Ta是不会打印出来的,但Ta会作为函数的一部分存储起来,这个我们称之为函数文档字符串,Ta的功能跟注释是一样的。函数的文档字符串可以按如下方式访问:

>>> MyFirstFunction.__doc__
'函数文档在函数定义的最开头部分,用不记名字符串表示'


#另外,我们用help()来访问这个函数也可以看到这个文档字符串:
>>> help(MyFirstFunction)
Help on function MyFirstFunction in module __main__:
 
MyFirstFunction(name)
函数文档在函数定义的最开头部分,用不记名字符串表示

        建议使用help 


4. 默认参数和关键字参数表面最大的区别是什么?

【参考答案】

        关键字参数是在函数调用的时候,通过参数名制定需要赋值的参数,这样做就不怕因为搞不清参数的顺序而导致函数调用出错。
        而默认参数是在参数定义的过程中,为形参赋初值,当函数调用的时候,不传递实参,则默认使用形参的初始值代替。

 

动动手:

019 函数:我的地盘听我的



0. 编写一个符合以下要求的函数:

a) 计算打印所有参数的和乘以基数(base=3)的结果
b) 如果参数中最后一个参数为(base=5),则设定基数为5,基数不参与求和计算。

def mFun(*param, base=3):
    result = 0
    for each in param:
        result += each
 
    result *= base
    
    print('结果是:', result)
 
mFun(1, 2, 3, 4, 5, base=5)

【笔记】
        细品,基数都不会求和,当base没有指定为5时默认为3,使用默认参数
        若base为5,则需要覆盖掉原来默认参数为3


 1. 寻找水仙花数 题目要求:如果一个3位数等于其各位数字的立方和,则称这个数为水仙花数。

        例如153 = 1^3+5^3+3^3,因此153是一个水仙花数。编写一个程序,找出所有的水仙花数。

def Narcissus():
    for each in range(100, 1000):
        temp = each
        sum = 0
        while temp:
            sum = sum + (temp%10) ** 3
            temp = temp // 10  # 注意这里用地板除
 
        if sum == each:
            print(each, end='\t')
 
print("所有的水仙花数分别是:", end='')
Narcissus()

2.编写一个函数 findstr(),该函数统计一个长度为 2 的子字符串在另一个字符串中出现的次数。

def findstr(source,sub):
	ans=0
	for i in range(len(source)):
		if source[i]==sub[0] and source[i+1]==sub[1]:
			ans+=1
	return ans

020 函数:内嵌函数和闭包



测试题:

0.【复习】如果希望在函数中修改全局变量的值,应该使用什么关键字?__global 关键字:

>>> count = 5
>>> def MyFun():
                global count
                count = 10
                print(count)
 
>>> MyFun()
10
>>> count
10
>>> count = 5
>>> def MyFun():
                count = 10
                print(count)
 
>>> MyFun()
10
>>> count
5
'''没有加gobal,如果对全局变量进行修改函数会启动shadowing来保护全局变量,
   在内部创建一个同名的局部变量'''

1.【复习】在嵌套的函数中,如果希望在内部修改外部函数的局部变量,应该使用什么关键字?__nonlocal关键字:

>>> def Fun1():
                x = 5
                def Fun2():
                        nonlocal x#没有这个  调用fun1会报错
                        x *= x
                        return x
                return Fun2()
 
>>> Fun1()
25

2. 【复习】Python 的函数可以嵌套,但要注意访问的作用域问题哦,请问以下代码存在什么问题呢?

def outside():
    print('I am outside!')
    def inside():
        print('I am inside!')
 
inside()

【解释】使用嵌套函数要注意一点就是作用域问题,inside() 函数是内嵌在 outside() 函数中的,在外边或者别的函数体里是无法对其进行调用的。
 【正确写法】:

def outside():
    print('I am outside!')
    def inside():
        print('I am inside!')
        
    inside()
outside()

3. 请问为什么代码 A 没有报错,但代码 B 却报错了?应该如何修改?

代码A:

def outside():
    var = 5
    def inside():
        var = 3
        print(var)
        
    inside()
outside()

代码B:

def outside():
    var = 5
    def inside():
        print(var)
        var = 3
        
    inside()
outside()

【原因分析】 
        这里 outside() 函数里有一个 var 变量,但要注意的是,内嵌函数 inside() 也有一个同名的变量,Python 为了保护变量的作用域,故将 outside() 的 var 变量屏蔽起来,因此此时是无法访问到外层的 var 变量的。
【正确写法】:

def outside():
    var = 5
    def inside():
        nonlocal var
        print(var)
        var = 3
        
    inside()
outside()


4. 请问如何访问 funIn() 呢?

def funOut():
    def funIn():
        print('宾果!你成功访问到我啦!')
    return funIn() #返回的是函数finIn的返回值  这样会执行一次funIn

【访问方式】只需要直接调用 funOut() 即可

funOut()
宾果!你成功访问到我啦!

5. 请问如何访问 funIn() 呢?

def funOut():
    def funIn():
        print('宾果!你成功访问到我啦!')
    return funIn   #少了括号  相当于返回内嵌函数的引用 和第4不同它不会执行funIn

【访问方式】区别于上一题,这里返回的是一个函数对象,那么你就需要用 funOut()() 访问啦:

funOut()()
宾果!你成功访问到我啦!

【#】当然你也可以“曲线救国”:

go = funOut()
go()
宾果!你成功访问到我啦!

6. 以下是“闭包”的一个例子,请你目测下会打印什么内容?

def funX():
    x = 5
    def funY():
        nonlocal x #把x声明为非局部变量
        x += 1
        return x
    return funY
 
a = funX()#a是一个函数对象
print(a())
print(a())
print(a())

【答案】当a=funX()的时候,只要a变量没有被重新赋值,funX()就没有被释放,也就是说局部变量x就没有被重新初始化,所以当全局变量不适用的时候,可以考虑使用闭包更稳定和安全
打印出:

6
7
8

动动手:

1. 请用已学过的知识编写程序,找出小甲鱼藏在下边这个长字符串中的密码,密码的埋藏点符合以下规律:

str1 = '''ASDASdFSDFdFDSFdGSDGVDd'''

【参考答案】

countFront = 0  # 统计前边的大写字母
countMid = 0  # 统计中间小写字母
countRear = 0  # 统计后边的大写字母
length = len(str1)
target=""
for i in range(length):
    if str1[i] == '\n':
        continue
    if str1[i].isupper():
        if countMid:
            countRear+=1
            if countRear==4:
                countRear=3
        else:
            countFront+=1
            if countFront==4:
                countFront=3
    if str1[i].islower():
        if countMid:
            countFront = 0
            countMid = 0
            countRear = 0
        else:
            countMid=1
            target=str1[i]
    if countFront==3 and countRear==3:
        print(target)
        countFront = 3
        countMid = 0
        countRear = 0

021 函数:lambda表达式



 1.请将下边的匿名函数转变为普通的屌丝函数。

lambda x : x if x % 2 else None #三目操作符

【参考答案】

def is_odd(x):
        if x % 2:
                return x
        else:
                return None 

【笔记】
        对于【 if x % 2:】,如果【 if x % 2 == 0】,则不符合规定,执行【return None】。依此原理可以找出奇数。


3. 你可以利用filter()和lambda表达式快速求出100以内所有3的倍数吗?

【参考答案】

lambda x : x if x % 3 else None

【屌丝函数形式】

def fun(x):
	if x%3==0:
		return 1
	else:
		return 0
list(filter(fun,list(range(0,100))))

4. 还记得列表推导式吗?完全可以使用列表推导式代替filter()和lambda组合,你可以做到吗?

[i for i in range(0,100) if i%3==0]
#注意和正常的for循环不同 这里不用冒号了

5. 还记得zip吗?使用zip会将两数以元祖的形式绑定在一块,例如:

>>> list(zip([1, 3, 5, 7, 9], [2, 4, 6, 8, 10]))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

但如果我希望打包的形式是灵活多变的列表而不是元祖,(希望是[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]这种形式),你能做到吗?(采用map和lambda表达式)

【参考答案】

>>> list(map(lambda x, y : [x, y], [1, 3, 5, 7, 9], [2, 4, 6, 8, 10]))
[[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]

6. 请目测以下表达式会打印什么?

def make_repeat(n):
        return lambda s : s * n
 
double = make_repeat(2) 
print(double(8))
print(double('FishC'))

【自己做的!】

double = make_repeat(2)   #返回一个s=2*s的函数
print(double(8))  #返回16
print(double('FishC'))   #返回FishCFishC

022 函数:递归是神马

        递归,函数调用本身这么一个行为



 1. 递归必须满足哪两个基本条件?

【答案】
        
(1)函数调用自身
        (2)设置了正确的返回条件

2. 思考一下,按照递归的特性,在编程中有没有不得不使用递归的情况?

【答案】
        
例如汉诺塔,目录索引(因为你永远不知道这个目录里边是否还有目录),快速排序(二十世纪十大算法之一),树结构的定义等如果使用递归,会事半功倍,否则会导致程序无法实现或相当难以理解。

3. 用递归去计算阶乘问题或斐波那契数列是很糟糕的算法,你知道为什么吗?

        “普通程序员用迭代,天才程序员用递归”这句话是不无道理的。
        但是你不要理解错了,不是说会使用递归,把所有能迭代的东西用递归来代替就是“天才程序员”了,恰好相反,如果你真的这么做的话,那你就是“乌龟程序员”啦。
        为什么这么说呢?不要忘了,递归的实现可以是函数自个儿调用自个儿,每次函数的调用都需要进行压栈、弹栈、保存和恢复寄存器的栈操作,所以在这上边是非常消耗时间和空间的。
        另外,如果递归一旦忘记了返回,或者错误的设置了返回条件,那么执行这样的递归代码就会变成一个无底洞:只进不出!所以在写递归代码的时候,千万要记住口诀:递归递归,归去来兮!出来混,总有一天是要还的

4. 请聊一聊递归的优缺点(无需官方陈词,想到什么写什么就可以)

优点:

(1)递归的基本思想是把规模大的问题转变成规模小的问题组合,从而简化问题的解决难度(例如汉诺塔游戏)。
(2)有些问题使用递归使得代码简洁易懂(例如你可以很容易的写出前中后序的二叉树遍历的递归算法,但如果要写出相应的非递归算法就不是初学者可以做到的了。)

缺点:

(1)由于递归的原理是函数调用自个儿,所以一旦大量的调用函数本身空间和时间消耗是“奢侈的”(当然法拉利也奢侈,但还是很多人趋之若鹜)。
(2)初学者很容易错误的设置了返回条件,导致递归代码无休止调用,最终栈溢出,程序崩溃。

动动手 :

0.递归编写一个 power() 函数模拟内建函数 pow(),即 power(x, y) 为计算并返回 x 的 y 次幂的值。

 def pow(x,y):
	if y==1:
		return x
	else:
		return x*pow(x,y-1)

1. 使用递归编写一个函数,利用欧几里得算法求最大公约数,例如 gcd(x, y) 返回值为参数 x 和参数 y 的最大公约数

 def gcd(x,y):
	if x%y==0:
		return y
	else:
		return gcd(y,x%y)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值