• 首页

  • 分类&标签

  • 归档

  • 手册

  • 项目池

  • 友链

  • 关于
伯 乐 讲 堂
伯 乐 讲 堂

查看「系列思维导图」

侠客 · Mr.潘

获取中...

08
16
小Python 起步

第 7 节 小Python 之函数

发表于 2020-08-16 • Python • 被 392 人看爆

上一节,博主从数组组织的维度探索了 小Python 的用法。随着你所掌握的技术越来越广阔、深入,你的代码量也在突飞猛进,但代码重复和可读性性差的问题,也在慢慢凸显。这时候,该有个角色帮我们适当的组织下代码了 —— 函数。

1. 何谓函数

想象如下场景,你来到了KFC之后,准备大吃一顿,于是乎你冲服务员叫道:“美女,给我来***吮指原味鸡5块+香辣鸡翅6块+醇香土豆泥1份+香甜粟米棒1份+1.25L装百事可乐1瓶***”,服务员,擦了把汗,说道:“帅哥,下次来直接说***全家桶***一份就可以了"。你眨巴眨巴眼睛,付完款悻悻的找了个位置等待用餐。

如果由程序员来解读这个场景,那么啰里吧嗦的内容罗列就是程序中的一条一条的语句,而***全家桶***则可以称之为***函数***。全家桶(函数)就是对***吮指原味鸡5块+香辣鸡翅6块+醇香土豆泥1份+香甜粟米棒1份+1.25L装百事可乐1瓶***这多个商品(语句)的封装,言简意赅。

再从专业的编程角度强化一下函数的含义及存在价值:

  1. 定义:指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可。
  2. 价值一:简化代码,将多条语句封装为一个函数,通过这个函数就可以找到对应的多条语句
  3. 价值二:提高代码的复用性,多次使用再也不需要重复多条语句,只需要使用函数就可以
  4. 价值三:代码可扩展,函数实现由变,只需要修改函数中的语句即可,添删改随你的便。

函数的定义:

81.png

函数的调用:

82.png

2. 分门别类话函数

2.1 函数的来源

  • 内置函数:为了提高程序员的开发效率,Python标准库里(语言自身携带的)提供了很多函数,俗名为拿来主义式函数,只需要根据其用法描述直接使用即可,点击访问内置函数详解。
  • 用户自定义函数:为了满足程序员自身的开发需求,由使用者(第三方用户)编写的函数,俗名为自产自销函数,需要根据函数的定义规范生产创造函数。

2.3 函数返回值

首先,Python中所有函数都有返回值,而这个返回值通过return语句实现,如果你没看见这个return语句,函数运行结束会隐含返回一个 None 作为返回值,类型是 NoneType,与 return 、return None 等效,都是返回 None。

其次,一个函数可以存在多条 return 语句,但只有一条可以被执行,如果函数执行了 return 语句,则结束函数,即:return 之后的语句都不会被执行。

83.png

2.4 参数分类

当函数中处理的数据对象因调用者不同,处理的数据对象也有所不同的话,那么调用者就需要把要处理的数据传递给函数,而用于接收这些数据的占位作用的就是参数,如同我们点菜时,不同用户口味不同,就需要传递给商家不同的口味一样。

根据函数是否需要接收参数,以及接收参数的方式,可分类如下:

2.4.1 无参函数

# =*=*=*=*=*=*=*=*=*=*=*=*无参函数*=*=*=*=*=*=*=*=*=*=*=*=
def sum():
	return 10 + 20

s = sum()
print(s)
# =*=*=*=*=*=*=*=*=*=*=*=*无参函数*=*=*=*=*=*=*=*=*=*=*=*=

2.4.2 位置或关键字参数

# =*=*=*=*=*=*=*=*=*=*=*=*positional-or-keyword:位置或关键字,必传参数*=*=*=*=*=*=*=*=*=*=*=*=
def sum(first_number, second_number):
    print("first_number = " + str(first_number))
    print("second_number = " + str(second_number))

    return first_number + second_number

s1 = sum(100, 10)          						# 位置对应
print(s1)

s2 = sum(second_number=100, first_number=10)    # 关键字对应
print(s2)
# =*=*=*=*=*=*=*=*=*=*=*=*positional-or-keyword:位置或关键字,必传参数*=*=*=*=*=*=*=*=*=*=*=*=

2.4.3 关键字默认值参数

# =*=*=*=*=*=*=*=*=*=*=*=*keyword-default-value:关键字参数默认值,可选参数*=*=*=*=*=*=*=*=*=*=*=*=
def sum(first_number, second_number=0):
    print("first_number = " + str(first_number))
    print("second_number = " + str(second_number))

    return first_number + second_number

s1 = sum(100)
print(s1)

s2 = sum(100, 10)
print(s2)
# =*=*=*=*=*=*=*=*=*=*=*=*keyword-default-value:关键字参数默认值,可选参数*=*=*=*=*=*=*=*=*=*=*=*=

2.4.4 仅限位置参数

# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*positional-only:仅限位置*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
# 仅限位置:指定一个只能按位置传入的参数。Python 中没有定义仅限位置参数的函数语法,但是一些内置函数有仅限位置形参(比如 abs())。
abs_value = abs()
print(abs_value)

abs_value = abs(-10)
print(abs_value)

# abs_value = abs(x=-10)    # 不支持关键字参数传递,异常报错
# print(abs_value)

# PS:=*=*=*=*用户自定义函数不支持 仅限位置参数,支持位置和关键字*=*=*=*=
def sum(x):
    return x

s1 = sum(100)
print(s1)

s2 = sum(x=100)
print(s2)
# =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*positional-only:仅限位置*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*

2.4.5 可变位置参数

# =*=*=*=*=*=*=*=*=*=*=*=*var-positional:可变数量位置参数*=*=*=*=*=*=*=*=*=*=*=*=
def sum(first_number, second_number=0, *args):
    print("first_number = " + str(first_number))
    print("second_number = " + str(second_number))
    print("args = " + str(args))

    s = first_number + second_number  
    for arg in args:
        s += arg
  
    return s

s1 = sum(100, 10)
print(s1)

s2 = sum(100, 10, 1000)
print(s2)

s3 = sum(100, 10, 1000, 2000, 3000, 4000)		# 涉及到 打包
print(s3)

numbers = [1000, 2000, 3000, 4000]      		# 序列类型对象都可作为实参传递
s4 = sum(100, 10, *numbers)						# 涉及到 解包 和 打包
print(s4)
# =*=*=*=*=*=*=*=*=*=*=*=*var-positional:可变数量位置参数*=*=*=*=*=*=*=*=*=*=*=*=

图解:

84.png

2.4.6 可变关键字参数

# =*=*=*=*=*=*=*=*=*=*=*=*var-keyword:可变数量关键字参数*=*=*=*=*=*=*=*=*=*=*=*=
def sum(first_number, second_number=0, **kwargs):
    # 关键字是隐性的要求,但数量不确定
    # 隐性的关键字如下:third_number, fourth_number, fifth_number
    # kwargs = {"third_number":1000, "fourth_number":2000, "fifth_number":4000}
    # kwargs.items()  ====> [("third_number",1000), ("fourth_number",2000), ("fifth_number",4000)]
    s = first_number + second_number
    for key,value in kwargs.items():
        print(key + " = " + str(value))
        s += value
  
    return s

s1 = sum(100)
print(s1)

s2 = sum(100, 10, third_number=1000)                    # 涉及到 打包
print(s2)

s3 = sum(100, 10, third_number=1000, fifth_number=4000) # 涉及到 打包
print(s3)

numbers = {"third_number":1000, "fourth_number":2000, "fifth_number":4000}
s4 = sum(100, 10, **numbers)                            # 涉及到 解包 和 打包
print(s4)
# =*=*=*=*=*=*=*=*=*=*=*=*var-keyword:可变数量关键字参数*=*=*=*=*=*=*=*=*=*=*=*=

PS:此处涉及的解包和打包同可变数量位置参数相同,不在赘图。

2.4.7 仅限关键字参数

# =*=*=*=*=*=*=*=*=*=*keyword-only:仅限关键字,关键字前面一定是可变数量的位置参数*=*=*=*=*=*=*=*=*=*=
# 四则运算:+,-,*,/
def calc(first_number, second_number=0, *, operation=None):
    s = 0
    if operation == "+":
        s = first_number + second_number
    elif operation == "-":
        s = first_number - second_number
    elif operation == "*":
        s = first_number * second_number
    elif operation == "/":
        s = first_number / second_number
    else:
        return "Invalid operation"        # 终止整个函数,函数提前结束了
  
    return s

result1 = calc(100, 10)
print(result1)

result2 = calc(100, 10, operation="+")
print(result2)
# =*=*=*=*=*=*=*=*=*=*keyword-only:仅限关键字,关键字前面一定是可变数量的位置参数*=*=*=*=*=*=*=*=*=*=

点击访问形参分类详解

3. Lambda函数

Python 中定义函数有两种方法,一种是用常规方式def定义,函数要指定名字,第二种是用lambda定义,不需要指定名字,称为Lambda函数。

Lambda 函数又称匿名函数,匿名函数就是没有名字的函数,函数没有名字也行?当然可以啦。有些函数如果只是临时一用,而且它的业务逻辑也很简单时,就没必要非给它取个名字不可。

好比电影里面的群众演员,往往他们的戏份很少,最多是衬托主演,跑跑龙套,他们需要名字吗?不需要,因为他们仅仅只是临时出镜,下次可能就用不着了,所以犯不着费心思给他们每个人编个号取个名字,毕竟取个优雅的名字是很费劲的事情。

图解lambda函数:

85.png

4. 我的地盘,我做主

Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种,分别是:

  • L (Local) 局部作用域:属于内层函数的范围,不属于外层
  • E (Enclosing) 闭包函数外的函数中:函数内部属于本函数的作用范围,因为函数可以嵌套函数,嵌套的内层函数有自身的内层范围
  • G (Global) 全局作用域:文件级别的,或者说是模块级别的,每个py文件中处于顶层的变量都是全局作用域范围内的变量
  • B (Built-in) 内建作用域:预先定义好的,在__builtins__模块中。这些名称主要是一些关键字,例如open、range、quit等

以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

PS:闭包函数相对比较复杂,暂时不涉及,将会在Python进阶中深入讲解。

图解***L –> G –>B***:

86.png

4.1 搜索规则

当在某个范围引用某个变量的时候,将从它所在的层次开始搜索变量是否存在,不存在则向外层继续搜索。搜索到了,则立即停止。

内层范围可以引用外层范围的变量,外层范围不包括内层范围的变量。

4.2 内建作用域

两种方式可以搜索内置作用域:一是直接导入builtins模块,二是让 小Python 自动搜索。导入builtins模块会让内置作用域内的变量直接置于当前文件的全局范围,自动搜索内置作用域则是最后的阶段进行搜索,如图解中的variable_1 = int(1.2)中的int。

一般来说无需手动导入builtins模块,不过可以看看这个模块中包含了哪些内置变量。

# 交互式解释环境
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', ...............
'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

4.3 变量掩盖和修改规则

如果在函数内部引用了一个和全局变量同名的变量,且不是重新定义、重新赋值(其实 小Python 中没有变量声明的概念,只有赋值的概念),那么函数内部引用的是全局变量,如图解中的variable_2。

如果函数内部重新赋值了一个和全局变量名称相同的变量,则这个变量是本地变量,它会掩盖全局变量。注意是掩盖而非覆盖,掩盖的意思是出了函数的范围(函数退出),全局变量就会恢复。如图解中的,在函数内部看到的是本地变量variable_1=2,在函数外部看到的是全局变量variable_1 = int(1.2)。

如果想要在def的内部修改全局变量,就需要使用global关键字声明变量,global可以声明一个或多个变量为全局变量,多个变量使用逗号隔开,也可以声明事先不存在的变量为全局变量。如图解中的,global variable_3。

4.4 关于全局变量

  • 每个py文件(模块)都有一个自己的全局范围
  • 文件内部顶层的,不在def区块内部的变量,都是全局变量
  • def内部声明(赋值)的变量默认是本地变量,要想让其变成全局变量,需要使用global关键字声明
  • def内部如果没有声明(赋值)某变量,则引用的这个变量是全局变量

5. 场景驱动

场景描述:某班级有 N 名学员,刚经历了一次考试的洗礼,现使用 小Python 编写一个成绩清单,成绩清单中包含学号、姓名、成绩三项数据,功能如下:

  • 提供录入功能
  • 成绩单按照分数从高到低显示
  • 可以根据学号查找出某位学员的的详细信息
  • 可将补考学员的信息,插入到已排序后的成绩单中

PS:针对该场景,在上节实现中,是否发现有很多重复的代码块,相似的代码块,那能否像“全家桶”套餐那样封装重用呢?函数,封装、重用的利器,试一试吧。

预知新作如何,请看下节分解。
标题:第 7 节 小Python 之函数
作者:侠客 · Mr.潘

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加在下微信(pansfy)讨论:

第 8 节 小Python 的对象上
第 6 节 小Python 之数据结构
侠客 · Mr.潘

侠客 · Mr.潘

未来的你,会感谢今天仍正在奋斗的你

Github QQ Email RSS
看爆 Top10
  • 助力项目池 3,538次看爆
  • 第 4 节 yum 版的 LAMP 环境部署 2,535次看爆
  • 你的生产力工具集成就高效人士 1,880次看爆
  • 第 5 节 yum 版的 LNMP 环境部署 1,676次看爆
  • 第 3 节 企业级系统环境之上云篇 1,387次看爆
  • 服务端的架构的演进之路 1,285次看爆
  • 第 1 节 Docker 实践 1,190次看爆
  • 镜像仓库一文打尽 982次看爆
  • Docker本地私有镜像仓库Harbor搭建及配置 980次看爆
  • 第 6 节 源码版的LAMP环境部署 945次看爆

Copyright © 2023 侠客 · Mr.潘 · 苏ICP备19067937号

Proudly published with Halo · Theme by fyang · 站点地图