Python 极简教程(十八)函数(2)参数

我们在编写代码时,如果要调用别人写的函数,一般我们不需要关心这些函数是如何实现的,只需要关注如何使用这个函数。
那么怎么才能知道如何使用呢?也就是我们必须要知道这个函数需要哪些参数,参数都代表什么,哪些参数是必须写的,哪些是不用管的,最后关注其返回的结果如何,这样就能正确的使用该函数。

比如我们前面讲过的range函数:

range(10)
range(1,10)
range(1, 10, 2)

我们看在传参数的时候,数量不同会得到不同的结果。其实这就是 Python 在函数定义的时候提供的各种参数形式。这种形式非常灵活,且功能强大。

在 Python 中,有多种参数形式,分别是位置参数(必选参数),默认参数,可变参数和关键字参数(不同的教程叫法不太一样)。

位置参数

也叫做必选参数,也就是说在函数定义的时候声明的参数,在调用的时候就必须全部给予实参,并且必须按照参数定义的顺序赋值。

比如下例,我们都知道求圆的面积和周长必须要用到 半径 r 和圆周率 π ,现在实现一个函数,提供半径 r 和 圆周率 π,返回面积(πr2)和周长(2πr)

def circle(r, pi):
    area = pi * r ** 2
    perimeter = 2 * pi * r
    return area, perimeter

当我们调用 circle() 函数的时候,必须同时提供 r 和 π 两个参数,否则就会报错,当然多了也不行。

>>> circle(4)
TypeError: circle() missing 1 required positional argument: 'pi'

也就是说参数不能多也不能少,必须按照函数定义的参数个数依次赋值,才能正常调用该函数。

>>> circle(4, 3.14)
(50.24, 25.12)

如果顺序反了就得不到你想到的结果了!

>>> circle(3.14, 4)
(39.4384, 25.12)

如果你非要反着用(不按顺序),请使用 参数名=参数值 的形式。

>>> circle(pi=3.14, r=4)
(50.24, 25.12)

对于没有任何标识的参数,我们就称之为 位置参数

默认参数

刚才的 circle() 函数,我们在用的时候必须同时传入 r 和 π 两个参数。但是我们发现 π 也就是圆周率,一般都是固定的用两位小数 3.14。

那么我们就没必要每次调用这个函数都要输这个 π,毕竟圆周率不是谁想改就能改的。但是偶尔我们需要使用小数点后面 4 位或者 6 位以求得更精确的面积和周长。那么如何实现呢?

对于函数的参数,绝大部分情况使用固定的值,在少数情况需要改该参数的值,那么我们可以设置为默认参数

简而言之,就是给参数赋一个默认值。这样当我们没有特殊需要的时候,该参数直接用默认值,而当你有特殊需要的时候,又可以通过提供新的值来实现你的需求。而且当你没有特殊需要的时候,还可以少传一个参数,使用起来更方便。

我们来改造一下上面的 circle() 函数:

def circle(r, pi=3.14):     # 给参数赋一个默认值即可
    area = pi * r ** 2
    perimeter = 2 * pi * r
    return area, perimeter

那么我们调用改造后的函数:

# 没有特殊需要的时候
>>> circle(4)
(50.24, 25.12)

# 精度要求更高的时候
>>> circle(4, 3.14159265358)
(50.26548245728, 25.13274122864)

是不是很方便!

灵活使用默认参数,会大大增加函数调用者的体验 :】

可变参数

参数收集

在某些情况下,我们需要输入不确定数量的参数(其实一般初级编程用不上),那么根据上面我们说的参数必须按照顺序和个数传入,那么这种情况要怎么处理呢?

在 Python 中提供了参数收集的机制,在函数定义时,指定可变参数(参数收集),在函数调用的时候就可以提供任意数量的参数。

比如,我们要写一个函数,用于保存任意多的用户姓名:

def collect_names(*name):
  print('收集到的姓名:', name)

没错,就是在参数前面加个星号*,在函数内部使用这个参数即可。我们看看结果是什么?

>>> collect_names('nemo', 'nano', 'tato', 'jim', 'kid')   # 你想传多少都可以!
收集到的姓名: ('nemo', 'nano', 'tato', 'jim', 'kid')

从结果我们可以看出,通过参数收集到的任意参数,传入函数内部就变成了一个元组,元组嘛,当然可以存任意多的数据了。

当然收集到元组如何用,就看你的需求了!

除了上面的直接收集参数的形式,还有一种收集 参数名=参数值的传参形式,而通过这种方式收集到的参数在内部构成一个字典。

如下例,写一个函数,收集一个人的信息。

def person(**attr):
  print('用户的信息:', attr)

调用的结果:

>>> person(name='nemo', age=18, sex='男', height=180)
用户的信息: {'name': 'nemo', 'age': 18, 'sex': '男', 'height': 180}

收集到内部形成一个字典,然后你要操作就随你咯。比如:

def person(**attr):
  for k in attr.keys():
    print('用户的%s信息:%s' % (k, attr.get(k)))

>>> person(name='nemo', age=18, sex='男', height=180)
用户的name信息:nemo
用户的age信息:18
用户的sex信息:男
用户的height信息:180

也就是说你收集到了用户的信息后,根据你的需要从字典中提取数据并处理。

参数收集的逆向过程

其实上面讲的参数收集,在你日常编码过程中使用相对较少,反倒其逆向过程用得更多。
那什么是参数收集的逆向过程呢?

参数收集是在函数调用时,传入任意多的参数,在函数内部形式一个元组或字典。而逆向过程,是先有一个元组(或任意序列)或字典,在函数调用时传入该序列或字典。

那么这有何用处呢?可以简化函数调用,并增加函数的可读性。
首先我们看第一种参数收集的逆向过程(元组):
比如用上面的 circle 函数的例子,我们在一个数据文件中存储了多个需要求面积和周长的数据。然后提取出来后放在一个二维的元组中,我们需要调用该函数逐一运算。代码如下:

data = ((4, 3.14), (6, 3.1415), (11, 3.14))
for i in data:
  circle(i[0], i[1])

好在我们这里只有两个参数,如果参数更多一些,比如5个6个,那么你要怎么写 fun(i[0], i[1], i[2]....)编不下去了。。。急需一个更简单方便的方式,正好。。。

data = ((4, 3.14), (6, 3.1415), (11, 3.14))
for i in data:
  circle(*i)  # 在变量前加一个星号就搞定啦

我们来看一下,当 i 的值为(4, 3.14),那么这是一个元组,用星号能起什么作用呢?星号相当于拆解,把元组拆解成单个的元素,再分别按顺序赋值给 circle() 函数的各个参数。circle(*i) 等同于 circle(4, 3.14)。

同样字典形式的参数收集也可以逆向来用。这个就更有用啦。比如我们连接 mysql 数据的时候(这里我只举函数调用的例子,数据库的使用在 Python 基础学完后再深究),使用到的函数有很多的参数:

db = connect(host=127.0.0.1, port=3306, user='root', password='123456', db='happy')  # 需要的话还有参数需要赋值

那么对于这种情况,我们可以写一个字典作为数据库连接的配置:

db_config = {
  'host': '127.0.0.1',
  'port': 3306,
  'user': 'root',
  'password': '123456',
  'db': 'happy'
}

db = connect(**db_config)

这里两个星号的作用,把字典拆解为单个的 参数名=参数值 的形式分别赋值给函数的各个参数。

虽然代码看起来更多一些,但是可读性更强,维护起来也更容易些,可以把这个字典放在配置文件中维护,其他需要用到的地方直接引入这个字典即可。

关键字参数

最后还剩一个关键字参数,关键字参数在函数定义时是一种参数的限制条件。

定义为关键字参数的参数,在调用时,必须以 参数名=参数值的形式赋值,不能直接写参数值。

def person(name, age, *, sex, height):
  print('用户的姓名为:', name)
  print('用户的年龄为:', age)
  print('用户的性别为:', sex)
  print('用户的身高为:', height)

上面例子中函数参数中的星号看到了么?星号后面的参数 sexheight 就是关键字参数了。

注意星号不用赋值也不算一个参数,星号属于一个标识用来区分普通参数和关键字参数。

在函数调用时,sex 和 height 两个关键字参数,必须以 sex='男' 和 height=180 这种形式赋值,否则就会报错。

>>> person('nemo', 18, '男', 180)  # 提示只需要两个位置参数,但是给了4个
TypeError: person() takes 2 positional arguments but 4 were given

>>> person('nemo', 18)  # 提示缺少两个关键字参数
TypeError: person() missing 2 required keyword-only arguments: 'sex' and 'height'

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

推荐阅读更多精彩内容