上一章我们已经把函数最核心的骨架搭起来了。
你已经知道,函数不是一段被包起来的代码而已。 它是一个能接收输入、处理信息、再把结果交回来的小工具。
也正因为函数要接收输入,参数这件事就开始变得越来越重要。
前面我们只是先认识了参数,知道函数定义时可以在括号里留位置,调用时把具体值传进去。 但真正一写多了,你马上就会碰到几个非常现实的问题:
参数到底是按什么规则对应上的 有些参数每次都要传,有没有办法给个默认值 参数一多时,顺序总记不住怎么办 我能不能直接写参数名,把值指定给它 位置参数、默认参数、关键字参数,到底什么时候该用谁
这些问题如果没理顺,函数很容易写得又乱又脆。 但只要理顺了,你会明显感觉到:
函数不仅更灵活了,而且更顺手了。
这一章,我们就把参数里最基础、最常用、也最容易混的三件事讲透:
位置参数 默认参数 关键字参数
这三样看起来只是细节, 其实它们决定了你后面函数写得清不清楚、调得顺不顺手。
一、先把最基础的参数调用规则重新站稳
我们先从最普通的函数开始。
比如:
defshow_info(name, age): print(f'我叫{name},今年{age}岁')
调用:
show_info('张三', 18)
输出:
我叫张三,今年18岁
这里其实已经隐含了一个很重要的规则:
第一个值 '张三' 对应第一个参数 name第二个值 18 对应第二个参数 age
这种靠顺序一一对应的方式,就是位置参数最核心的逻辑。
你现在先记一句特别重要的话:
只要你调用函数时,直接按顺序往里塞值,这些值通常就是按位置去匹配参数的。
这就是后面所有内容的起点。
二、什么是位置参数
位置参数,说白了就是:
按照位置顺序来传递的参数
比如:
defadd(a, b):return a + b
调用:
print(add(3, 5))
这里的 3 会给 a,5 会给 b。 为什么?
因为它们出现的位置就是这么排的。
第一个位置给第一个参数 第二个位置给第二个参数
再看一个稍微长一点的例子:
defstudent_info(name, age, city): print(f'姓名:{name},年龄:{age},城市:{city}')
调用:
student_info('张三', 18, '北京')
输出:
姓名:张三,年龄:18,城市:北京
这里也是同样的道理。
张三 对应 name18 对应 age北京 对应 city
这就是最标准、最基础的位置参数调用方式。
三、位置参数为什么最容易上手
因为它最符合我们一开始的直觉。
函数定义里写了几个参数, 调用时就按顺序把几个值塞进去。
这种方式特别适合参数不多、含义也很清楚的函数。
比如:
defmultiply(a, b):return a * b
调用:
print(multiply(4, 6))
谁看到都不难理解。
再比如:
defget_total(price, count):return price * count
调用:
print(get_total(59.9, 3))
也很自然。
所以位置参数的优点就是:
简单 直接 适合短小函数 写起来快
这也是为什么你最先接触到的参数形式,几乎都是位置参数。
四、位置参数最容易犯的错,就是顺序错了
这点一定要单独拎出来说。
看函数:
defshow_info(name, age): print(f'我叫{name},今年{age}岁')
正常调用:
show_info('张三', 18)
但如果你写成:
show_info(18, '张三')
程序大概率不会报错。 可输出会变成:
我叫18,今年张三岁
这就是位置参数的典型风险:
程序不一定提醒你 但结果可能已经悄悄错了
因为它根本不管你心里怎么想。 它只认位置。
第一个值就给第一个参数 第二个值就给第二个参数
所以位置参数虽然简单,但只要参数一多,或者含义稍微相近,就很容易传错顺序。
这也是为什么后面关键字参数会那么有用。
五、位置参数个数也必须对上
除了顺序,数量也得对。
比如:
defadd(a, b):return a + b
你正常应该传两个值。
print(add(3, 5))
如果你只传一个:
print(add(3))
就会报错。
如果你传三个:
print(add(3, 5, 7))
也会报错。
因为这个函数明确说了,它要两个参数。 那你就得不多不少,正好给两个。
所以位置参数最基础的两条规则就是:
顺序要对 个数要对
只要这两条出问题,函数调用就会不顺。
六、如果某个参数大多数时候都一样,怎么办
这时候默认参数就该出场了。
来看一个很典型的例子。
比如你想写一个函数,打印欢迎语。 大多数时候,欢迎对象的城市都写成 北京。 偶尔才会变。
如果还像普通位置参数那样写:
defwelcome(name, city): print(f'欢迎{name},你来自{city}')
那每次调用都要写城市:
welcome('张三', '北京')welcome('李四', '北京')welcome('王五', '上海')
你会发现,北京 被重复传了好多次。 这就有点累。
这时候就可以给参数一个默认值:
defwelcome(name, city='北京'): print(f'欢迎{name},你来自{city}')
这样调用时:
welcome('张三')welcome('李四')welcome('王五', '上海')
输出:
欢迎张三,你来自北京欢迎李四,你来自北京欢迎王五,你来自上海
这就是默认参数最直接的价值:
大多数时候一样的内容,不用每次都手动再传一遍。
七、什么是默认参数
默认参数,就是:
在定义函数时,先给某个参数一个默认值 如果调用时没传,就自动用这个值 如果传了,就用你传的那个
形式长这样:
def 函数名(参数=默认值): ...
比如:
defsay_hello(name, word='你好'): print(f'{word},{name}')
调用:
say_hello('张三')say_hello('李四', '欢迎')
输出:
你好,张三欢迎,李四
你会发现,默认参数本质上是在给函数准备一个 兜底值。
你不给,我就用默认的。 你给了,我就尊重你的输入。
这会让函数既有默认行为,又保留灵活性。
八、默认参数特别适合哪些场景
特别适合这类情况:
某个参数常常有固定值 但偶尔又允许修改
比如打印分隔线:
defprint_line(char='-', count=20): print(char * count)
调用:
print_line()print_line('*', 10)
输出:
--------------------**********
你看,这就很顺。
平时不用多想,直接调用就有默认效果。 真要变,也可以临时改。
再比如发通知时,默认签名固定:
defsend_message(name, sender='系统管理员'): print(f'{name},请查收。发送人:{sender}')
大多数时候你不用传 sender, 但偶尔你也可以指定。
这就是默认参数特别好用的地方。
九、默认参数和位置参数可以一起用
当然可以。
其实最常见的函数写法之一,就是:
前面放普通位置参数 后面放带默认值的参数
比如:
defstudent_info(name, age, city='北京'): print(f'姓名:{name},年龄:{age},城市:{city}')
调用:
student_info('张三', 18)student_info('李四', 20, '上海')
输出:
姓名:张三,年龄:18,城市:北京姓名:李四,年龄:20,城市:上海
这里:
name 和 age 是必须传的city 是可选的,不传就默认北京
这就是很典型的函数设计思路:
核心参数必须给 附加参数可以有默认值
你以后写函数时,这种结构会特别常见。
十、为什么默认参数要放在后面
这个规则很重要。
比如下面这种写法就是不合适的:
defshow_info(city='北京', name, age): ...
为什么不行?
因为调用时程序会很难判断:
你传进来的第一个值,到底该对应哪个参数
而如果写成这样:
defshow_info(name, age, city='北京'): ...
就很清楚了。
必须传的先匹配 可选的放后面补充
所以你先记住一个非常实用的规则:
默认参数通常放在普通位置参数后面
这不仅是语法习惯,也符合人的阅读和调用直觉。
十一、默认参数不是让你少写代码而已,它还在表达“推荐配置”
这个角度特别值得理解。
比如:
defconnect_db(host='127.0.0.1', port=3306): print(f'连接数据库:{host}:{port}')
这里的默认值不只是为了偷懒。 它其实还在表达一层意思:
通常情况下,我建议就用这个配置
再比如:
defformat_score(score, digits=1):returnf'{score:.{digits}f}'
这里 digits 如果以后设计得更完整,默认值也可以表达:
平时我们建议保留 1 位小数
所以默认参数除了方便调用, 还在向使用者传达:
这个函数平时推荐怎么用
这也是为什么函数写得好不好,和参数设计关系非常大。
十二、接下来是关键字参数:参数一多时,终于不用死记顺序了
我们先看一个例子。
函数:
defstudent_info(name, age, city): print(f'姓名:{name},年龄:{age},城市:{city}')
如果用位置参数调用:
student_info('张三', 18, '北京')
当然可以。
但你有没有发现,参数一多时,眼睛其实挺累。
尤其是以后函数再复杂一点,比如:
defcreate_user(name, age, city, email, phone): ...
这时候你再纯靠位置传值,很容易写错、看错、改错。
这时候,关键字参数就特别香了。
调用可以写成:
student_info(name='张三', age=18, city='北京')
这里就是关键字参数。
十三、什么是关键字参数
关键字参数,就是:
调用函数时,明确写出 参数名=值
比如:
defshow_info(name, age): print(f'我叫{name},今年{age}岁')
你可以这样调用:
show_info(name='张三', age=18)
也可以这样:
show_info(age=18, name='张三')
输出都是:
我叫张三,今年18岁
你会发现,关键字参数最大的好处就是:
顺序不再那么重要了
因为你已经明确告诉函数:
这个值给谁 那个值给谁
这会让代码清晰很多。
十四、关键字参数为什么特别适合参数多的函数
因为它让函数调用变得像填表。
比如:
defcreate_user(name, age, city, email): print(name, age, city, email)
如果只写位置参数:
create_user('张三', 18, '北京', 'test@example.com')
虽然能跑,但你得花点时间确认:
第三个到底是城市还是邮箱 第四个到底是邮箱还是手机号
可如果写成关键字参数:
create_user(name='张三', age=18, city='北京', email='test@example.com')
就一眼看明白了。
这就是关键字参数最核心的优势:
可读性特别强
尤其是当参数有四五个以上时,这个优势会越来越明显。
十五、关键字参数还能避免位置传错的问题
比如:
defrectangle(length, width): print(length * width)
用位置参数时,你要记得哪个在前,哪个在后。
但如果写成关键字参数:
rectangle(length=5, width=3)
就非常清楚。
再比如:
defuser_info(name, age, city): print(f'{name}-{age}-{city}')
你如果位置写错,程序不一定报错。 但关键字参数就不容易出现这种 silent bug。
因为你已经明确地说了:
这个值就是给 name这个值就是给 age
所以关键字参数不仅让调用更清楚, 也在帮你减少低级错误。
十六、位置参数和关键字参数,可以混着用吗
可以,但有规则。
比如:
defshow_info(name, age, city): print(f'姓名:{name},年龄:{age},城市:{city}')
你可以这样写:
show_info('张三', age=18, city='北京')
这没问题。
但你不能这样写:
show_info(name='张三', 18, city='北京')
这就不合适了。
为什么?
因为一旦开始用关键字参数,再在后面塞纯位置参数,调用顺序就会很混乱。
所以你先记住一个简单规则:
可以先写位置参数,再写关键字参数 但不要先写关键字参数,再写位置参数
这条规则非常实用。
十七、三种参数放在一起看,你就更容易理解
我们来看一个统一例子:
defgreet(name, word='你好'): print(f'{word},{name}')
调用方式一,纯位置参数:
greet('张三')greet('李四', '欢迎')
调用方式二,关键字参数:
greet(name='王五')greet(name='赵六', word='早上好')
调用方式三,混合:
greet('小明', word='晚上好')
你会发现:
位置参数负责按顺序快速传值 默认参数负责提供默认行为 关键字参数负责让调用更清楚、更灵活
这三者不是互相替代的关系。 而是你写函数时可以组合使用的三种基础手段。
十八、一个特别常见的实战函数:商品总价计算
比如:
defget_total(price, count=1, discount=1):return price * count * discount
这里:
price 是必须传的count 默认 1discount 默认 1
调用时你可以很灵活:
print(get_total(100))print(get_total(100, 3))print(get_total(100, 3, 0.8))print(get_total(100, discount=0.8))
输出大概是:
100300240.080.0
这个函数特别有代表性。
因为现实里很多函数,恰恰就是这样设计的:
必须给核心输入 其他参数可以用默认值兜底 当调用者有特殊需求时,再通过关键字参数定制
这就是参数设计真正开始有味道的地方。
十九、再看一个很像文本处理工具的例子
比如写一个函数,把文本整理成统一格式。
defclean_text(text, to_lower=True): text = text.strip()if to_lower: text = text.lower()return text
调用:
print(clean_text(' PYTHON '))print(clean_text(' PYTHON ', to_lower=False))
输出:
pythonPYTHON
这里的 to_lower=True 就很典型。
大多数时候,我们希望统一转小写。 所以默认值是 True。 但偶尔如果不想转,也可以通过关键字参数改掉。
这比你写两个类似函数会自然得多。
二十、什么时候优先用位置参数
一般来说,当函数参数不多,而且含义非常明确时,位置参数最省事。
比如:
defadd(a, b):return a + b
调用:
add(3, 5)
就很好。
再比如:
defarea(length, width):return length * width
调用:
area(5, 3)
也很自然。
所以位置参数适合的通常是:
参数少 顺序清晰 一眼就不容易搞错
这种时候,没必要强行全写关键字参数。
二十一、什么时候优先考虑默认参数
当某个参数大多数时候都一样, 但又希望保留调整空间时,默认参数特别合适。
比如:
defprint_line(char='-', count=20): print(char * count)
你平时直接调用就行。 需要特殊样式时,再改参数。
所以默认参数特别适合:
常用值比较固定 偶尔需要改 不想每次都重复传同样内容
它不是为了炫技巧, 而是在给函数建立一个更舒服的默认行为。
二十二、什么时候关键字参数特别值得用
当参数比较多、意义容易混、调用时希望更清楚时,关键字参数很值得用。
比如:
defcreate_user(name, age, city, email): ...
这种函数如果全靠位置传值, 调用时很容易眼花。
而写成:
create_user(name='张三', age=18, city='北京', email='test@example.com')
就特别清楚。
所以关键字参数特别适合:
参数多 顺序不好记 希望调用代码更自解释 希望少犯位置传错的错误
这一点在真实项目里非常常见。
二十三、最容易犯的几个错
第一,位置参数顺序传错。 程序未必报错,但结果可能悄悄错掉。
第二,默认参数放在普通参数前面。 这通常不合适,也容易引发语法问题。
第三,函数定义有默认参数,调用时却忘了自己可以省略它,于是每次还在机械重复传一样的值。
第四,关键字参数和位置参数混用时顺序写反。 一定记得:先位置,后关键字。
第五,函数一复杂就全靠位置参数硬传,导致调用处可读性越来越差。 这时候往往该考虑关键字参数了。
二十四、这一章最该建立的,不是术语,而是参数设计意识
很多人学到这里,容易把注意力全放在名字上:
这叫位置参数 那叫默认参数 那又叫关键字参数
这些名字当然要知道。 但更重要的是,你要慢慢建立一个感觉:
我的函数到底需要几个必要输入 哪些是必须给的 哪些可以给默认值 哪些在调用时容易写混,应该考虑关键字参数
这其实已经不是简单记忆了, 而是在开始学怎么设计一个更好用的函数。
这一步非常关键。 因为后面函数一多,程序顺不顺,往往就取决于参数设计得好不好。
二十五、练习题:这一章一定要手敲
下面这些小练习特别有代表性,建议你自己敲一遍。
1. 定义一个函数,接收姓名和年龄,打印个人信息
defshow_info(name, age): print(f'我叫{name},今年{age}岁')show_info('张三', 18)
2. 定义一个函数,接收价格和数量,数量默认是 1
defget_total(price, count=1):return price * countprint(get_total(100))print(get_total(100, 3))
3. 用关键字参数调用上面的函数
print(get_total(price=100, count=3))print(get_total(price=100))
4. 定义一个函数,打印欢迎语,默认问候词是 你好
defgreet(name, word='你好'): print(f'{word},{name}')greet('李四')greet('王五', '欢迎')greet(name='赵六', word='早上好')
5. 定义一个函数,接收长和宽,返回面积,并分别用位置参数和关键字参数调用
defarea(length, width):return length * widthprint(area(5, 3))print(area(length=5, width=3))
你把这几题敲顺,位置参数、默认参数、关键字参数的感觉就会稳定很多。
二十六、本章小结
这一章你真正要带走的,不只是三个名字,而是一套更细的参数使用思路。
位置参数,按顺序传值,简单直接。 默认参数,给函数准备好常用默认行为。 关键字参数,让调用更清楚,也更不容易传错。
你要记住几件最实用的事:
位置参数看顺序 默认参数看兜底 关键字参数看清晰度 默认参数通常放在普通参数后面 混用时一般先位置,再关键字
从这一章开始,函数不只是能接收参数了, 而是开始学会用更合理的方式接收参数。
这一步特别关键。 因为它会直接影响你后面函数写得灵不灵活、顺不顺手、清不清楚。
下一章我们继续讲 可变参数 *args 和 **kwargs 到底怎么理解。 到那一章,你会发现,前面这些参数虽然已经够常用了,但还有一种场景很现实:
你压根不确定以后会传几个参数。