把一段代码打包成"按钮"——定义、调用、参数三步走
28.1 为什么需要函数
打印三个学生的成绩单,代码要复制三遍:
复制粘贴 vs 函数
# 复制粘贴版(灾难的开始) print("=" * 20) print("姓名: 小明 成绩: 92") print("=" * 20) print("姓名: 小红 成绩: 88") print("=" * 20) print("姓名: 小刚 成绩: 95") print("=" * 20) # 函数版: 打包成一个"按钮",之后一键调用 from_ = None # (演示用占位) def print_report(name, score): """打印一名学生的成绩单""" print("=" * 20) print(f"姓名: {name} 成绩: {score}") print("=" * 20) print_report("小明", 92) # 一行顶三行 print_report("小红", 88) print_report("小刚", 95)第一, 函数等同于你自己制作的电器按钮。第二, 在首次进行装修期间, 将“开灯”那种复杂的接线, 也就是三行代码, 封印进墙体之内, 此为函数定义。第三, 历经初次装修以后, 往后的每一天, 只要按压开关, 这就是调用。第四, 那次的接线仅仅施行一次, 而按钮却要按压一万次, 这便是一次定义、无限复用。随后再次装修时, 也仍旧只是改动墙体里的接线处, 即函数体, 如此一来, 所有按钮会自动实现升级。
28.2 定义与调用:两个阶段
定义与调用
# ① 定义(只打包,不执行!好比写菜谱,不等于炒菜) def greet(): print("你好呀") print("欢迎学习Python") # 此刻屏幕上什么都没有 —— 定义不产生输出 # ② 调用(按名字加括号,才是真的执行) greet() # 打印两行 greet() # 想按几次按几次阶段
代码样子
发生什么
定义 def
def f(): + 缩进块
只是把代码打包登记,一行都不执行
调用 f()
函数名 + 小括号
跳进函数体执行,跑完回到调用处继续
漏括号 f
只有名字没有括号
拿到的是函数本身(对象,不执行)——函数进阶章的主角
新入门者所遭遇的首个极大陷阱是, 仅仅进行定义却不加以调用, 进而误以为函数出现故障, 程序悄然无声毫无输出, 于是花费大量时间去进行检查, 实际上却是忘记按下按钮(也就是没有编写 greet())。第二个极大陷阱是, 调用操作写在了定义之前(由于是从上往下执行, 在尚未定义之时就按下按钮便会出现这种情况)。
28.3 参数:让按钮带旋钮
形参与实参
# name 是"形参"(形式参数): 定义时占位,像个空插座 def greet(name): print(f"{name}, 你好呀!") # "小明" 是"实参"(实际参数): 调用时真插进来的值 greet("小明") # 小明, 你好呀! greet("小红") # 小红, 你好呀! # 参数可以有多个,按顺序逗号隔开 def print_info(name, age, city): print(f"{name}, {age}岁, 来自{city}") print_info("小刚", 25, "成都") # 位置对应: 小刚→name, 25→age, 成都→city一句话而言形参和实参, 形参乃是函数定义之中的“占位符名字”, 就如同菜谱里面写着的“鸡蛋2个”, 而, 实参则是调用函数之际传进去的“真值”, 就好像你从冰箱里面实实在在拿出来的那双蛋, 在函数的体内运用形参名来开展相关操作, 而开展操作的时候所使用的值却是你传递进来的实参, 参数就是函数的数据入口处。
函数的命名方式同样是采用蛇形小写, 最好是以动词作为开头, 函数名要能够当作注释来读, 也就是说名字就是文档, 当看到调用该函数的地方时, 不用跳进函数的定义当中, 就能够知道这一行代码在做什么。
思考: 将十六课的九九乘法表, 包装成为函数, 接着升级为 n, 可以打印出 n 乘以 n 的乘法表。随后回答: 函数化之后, 复用以及参数化分别带来了什么样的好处呢? ——复用就是不需要复制代码;参数化指的是一个函数能够适配所有级别的规模, 这恰恰是练习里把十改成变量 n 的后续情况。
️ 本课练习
编写一个函数, 这个函数名为(name), 它能打印出三行生日歌, 生日歌里包含名字, 之后对这个函数进行两次调用, 一次是你调用, 一次是你的朋友进行调用。
定义一个名为def的东西, 它带有一个名为name的参数,有三行print语句。体会到, 没有添加参数的版本, 仅仅能够为一个人进行演唱, 而添加了参数的版本, 对于全人类都是通用的——参数所具备的“泛化”能力, 是函数设计的核心要点。
练习2: 去写个函数(定义为(n)), 此函数用于打印呈现n行的直角三角形(形状为*号组成), 然后分别对n等于3以及n等于5的情况进行调用。
将16课之中的三角形代码, 搬运进函数里头, 把那写死的range(3)替换成为range(n), 这便是“旧代码函数化”的标准动作, 即粘贴进来, 魔法数字换做参数, 完了。
练习 3: 刻意进行实验: 首先进行调用操作, 之后再进行定义, 要将括号写在定义函数的括号之上, 运行程序后抄下报错信息;接着把定义移回原来的位置进行验证从而得到成功的结果。
那个名为“”的并非如此。它属于那种“从上往下逐行执行”的解释器, 按钮必须先安装好之后才能够去按。把这个实验做上一遍, “定义在使用之前”这样的概念就会深深地刻进脑子当中。
练习 4: 求解画图题时, 需要在纸张之上画出, 当 greet("小明") 被调用之际的执行流情况, 具体是从程序之中的调用行位置, 跳跃进入函数体内部执行, 在函数体内部要执行两行 print 语句, 之后再返回到调用处的下一行位置。同时, 还需要在整个过程里的每个时刻, 准确标注出 name 的取值情况。
这被称作“追踪执行流”, 它属于理解函数(特别是后续递归方面)的关键技能。若画不出来, 那就返回去重新阅读 18.2 的阶段表——执行流具备的画面感相较于背诵概念而言要重要一百倍。