函数的参数详解

# 函数的参数

定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就算完成了。
对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了
函数内政部的复杂逻辑被封装起来,调用者无需了解。python的函数定义非常简单,单灵活度却非常大。除了正常定义的必选参数外,
还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数
还可以简化调用者的代码。一、位置参数
我们先写一个计算x的平方的函数
def power(x):return x * x
对于power(x)函数,参数想就是一个位置参数。当我们调用power函数时,必须传入有且仅有的一个参数x
>>> power(5)
25
>>> power(15)
225
>>> power(125)
15625那么,如果我们现在要计算x的立方怎么办?可以再定义一个power3函数,但是如果要计算x的四次方,五次方怎么办?
假设x的n次方,可以把x和n都设为参数def powerful(x, n):result = 1if n == 0:return resultelif n > 0:while n >= 1:result = result * xn-=1return resultelif n < 0:while n >= 1:result = result * xresult = 1 / resultreturn result对于这个修改后的powerful(x, n)函数,可以计算任意N次方
修改后的powerful(x, n)函数有两个参数,x和n,这两个参数都是位置参数,调用函数时,传入的两个值
按照位置顺序一次赋给参数x和n二、默认参数
如果我们经常计算x的平方,那么我们完全可以把第二个参数的默认值设定为2def powerful(x, n = 2):result = 1if n == 0:return resultelif n > 0:while n >= 1:result = result * xn-=1return resultelif n < 0:while n >= 1:result = result * xresult = 1 / resultreturn result>>> powerful(5)
25
>>> powerful(5,3)
125这样如果我们给n赋值,n就会成为被赋的值,如果不给n赋值,默认就是n=2,默认计算平方。从以上的例子可以看出,默认参数可以简化函数的调用。设置默认参数时,有几点问题需要注意:一是必选参数在前,默认参数在后,否则python的解释器会报错。
二是如何设置默认参数。当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。
变化小的参数就可以作为默认参数。使用默认参数最大的好处就是能够降低使用函数的难度。举个例子,我们写一个小学一年级学生注册的函数,需要传入name和gender两个参数
def enroll(name, gender):print("name:", name)print("gender:", gender)这样,调用enroll()函数只需要传入两个参数
>>> enroll("Sarah", "F")
name: Sarah
gender: F如果要继续传入年龄,城市信息怎么办? 我们如果规定一大堆位置参数,会使得调用函数的难度大大增加
因为位置参数需要严格按照位置传入,python默认从左到右给参数赋值,而位置参数一旦增多,
必然会要求函数调用者必须严格按照一定的顺序来,这样会导致出错的概率大大增加。
在本例中,由于年龄、城市信息绝大多数学生是一致的,因此可以设定为默认值
这样,大多数学生注册时不需要提供年龄和城市,只需提供必须的两个参数
只有与默认参数不符的学生才需要提供额外的信息def enroll(name, gender, age = 6, city = "BeiJing"):print("name:",name)print("gender:",gender)print("age:",age)print("city:",city)>>> enroll("xiaowang", "M")
name: xiaowang
gender: M
age: 6
city: BeiJing>>> enroll("xiaoshitou", "F", 8, "shanghai")
name: xiaoshitou
gender: F
age: 8
city: shanghai>>> enroll("xiaojuzi","F", 12)
name: xiaojuzi
gender: F
age: 12
city: BeiJing>>> enroll("ligoudan","M", city="LA")
name: ligoudan
gender: M
age: 6
city: LA可见,默认参数降低了函数调用的难度,而一旦需要更复杂的调用时,又可以传递更多的参数来实现
无论是简单调用还是复杂调用,函数只需要定义一个。有多个默认参数时,调用的时候,既可以按顺序提供默认参数
比如enroll("Bob","M",7)
意思是, 除了name和gender这两个参数外,最后一个参数应用在age上,
city参数由于没有提供,仍然使用默认值。也可以不按顺序提供部分默认参数。当不按顺序提供部分默认参数时,需要把参数名写上,相当于指名道姓的给某某赋值
比如调用enroll("Adam", "M", city= "tianjin")
意思是, city参数用穿进去的值,其他默认参数继续使用默认值。注意:默认参数很有用,但使用不当也会掉坑里。默认参数有个最大的坑,演示如下:
先定义一个函数,传入一个list,添加一个END再返回。def add_end(L=[]):L.append("END")return L当你正常调用时,结果似乎不错
>>> add_end([1,2,3])
[1, 2, 3, 'END']
>>> add_end(["anything you want","shuzu","data",1985])
['anything you want', 'shuzu', 'data', 1985, 'END']当时使用默认参数时,一开始的结果也是对的:
>>> add_end()
['END']但是,当你再次调用时,结果就不对了
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']很多人开始疑惑,默认参数是一个空列表[], 但是函数似乎每次都记住了上次添加了“END”后的list
原因解释如下:
python函数在定义的时候,默认参数L的值就被计算出来了,即[]
因为默认参数 L 也是一个变量,他指向对象[], 每次调用该函数,如果改变了 L 的内容,
则下次调用时, 默认参数的内容就变了,不再是函数定义时的[]了。也就是说,如果你的默认参数是一个变量,而这个变量在函数执行的过程中会发生改变,那么
当你下一次重新调用这个函数意图使用函数定义时的默认参数时,就不可能了,因为默认参数已经变了所以说,默认参数应该是一个固定的值,是大多数情况下不需要修改的值,必须指向不变对象!要修改上面的例子,可以用None这个不变对象来实现:def add_end(L = None):if L == None:L = []L.append("END")return Lelse:L.append("END")return L这样,无论调用多少次,都不会有问题,这样传入的参数会先进行判断,如果为空,则创建一个空列表,添加一个END返回
>>> add_end([1,2,3])
[1, 2, 3, 'END']
>>> add_end()
['END']
>>> add_end()
['END']
>>> add_end()
['END']为什么要设计str、None这样的不可变对象呢? 因为不可变对象一旦创建,对象内部的数据就不能修改,
这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取不需要加锁。
同时读取一点问题都没有。
我们在编写程序时,如果可以设计一个不可变对象,那就尽量设计成不变对象。什么是不可变对象?
比如你创建一个字符串"李狗蛋",李狗蛋就是李狗蛋,不能变成其他的,至于你说,可以拆分成"李狗""李蛋"
这不是发生了对字符串的更改,而是重新生成了两个字符串"李狗""李蛋",他们与原来字符串"李狗蛋"的关系
仅仅是在原来字符串基础上衍生的新字符串,并没有对原字符串发生更改。三、可变参数在python函数中,还可以定义可变参数。
顾名思义,可变参数就是传入的参数个数是可变的,可以是1个,2个,任意个。也可以0个。
我们以数学题为例子:
给定一组数字a, b, c....,请计算a的平方 + b的平方 + c的平方要定义出这个函数,我们必须确定输入的参数。然而,很悲伤,我们竟不知道有多少参数,
即使知道有多少个,但是总不能在函数定义时全部写出来吧!我们可以显而易见的想到一个比较好的方法:把参数用一个列表或者元组传进来,然后遍历,再做运算,听起来不错。def sum(numbers):sum = 0for n in numbers:sum = sum + nreturn sum但是调用的时候,需要先组装出一个list或者tuple
>>> sum([1,2,3,4,5])
15还有更好的方法:函数定义时,设置可变参数。同
样可以遍历传入的*numbersdef sum(*numbers):sum = 0for n in numbers:sum += nreturn sum>>> sum(1,3,5,7)
16
>>> sum()
0定义可变参数和定义一个list或者tuple参数相比,仅仅在参数前面加了一个 * 号。
在函数内部, 参数number接受到的是一个tuple,因此,函数的代码完全不变。
但是调用该函数时,额可以传入任意个参数,包括0个参数。
着重理解一下:*numbers 在函数内部是一个元组形式,具备元组的一些功能。如果已经有一个现成的list或者tuple,要调用一个可变参数怎么办?
可以这样做:
>>> nums= [1, 2, 3]
>>> sum(nums[0], nums[1], nums[2])
6这种写法是可行的,但是相当繁琐。
真正好的方法是在list或者tuple前面加一个 * ,好像一个反运算,又把list或者tuple的元素拆解出来传进去了>>> sum(*nums)
6*nums表示把nums这个list的所有元素作为可变参数穿进去。这种写法相当有用,而且很常见。四、关键字参数
可变参数允许你传入0个或者任意个参数,这些可变参数在函数调用时自动组装成为一个tuple。
但是,你传入的这些不确定个数的参数仅仅是一个值,至于这个值是干什么,什么属性,一切都无从得知
比如传入可变参数[1,3,5,7] 这是可以的,但是你想传入一个x,并且准备让x=9
也就是说你想传成这样的形式[1,3,5,7,x = 9]
这样该怎么办?使用关键字参数。
关键字参数允许你传入0个或者任意个含参数名的参数,这些参数可以说有名有姓,有键有值
这些关键字参数在函数内部自动组装为一个dict还是举小学生注册的例子:
def person(name, age, **kwargs):print("name:",name, "age:",age, "other:",kwargs)函数person出了必选参数name和age外, 还接受关键字参数kwargs
在调用该函数时,可以只传入必选参数>>> person("stephen curry", 29)
name: stephen curry age: 29 other: {}当然咯,作为一个信息录入的函数,我们也十分喜欢用户传的信息详细些,比如这样
>>> person("stephen curry", 29, gender = "M", tags="the famous basketball star")
name: stephen curry age: 29 other: {'gender': 'M', 'tags': 'the famous basketball star'}所以, 关键字参数有什么用呢?
它可以扩展函数的功能。
比如,在person函数里,我们保证能接受到name和age这两个参数,但是,如果调用者愿意提供更多的参数,我们也能收到。
试想,你正在做一个用户注册的功能,除了用户名和年龄是必填项外,其他都是可选项,利用关键字参数来定义这个函数
就能满足注册的需求。当然,也可以先组装一个dict,然后用 ** 分解开传进去
>>> extra = {"reward":"twice mvp of NBA", "hobby":"golf"}
>>> person("stephen curry", 29 **extra)
name: stephen curry age: 29 other: {'reward': 'twice mvp of NBA', 'hobby': 'golf'}
** extra表示把extra这个dict的所有key-value用关键字参数传入到函数的 **kwargs
kwargs将获得一个dict
注意kwargs获得的dict是extra的一份拷贝,对kwargs的改动不会影响函数外的extra。五、命名关键字参数对于关键字参数,函数的调用者可以传入任意不收限制的关键字参数,至于到底传入了哪些
就需要在函数内部通过kwargs检查
仍以person为例,我们希望检查时候有city和job参数def person(name, gender, **kwargs):if "city" in kwargs:print("有city参数传入")if "job" in kwargs:print("有job参数传入")print("name:",name, "gender:",gender, "other:",kwargs)但是调用者仍然可以传入不受限制的关键字参数>>> person("stephen curry", "M", hobby="basketball", city="Golden States")
有city参数传入
name: stephen curry gender: M other: {'hobby': 'basketball', 'city': 'Golden States'}如果要限制关键字参数的名字,就可以用命名关键字参数, 例如,只接收city和job作为关键字参数。
这种方式定义的函数如下:
def person(name, age, *, city, job):print(name, age, city, job)命名关键字参数必须传入参数名,这和位置参数不同。
>>> person("stephen curry",29, city="Colden States",job="basketball")
stephen curry 29 Colden States basketball如果没有传入参数名,调用将报错
>>> person("stephen curry",29,"GS","basketball")
Traceback (most recent call last):File "<pyshell#85>", line 1, in <module>person("stephen curry",29,"GS","basketball")
TypeError: person() takes 2 positional arguments but 4 were given由于调用时缺少参数名city和job,python解释器把这4个参数均视为位置参数,
但是person()函数仅接受了2个位置参数。正所谓不传不行,不指名也不行。
>>> person("xiaowang",24)
Traceback (most recent call last):File "<pyshell#92>", line 1, in <module>person("xiaowang",24)
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job'
>>> person("xiaowang",24, "beijing","program")
Traceback (most recent call last):File "<pyshell#93>", line 1, in <module>person("xiaowang",24, "beijing","program")
TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job'命名关键字参数可以有缺省值,从而简化调用:
def person(name, age, *, city="BeiJing", job):print(name, age, city, job)
由于命名关键字参数city具有默认值,调用时,可不传入city参数。
>>> person("Jack",24,job="Engineer")
Jack 24 BeiJing Engineer使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个 * ,如果缺少 * ,
python解释器将无法是被位置参数和命名关键字参数
def person(name, age, city, job):# 缺少 * city和job被视为位置参数pass命名关键字参数表示,只接收命名的关键字,多余的不再接受六、参数组合在python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。
但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。比如定义一个函数,包含上述若干种参数:
def f1(a, b, c=0, *args, **kwargs):print("a = ", a, "b = ", b, "c = ", c, "args = ", args, "kwargs = ", kwargs)def f2(a, b, c=0, *, d, **kwargs):print("a = ", a, "b = ", b, "c = ", c, "d = ", d, "kwargs = ", kwargs)在函数调用的时候,python解释器会自动按照参数位置和参数名把对应的参数传进去。>>> f1(1,2)
a =  1 b =  2 c =  0 args =  () kwargs =  {}
>>> f1(1,2,c=3)
a =  1 b =  2 c =  3 args =  () kwargs =  {}
>>> f1(1,2,3)
a =  1 b =  2 c =  3 args =  () kwargs =  {}
>>> f1(1,2,3,4)
a =  1 b =  2 c =  3 args =  (4,) kwargs =  {}
>>> f1(1,2,3,4,5)
a =  1 b =  2 c =  3 args =  (4, 5) kwargs =  {}
>>> f1(1,2,3,4,5,6,7)
a =  1 b =  2 c =  3 args =  (4, 5, 6, 7) kwargs =  {}
>>> f1(1,2,3,4,5,6,x=10)
a =  1 b =  2 c =  3 args =  (4, 5, 6) kwargs =  {'x': 10}>>> f2(1,2,3, d= 108, extra=None)
a =  1 b =  2 c =  3 d =  108 kwargs =  {'extra': None}最神奇的是通过一个tuple和dict,你也可以调用上述函数:>>> f1(*args, **kwargs)
a =  1 b =  2 c =  3 args =  (4,) kwargs =  {'d': 99, 'x': None}>>> f2(*args,**kwargs)
a =  1 b =  2 c =  3 d =  99 kwargs =  {'tags': 'basketball'}所以,对于任意函数都可以通过类似func(*args, **kwargs)的形式调用它,因为加入星号*作为参数传入进函数时
就相当于把参数拆解成一个个,如果传入的参数正确,函数会自动进行一一赋值。总结:python的函数具有非常灵活的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。注意:默认参数一定要用不可变对象,如果是可变对象,程序运行时会有逻辑错误。注意:定义可变参数和关键字参数的语法
*args是可变参数, args接收的是一个tuple
**kwargs 是关键字参数,kwargs接收的是一个dict注意:调用函数时如何传入可变参数和关键字参数的语法
可变参数既可以直接传入:func(1,2,3),又可以先组装成list或者tuple通过*args传入:func(*(1,2,3))
关键字参数既可以直接传入:func(a = 1, b = 2),又可以先组装dict,再通过**kwargs传入:func(**{"a":1,"b":2})使用*args 和 **kwargs 是python的习惯写法,当然也可用其他的写法,不过最好用习惯写法。命名的关键字参数是为了限制调用者可以传入的参数,同时可以提供默认值。注意:定义命名关键字参数在没有可变参数的情况下不要忘了写分隔符 * ,否则定义的将是位置参数。

 

转载于:https://www.cnblogs.com/themost/p/7242970.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369861.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gRPC之grpcurl

1、grpcurl grpcurl项目地址 &#xff1a;https://github.com/fullstorydev/grpcurl 一般情况下测试 gRPC 服务&#xff0c;都是通过客户端来直接请求服务端。如果客户端还没准备好的话&#xff0c;也可以使用 BloomRPC (https://appimage.github.io/BloomRPC/)这样的 GUI 客…

算法初步——two pointers

什么是 two pointers   以一个例子引入&#xff1a;给定一个递增的正整数序列和一个正整数 M&#xff0c;求序列中的两个不同位置的数 a 和 b&#xff0c;使得它们的和恰好为 M&#xff0c;输出所有满足条件的方案。 本题的一个最直观的想法是&#xff0c;使用二重循环枚举序…

H5 _拖放使用

1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <title>拖放API</title>6 <style>7 [iddragme]{8 width: 100px;9 height: 100px; 10 …

从XaaS到Java EE – 2012年哪一种该死的云最适合我?

您是否曾经想过要让Java EE在某个地方启动和运行需要什么&#xff1f; 是的 多年。 从托管我自己的主机开始&#xff0c;转到一些托管产品 &#xff0c;最后偶然发现了PaaS运动。 老实说&#xff0c;我并没有太认真。 我只是想把我的东西放到某个地方&#xff0c;而不在乎解决…

正方体最快最简单画_素描新手入门第一幅画可不只是“正方体”

很多素描教程都把正方体作为入门第一幅画学习内容。这种现象也成了约定俗成的规矩但是&#xff0c;学过画画的人大概都知道有很多人画了多年石膏几何形、静物、人头像甚至半身像全身像。到最后落得只会画这些学过的东西。这就说明学习出了问题。绘画练习一定要弄清楚每个物体练…

重写Alert和confirm方法去除地址显示

//重写alert方法&#xff0c;去掉地址显示window.alertfunction(name){var iframedocument.createElement("IFRAME");iframe.style.display"none";iframe.setAttribute("src",data:text/plain,); document.documentElement.appendChild(iframe);…

VS2015配置内核WDK7600环境,32位下.

VS2015配置内核WDK7600环境,32位下. 学习内核驱动的编写,就要会配置环境.不然总是用记事本编写.比较不方便. 环境配置如下. 1.首先下载WDK7600, 课堂资料代码中已经上传.链接&#xff1a;https://pan.baidu.com/s/1o9PjpUU 密码&#xff1a;k5sp 2.VS2015下载. 这个网络上有很多…

Camel 2.11 –具有URL重写功能的HTTP代理路由

在即将发布的Apache Camel 2.11版本中&#xff0c;我最近添加了对将自定义url重写实现插入基于HTTP的路由&#xff08;http&#xff0c;http4&#xff0c;jetty&#xff09;的支持。 当您使用骆驼代理/桥接HTTP路由时&#xff0c;这使人们可以控制url映射。 例如&#xff0c;假…

我的改进版2048(1)

&#xff08;假设有谁想要这个软件的话&#xff0c;在评论中留一个邮箱吧。&#xff09; 前几天好几次看到有朋友晒出玩2048刷高分的截图。我就想我能不能也做一个2048呢&#xff1f;细致想了想2048游戏的规律&#xff0c;发现事实上逻辑上非常easy&#xff0c;也不用研究什么算…

什么是 HTML5?

HTML5 是下一代的 HTML。 什么是 HTML5&#xff1f; HTML5 将成为 HTML、XHTML 以及 HTML DOM 的新标准。 HTML 的上一个版本诞生于 1999 年。自从那以后&#xff0c;Web 世界已经经历了巨变。 HTML5 仍处于完善之中。然而&#xff0c;大部分现代浏览器已经具备了某些 HTML5 支…

涉及CDI和JSF的过期对话的定制错误页面

自上次写博客以来已经有一段时间了。 我一直在考虑写一些技术博客&#xff0c;但最终却忙于其他事情。 上周&#xff0c;在Coderanch论坛上进行了非常有趣的讨论。 甚至更有趣&#xff0c;因为它涉及JBoss。 熟悉Java EE Web应用程序的开发人员会知道&#xff0c;Web应用程序部…

2020年市场最缺什么_2020年聚合氯化铝市场评述

2020年聚合氯化铝市场评述一、行情概述&#xff1a;今年聚合氯化铝价格整体呈下滑趋势&#xff0c;接近年底价格才有小幅反弹。但不同时期价格有小幅起伏&#xff0c;主要受疫情影响&#xff0c;在下游需求不佳的影响下价格出现下滑。1月受疫情影响&#xff0c;前期停产企业短期…

通过反射来将一个类的内容转换到另外一个类里

主函数&#xff1a; import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.ValueFilter;import com.google.common.base.Preconditions; import java.lang.reflect.Field;import java.lang.reflect.Modifier; public class leijun {public static void ma…

sql语句中left join和inner join中的on与where的区别分析

sql语句中left join和inner join中的on与where的区别分析 原文:sql语句中left join和inner join中的on与where的区别分析关于SQL SERVER的表联接查询INNER JOIN 、LEFT JOIN和RIGHT JOIN&#xff0c;经常会用到ON和WHERE的条件查询&#xff0c;以前用的时候有时是凭感觉的&…

开发辅助 | 阿里图标库iconfont入门使用

目前大多数的互联网公司&#xff0c;前端开发和UI设计师配合中&#xff0c;针对设计师给图的效果图&#xff0c;前端开发工程师不再像往常一样对于细小图标进行切图&#xff0c;取而代之的是引用阿里图标库&#xff08;http://iconfont.cn/&#xff09;&#xff1b;简单的临时开…

使用Spring Security对RESTful服务进行身份验证

1.概述 本文重点介绍如何针对提供安全服务的安全REST API进行身份验证 -主要是RESTful用户帐户和身份验证服务。 2.目标 首先&#xff0c;让我们看一下参与者-典型的启用了Spring Security的应用程序需要针对某些事物进行身份验证-该事物可以是数据库&#xff0c;LDAP或可以是…

可拖动的弹窗

pc端&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>可拖动的弹窗</title> <style type"text/css"> a{text-decoration: …

向量外积_解析几何 -向量

目录1.向量2.内积3.外积4.混合积5.双重外积6.关系式正文1.向量vector 引入vector O规定O没有确切的方向&#xff0c;即与任何向量不仅平行&#xff0c;而且垂直。申明&#xff1a;本文章的向量为自由向量&#xff0c;即始点不固定的向量&#xff0c;它可以任意的平行移动&#…

HTML5 参数传递

页面显示效果&#xff0c;如下图&#xff1a; 主页面代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><br><br><a href"jssendValue.html?i…

双向@OneToOne主键关联

现在该继续有关Hibernate的文章了。 最后一个致力于单向OneToOne关联 。 因此&#xff0c;今天我将向您展示如何获取双向OneTonOne主键关联 。 本教程中基于前一篇文章的示例。 让我们开始吧。 我将使用以前创建的相同表。 为了建立双向一对一关联&#xff0c;我需要更新两个P…