Python3中函数详解

对于编程语言而言,函数是不可或缺的一块内容。因为在编写代码的过程中,不可避免会出现很多重复、冗余的代码,从而导致代码变得冗长且不易维护。这时候,函数的诞生,就极好地解决了这个问题。

我们将代码中重复的、功能相对单一的代码抽离出来形成一个个代码块,然后使用函数、嵌套将代码块进行包装。优化了代码的简洁程度,同时保证模块功能的一致性,也增加了可扩展性。将单一功能提取出来形成函数后,也大大加强了我们对于功能测试的便利。

所以函数一般具备了以下特性:

  • 代码重用性
  • 模块功能一致性
  • 可扩展性
  • 测试便利性

那么,Python中的函数,是怎么样的呢?

函数的结构

def 函数名(参数):
       函数体内的代码块

创建一个函数的时候,我们使用def开始定义一个函数,紧接着函数名,并在后面的括号内写入函数的参数。在参数后的冒号后换行写入该函数体中的代码。

这边就涉及到了如下几个概念:

  • 函数名
  • 参数
  • 作用域
  • 返回值

函数名

函数名是用来识别、调用这个函数的。其命名规则和变量的命名规则一致。都需要遵守以下的规则:

  • 函数名必须以下划线或字母开头,可以包含任意字母、数字或下划线的组合。不能使用任何标点符号
  • 函数名是区分大小写
  • 函数名不能是保留字
Tip:保留字是Python底层框架中已经有用到或者声明的特定字符串,已经在Python中具有了特定的功能、语法意义。

Python中的保留字请参考下表:

保留字 说明
and 用于表达式运算,逻辑与操作
as 用于类型转换
assert 断言,用于判断变量或条件表达式的值是否为真
break 中断循环语句的执行
class 用于定义类
continue 继续执行下一次循环
def 用于定义函数或方法
del 删除变量或者序列的值
elif 条件语句 与if else 结合使用
else 条件语句 条件语句,与if,elif结合使用。也可以用于异常和循环使用
exceptexcept 包括捕获异常后的操作代码,与try,finally结合使用
exec 用于执行python语句
for 循环语句
finally 用于异常语句,出现异常后,始终要执行finally包含的代码块与try,except结合使用
from 用于导入模块,与import结合使用
global 定义全局变量
if 条件语句,与else,elif结合使用
import 用于导入模块,与from 结合使用
in 判断变量是否存在序列中
is 判断变量是否为某个类的实例
lambda 定义匿名函数
not 用于表达式运算,逻辑非操作
or 用于表达式运算,逻辑或操作
pass 空的类,函数,方法的占位符
print 打印语句
raise 异常抛出操作
return 用于从函数返回计算结果
try 包含可能会出现异常的语句,与except,finally结合使用
while 循环语句
with 简化Python的语句
yield 用于从函数依次返回值

参数

函数中的参数,是用来向函数中传递函数内部代码块需要用到的值的媒介。

参数的形式分为了形参和实参。

  • 形参是紧跟在函数名后括号内的参数名称,用来提醒用户这边需要传入的值的信息。顾名思义,形参就是形式参数,只是一个用来提醒的代号
  • 实参是我们在调用函数的时候函数名后面括号中的参数,也就是我们实际传入的参数
  • 形参与实参需要一一对应,否则调用函数的时候会报错

函数中的参数,主要分为以下几种:

  • 位置参数(必选参数)
  • 关键字参数
  • 默认参数
  • 可变参数
位置参数(必选参数)
def eat_lunch(person, food):
     print(f'{person} eat {food} for lunch.')

eat_lunch('Drink', 'bacon')    # Drink eats bacon for lunch.

位置参数必须以对应的关系一个一个传递进入函数,函数调用时传递的实参必须和函数定义时的形参一一对应,不能多也不能少,顺序也得一致。

如上述eat_lunch函数中,我们需要传入两个参数,一个是吃午餐的人,一个是午餐的食物,最终打印出'Drink eats bacon for lunch.'。如果我们把person和food两个参数对调传入,就会变成'bacon eats Drink for lunch.'。变成培根中午吃Drink,很可怕吧~当然在上述函数中我们只是打印,不会出大问题。但是在实际开发中,我们常常传入不同类型的参数,如果把Dictionary传递给List,那么系统将会崩溃。

关键字参数
def eat_lunch(person, food):
     print(f'{person} eat {food} for lunch.')

eat_lunch(food = 'bacon', person = 'Drink')   # Drink eats bacon for lunch.

关键字参数与位置参数不同,关键字参数的位置可以随意调换,但是书写的时候,必须要在传入实参的之前说明对应的形参。

如上述代码所示,我在给函数传入实参的时候,事先说明了对应的形参,便可以无所谓参数传入的顺序。

默认参数
def eat_lunch(person, food='bacon'):
     print(f'{person} eat {food} for lunch.')

eat_lunch('Drink')    # Drink eats bacon for lunch.

默认参数,顾名思义就是给函数需要传入的参数设置默认值。当调用该函数的时候,如果没有传入已预设默认值的参数时,调用函数的时候就会自动去读取预设的默认值。

如上述代码所示,当我们创建eat_lunch函数的时候,对food参数设置了默认值为'bacon'。我们调用eat_lunch函数,只传了个'Drink',系统默认我们传的是person参数。后面执行函数的时候,因为实参里按照位置参数的方式找不到food参数,所以直接读取了food参数的默认值。

注意:
  • 创建函数的时候,必选参数必须放在默认参数的前面,否则系统会报错
  • 一般我们对参数值变化小的参数去设置默认参数
  • 设置默认参数的时候,尽量使用不可变数据类型。如果使用可变数据类型(如List),该可变数据类型只会在第一次函数被调用的时候去初始化,后续调用则沿用初始化后的参数
可变参数

我们在开发中,可能存在一种情况,就是我们不太确定传入的参数数量的时候,那么我们就可以选择创建函数的时候,将形参设置成可变参数。可变参数主要分为两种:

*args:单星号参数

单星号参数的实质是传入一个元组。打印输出的时候也是返回一个元组

def print_info(*args):
       print('args')

print_info('Honda', 'Toyota', 'Nissan', 'Suzuki')    # ('Honda', 'Toyota', 'Nissan', 'Suzuki')
**kwargs:双星号参数
def print_info(**kwargs):
    for key, values in kwargs.items():
        print(f'{key} = {values}')

print_info('Honda' : '本田', 'Toyota' : '丰田', 'Nissan' : '尼桑', 'Suzuki' : '铃木')  
# Honda = 本田
# Toyota = 丰田
# Nissan = 尼桑
# Suzuki = 铃木

双星号参数的实质是传入一个字典,在传值的时候,一定要按照键值对的形式传值,且规则需同字典一致,否则系统会报错。

单星号参数和双星号参数其实也可以混搭使用

def print_info(*args, **kwargs):
        pass

实质上是声明了一个元组和一个字典,将不以键值对形式传入的参数塞入元组中,然后将以键值对形式传入的参数塞入字典中。

注意:Python中函数的参数是引用传递(注意不是值传递)。对于不可变类型(String、Tuple、Number),因变量不能修改,所以运算不会影响到变量本身;而对于可变类型(List、Dictionary)来说,函数内对于传入的参数的值进行了修改,函数外的该参数的值也会被修改。

作用域(Scope)和命名空间(NameSpace)

在Python中,变量的访问取决于变量赋值的地方。变量的作用域决定了变量的访问权限。Python中变量的作用域一共分为如下几种:

  • Local:局部作用域
  • Enclosing:闭包函数外的函数中
  • Global:全局作用域
  • Built-in:内建作用域

作用域的查找顺序如下所示:Local -> Enclosing -> Global -> Built-in

函数会先去找自身作用域内的局部变量是否含有需要的参数,如果没有的话,就去该查找该函数外层的局部变量(如,闭包),还是找不到的话就去找该文件下的全局变量,实在还是找不到,便去查找项目中的内建变量。

创建函数(def/lambda)的时候会创建新的作用域,每个生成器表达式都有引入新的作用域,意思是每次创建一个新函数、新生成器的时候,系统会相对地形成一个作用域,用于该函数、该生成器内部放置代码块。Class的定义是没有作用域的,只会创建一个命名空间。

命名空间(NameSpace)

命名空间是从名字到对象的一个映射。大部分的命名空间都是以 Python中的字典来实现的。有一些常见的命名空间:如类、如全局变量等。

命名空间都是有创建时间和生存期的。对于Python built-in names组成的命名空间,它在Python解释器启动的时候被创建,在解释器退出的时候才被删除;对于一个Python模块的global namespace,它在这个module被import的时候创建,在解释器退出的时候退出;对于一个函数的local namespace,它在函数每次被调用的时候创建,函数返回的时候被删除。

两个不同的命名空间中的两个名字相同的变量之间是不存在任何联系的,因为他们分属于不同的空间。

命名空间的种类有:

  • built-in名字集合,包括像abs()这样的函数,以及内置的异常名字等。通常,使用内置这个词表示这个命名空间-内置命名空间
  • 模块全局名字集合,直接定义在模块中的名字,如类,函数,导入的其他模块等。通常,使用全局命名空间表示。
  • 函数调用过程中的名字集合,函数中的参数,函数体定义的名字等,在函数调用时被“激活”,构成了一个命名空间。通常,使用局部命名空间表示。
  • 一个对象的属性集合,也构成了一个命名空间。但通常使用objname.attrname的间接方式访问属性,而不是直接访问,故不将其列入命名空间讨论。
  • 类定义的命名空间,通常解释器进入类定义时,即执行到class ClassName:语句,会新建一个命名空间。
作用域(Scope)

作用域是Python中的一块文本区域。在该文本区域中,可以对命名空间直接访问,而不是通过属性来访问。直接访问的意思是,我们可以通过一个变量名在所有的命名空间内去查找该变量。属性访问(间接访问)的意思是,我们必须要通过该变量的.语法去访问相应的内容。

那么作用域和命名空间之间的关系是怎么样的呢?

  • 作用域是由命名空间按照特定的层级结构组合起来的
  • 作用域一定是个命名空间,但命名空间不一定是作用域

返回值

Python中的函数使用return语句返回“返回值”,我们可以将获得“返回值”赋予其他变量用于其他用处。所有函数都返回值,如果没有return语句,会隐式地调用return None作为返回值。

一个函数中可以存在多条return语句,但只有一条可以被执行,如果没有任何一条return语句被执行,同样会隐式调用return None作为返回值。

如果函数执行了return语句,函数会立刻结束调用且返回返回值,其余的语句将不会再被执行了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容

  • 〇、前言 本文共108张图,流量党请慎重! 历时1个半月,我把自己学习Python基础知识的框架详细梳理了一遍。 ...
    Raxxie阅读 18,954评论 17 410
  • 第5章 函数和函数式编程 5.1 引言函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数...
    VIVAFT阅读 957评论 0 5
  • 函数和对象 1、函数 1.1 函数概述 函数对于任何一门语言来说都是核心的概念。通过函数可以封装任意多条语句,而且...
    道无虚阅读 4,563评论 0 5
  • 01 你和她的女儿在草坪上玩耍,她坐在一旁,拿着相机,时不时的按下快门。她的嘴角上扬着,夹着一缕被风吹乱的头...
    Hosea1阅读 332评论 0 2
  • “孤影碎,沉香醉,江里红妆又几回。残烛微冷珠帘脆,归雁不归,了却心事,浊酒空几杯? 红叶飞,花易泪,寒花未落何伤悲...
    千殇白狐阅读 228评论 0 0