零基础入门学习Python P18-20 函数、函数的参数、过程,内嵌函数与闭包

209 阅读12分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

P18 函数

使得程序更加精美、有逻辑,可看小强,需要把程序拆分成合理的最小组成部分。 拆分方法:函数、对象、模块

函数:完成某个小功能的代码块 函数需要被调用,代码才会被执行。调用函数时,会往上寻找函数代码块,索引代码块要在调用位置之上,否则会报错。

函数是参数数量是没有上限的,但一般建议在3-4个之内。 要写好函数注释,方便后续调用时快速理解函数功能。

函数的返回值 用return 返回函数结果。执行return时,函数会结束执行。

测试题

  1. 你有听说过DRY吗?

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

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

  2. 创建函数使用什么关键字,要注意什么? def 函数名(参数): 函数体 使用“def”关键字,要注意函数名后边要加上小括号“()”,然后小括号后边是冒号“:”,然后缩进部分均属于函数体的内容

  3. 请问这个函数有多少个参数? def MyFun((x, y), (a, b)): return x * y - a * b

2个,1个逗号算1个,调用时传2个参 0,因为类似于这样的写法是错误的! 我们分析下,函数的参数需要的是变量,而这里你试图用“元祖”的形式来传递是不可行的。

  1. 请问调用以下这个函数会打印什么内容? def hello(): print('Hello World!') return print('Welcome To FishC.com!')

'Hello World!' 打印完就return了

编程作业

  1. 编写一个函数power()模拟内建函数pow(),即power(x, y)为计算并返回x的y次幂的值。
print('--------Fred-2022-7-25-----------')

def power(x,y):
    result = x

    for i in range(y):
        result *= x
    return result

print(power(2,8))
  1. 编写一个函数,利用欧几里得算法(脑补链接)求最大公约数,例如gcd(x, y)返回值为参数x和参数y的最大公约数。
def gcd(x,y):
    if x<y:
        x,y = y,x
    if x % y == 0:
        return y

    return gcd(y,x % y)
    
print(gcd(128,56))
  1. 编写一个将十进制转换为二进制的函数,要求采用“除2取余”(脑补链接)的方式,结果与调用bin()一样返回字符串形式。
def bina(x):
    result = ''
    while x:
        result = str(x % 2) + result
        x //= 2
        
    return result

print(bina(19))

  1. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!

P19 函数的参数

形参:函数创建和定义过程中的参数: def func(a: int): 中的 a 实参:调用函数时传入的参数,是具体的值: func(11) 中的 11

函数文档

函数注释:在def下面一行,用字符串可以写注释 通过 func.doc 可以打印出注释 同理,自定义函数、内置函数,都可以通过__doc__ 打印出注释 因此写函数时,要记得写好注释,方便后续理解。 image.png

关键字参数

在多个参数调用的函数中使用,避免参数因顺序错误而导致函数执行出错。 实参调用函数时,实参使用形参赋值的形式,例如: def func(a,b): 函数体 func(a = 1, b = 2) 当函数参数越来越多的时候,关键字参数作用越大。

默认参数

在函数定义过程中,给参数一个默认值(初值)。 在调用过程中,有默认值的参数不一定需要传参,没传参的默认参数,其值默认。 注意:默认参数要放在后面,非默认参数要放在前面,调用时,前面的非默认参数一定要按顺序传参,默认参数可穿可不传。 image.png

收集参数(可变参数)

当不确认函数参数个数是,可以定义可变参数 调用时,可以传入任意个实参,此时会生成一个函数参数元组 def func(*参数名): 函数体 如果可变参数后面还有其他参数,那么在调用时,其他参数要使用关键词参数调用,且放在最后。关键字参数前面的参数都会被收集给可变参数。

image.png

image.png

print 就是一个带有收集参数的函数。

课后题

  1. 请问以下哪个是形参哪个是实参? def MyFun(x): return x ** 3 y = 3 print(MyFun(y))

x 是MyFun函数的形参 y 是MyFun函数的实参 MyFun(y) 返回的结果是 print 函数的实参

  1. 函数文档和直接用“#”为函数写注释有什么不同? #在__doc__中不会显示

  2. 使用关键字参数,可以有效避免什么问题的出现呢? 关键字过多,调用函数时实参位置出错导致的函数出错

  3. 使用help(print)查看print()这个BIF有哪些默认参数?分别起到什么作用? print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

    Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream. 将值打印到流或sys。默认stdout。 可选关键字参数: File:类文件对象(流);默认为当前sys.stdout。 Sep:插入值之间的字符串,默认为空格。 End:字符串附加在最后一个值之后,默认为换行符。 flush:是否强制刷新流。

  4. 默认参数和关键字参数表面最大的区别是什么? 默认参数是函数定义时设置的,用于给参数一个初始值,默认参数可穿可不传。默认参数值是唯一的。 关键字参数值是变化的。

编程作业

  1. 编写一个符合以下要求的函数: a) 计算打印所有参数的和乘以基数(base=3)的结果 b) 如果参数中最后一个参数为(base=5),则设定基数为5,基数不参与求和计算。
print('--------Fred-2022-7-26-----------')

def base(*x,b=3):
    l = list(x)
    if l[len(l)-1] == 5:
        b = 5
        del l[len(l)-1]

    print(sum(l)*b)

base(3,4,5,6)
base(3,4,5)

image.png

  1. 寻找水仙花数

题目要求:如果一个3位数等于其各位数字的立方和,则称这个数为水仙花数。例如153 = 1^3+5^3+3^3,因此153是一个水仙花数。编写一个程序,找出所有的水仙花数。

def isflower(i:int):
    sum = 0
    a = i
    while i:
        sum += (i%10) ** 3
        i = i // 10
    return a == sum


def findflower():
    for x in range(100,1000):
        if isflower(x):
            print(x)

findflower()

image.png

  1. 编写一个函数 findstr(),该函数统计一个长度为 2 的子字符串在另一个字符串中出现的次数。例如:假定输入的字符串为“You cannot improve your past, but you can improve your future. Once time is wasted, life is wasted.”,子字符串为“im”,函数执行后打印“子字母串在目标字符串中共出现 3 次”。

程序执行效果:

image.png

def findstr(long, short):
    count = 0
    while True:
        if long.find(short) != -1:
            long = long[:long.find(short)]+long[long.find(short)+len(short):]
            count += 1
        else:
            break
    return count


long_str = input('请输入字符串:')
short_str = input('请输入子字符串:')

print(findstr(long_str, short_str))

image.png

  1. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式! 收集参数

P20 函数与过程

Python只有函数,没有过程。 没有返回值的函数,会默认返回None对象

返回值

Python是动态确定参数类型,是动态的。 python没有变量,只有名字。 返回多个值,有2种方法: return [a,b,c] :接收到一个list = [a,b,c] return a,b,c :接收到一个元组 tuple = (a,b,c)

变量的作用域

局部变量

函数中的变量叫局部变量,只在函数中生效,在函数执行结束后,整个函数栈会被清空,局部变量就会被释放,无法在其他函数,或程序主体中使用。

全局变量

作用于整个文件,在函数内部也可以使用全局变量。 全局变量的使用要非常小心,最好不要在函数中修改全局变量,读取就好。 在函数中为全局变量赋值,相当于在函数中创建了一个同名局部变量,会随着函数执行结束被清空释放,其中函数中的赋值不会带到主程序中。

课后题

  1. 下边程序会输入什么? def next(): print('我在next()函数里...') pre()

def pre(): print('我在pre()函数里...')

next()

我在next()函数里... 我在pre()函数里...

  1. 请问以下这个函数有返回值吗? def hello(): print('Hello FishC!')

None

  1. 请问Python的return语句可以返回多个不同类型的值吗? 可以,返回一个list,或者逗号隔开,自动编程一个元组tuple

  2. 目测以下程序会打印什么内容: def fun(var): var = 1314 print(var, end='')

var = 520 fun(var) print(var)

1314 520

  1. 目测以下程序会打印什么内容? var = ' Hi '

def fun1(): global var var = ' Baby ' return fun2(var)

def fun2(var): var += 'I love you' fun3(var) return var

def fun3(var): var = ' 小甲鱼 '

print(fun1())

Baby I love you

编程作业

  1. 编写一个函数,判断传入的字符串参数是否为“回文联”(回文联即用回文形式写成的对联,既可顺读,也可倒读。例如:上海自来水来自海上)

程序执行结果如图:

image.png

  1. 编写一个函数,分别统计出传入字符串参数(可能不只一个参数)的英文字母、空格、数字和其它字符的个数。

程序执行结果如图:

image.png

def huiwen(x):
    while x:
        if len(x) == 1:
            return True
        elif x[0] != x[len(x)-1]:
            return False
        x =  x[1:len(x)-1]
        
    return True

s = input('输入字符串判断是否为回文数:')
print(huiwen(s))

image.png

  1. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!

P21 内嵌函数与闭包

global 关键字

仍然想在函数中修改全局关键字,就需要用global 关键字

def func(): global 全局函数变量 修改全局函数

image.png

内嵌函数(内部函数)

Python允许在函数内部,再定义内部函数 内嵌函数缩进与函数体一致。 内嵌函数只能在父函数全域内调用,无法在主程序或其他函数中调用。 反过来,一个函数可以调用主程序内的早于它定义的其他函数,但不能调用其他函数的内嵌函数。

def func(): def func2(): func2函数体 func函数体 func2() #在func函数内调用其内嵌函数

image.png

内部函数中的变量与外部函数中变量的关系,跟函数变量与全局变量的关系一样,有严格的作用域。 如果要在内部函数中修改外部函数变量,可以使用 nonlocal 关键字,用法与global类似。

闭包

函数式编程语言。 如果在一个内部函数中,对外部作用域的变量(但非全局变量)进行引用,就成为闭包。

def func1(x): def func2(y): z = x * y return z return func2

在上面的函数里,内嵌函数func2里面引用了外部函数func1的变量x,此时func2就形成了闭包。 调用时这个闭包时,可以同时给2个参数: func1(5)(8) 也可以先传1个参数: i = func1(5) 此时i的类型是function,是x=6的func2整个闭包函数 再传第二个参数 i(8)

image.png

闭包也是内部函数,其变量与内部函数变量规则相同。

课后作业

  1. 如果希望在函数中修改全局变量的值,应该使用什么关键字? global

  2. 在嵌套的函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字? nonlocal

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

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

inside()

主程序中调用了内嵌函数,会报错。

  1. 请问为什么代码 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()

B中,给var变量赋值了,此时var就变成inside的内部变量,print(var)在赋值语句之前,此时还没定义var变量,因此报错。

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

def funOut(): def funIn(): print('宾果!你成功访问到我啦!') return funIn()

funOut()

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

def funOut(): def funIn(): print('宾果!你成功访问到我啦!') return funIn

i = funOut() i()

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

def funX(): x = 5 def funY(): nonlocal x x += 1 return x return funY

a = funX() print(a()) print(a()) print(a())

6 7 8 a函数一直没有释放,x变量一直有效

编程作业

  1. 请用已学过的知识编写程序,统计下边这个长字符串中各个字符出现的次数并找到小甲鱼送给大家的一句话。

(由于我们还没有学习到文件读取方法,大家下载后拷贝过去即可)

请下载字符串文件: string1.zip (55.49 KB, 下载次数: 18670)

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

    a) 每位密码为单个小写字母 b) 每位密码的左右两边均有且只有三个大写字母

(由于我们还没有学习到文件读取方法,大家下载后拷贝过去即可)

请下载字符串文件: string2.zip (6.17 KB, 下载次数: 18404)

  1. 请写下这一节课你学习到的内容:格式不限,回忆并复述是加强记忆的好方式!