本文主要是介绍python练习生|函数三部曲——初来乍到(初识函数参数),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
python练习生|函数三部曲——初来乍到(初识函数&参数)
- 一.认识对象
- 1.对象分类
- 2.基本应用
- 1).可变对象
- 二.函数
- 1.初识函数
- 2.函数的语法
- 3.函数的参数
- 1).参数的分类
- 4.函数的传递方式
- 1).默认值传递
- 2). 位置传递
- 3). 关键字传递
- 5.函数中的对象
- 6.不定长参数
- 1).要点
- 2).( *号参数)和(** 参数)举例
- 7.参数的解包
在我们拜访函数之前,我们先要有一个认知,你认识的对象是否可变?
不要多想,不是那个对象,而是下面我要说的对象
- 在python领域有这样一句话:“一切皆为对象”。当然,这句话你自己知道就好了。要是面试的时候面试官问你,什么是对象?
- 你回答:一切皆是对象,恭喜你,喜提再来一次。
还记得之前的博文有说到序列吗?一.序列
序列其实就是一种对象
那么,按照序列的分类,发散思维,对象也可分为可变对象和不可变对象
一.认识对象
1.对象分类
- python中保存的数据都是围绕对象这个概念展开的
- 对象分类:可变对象和不可变对象
- 可变对象:例如 list,dict
- 不可变对象:例如 int,str,tuple
2.基本应用
-
每个对象中都保存了三个数据:内存地址(id)、对象类型(type)、值(value)
-
有鉴于可变对象的实用性,在这里我们仅浅谈可变对象
1).可变对象
- 我们所说的可变对象,其实就是说,对象内变量的值是可以改变的
- 只要是在原来值的基础上进行修改,其内存地址就不会发生改变
- 如果我们通过重新赋值改了变量,虽说也是改变了对象的值,但是其对应的内存地址也改变了
举个栗子:
lst = [1,2,3,4]
print(lst,type(lst),id(lst))
lst[1] = 100
print(lst,type(lst),id(lst))lst = [2,3,4,5] #重新赋值
print(lst,type(lst),id(lst))
- 我们可以这样理解:
lst[1] = 100
- 这步操作是修改对象里面的值,通过对象内部修改变量来进行
- 因此,这个操作不会改变变量指向的对象
lst = [2,3,4,5]
- 这步操作相当于对变量进行重新赋值,相当于生成新的对象
- 因此,这个操作会改变变量指向的对象
(直白的讲:)就相当于你去看牙了,你有颗牙坏了,你去修修补补,这个牙还是你的牙(你的对象并没有变)。但是,如果你把这颗牙拔了,重新换一颗牙,那么,这个牙对于你来说,相当于是新的对象(你的对象变了)。
分割线:对象就讲到这,提前讲有助于我们后面对函数的理解
二.函数
1.初识函数
- 函数:函数是一个功能代码块,在执行其功能时还有一个入口和出口,入口就是函数中的各种形参,出口就是实现功能的返回值
- 函数⽤来保存⼀些可执⾏的代码,并且可以在需要时,对这些语句进⾏多次调⽤
- 函数也是⼀个对象
2.函数的语法
def 函数名([形参1,形参2,形参3....]):代码块
注意:
- 函数名必须符合标识符的规范(可以包含字⺟、数字、下划线但是不能以数字开头)
- 关于函数和标识符的相关介绍请移步:函数&标识符
- 函数中保存的代码不会立即执行,需要调用后才会执行
def fn(a,b,c):print("a =",a)print("b =",b)print("c =",c)fn
print(fn,type(fn))
fn(1,2,3)
- 如图所示:fn是函数,而fn()则是调用函数
3.函数的参数
1).参数的分类
- 分类:形参和实参
- 形参(形式参数): 定义形参就相当于在函数内部声明了变量,但是并不是赋值
- 实参(实际参数):指定了形参,在调⽤函数时必须传递实参,实参将会赋值给对应的形参,一般来说有⼏个形参就要有⼏个实参
就拿这部分代码来说
def fn(a,b,c): #声明了三个形参,a,b,cprint("a =",a)print("b =",b)print("c =",c)fn(1,2,3) #通过函数的调用将实参(1,2,3)传递给形参(a,b,c)
4.函数的传递方式
- 默认值传递:定义形参时,可以为形参指定默认值。指定了默认值以后,在调用函数过程中,如果传递了参数(实参)则默认值不会⽣效。如果没有传递,则默认值就会⽣效。
- 位置传递:位置参数就是将对应位置的实参赋值给对应位置的形参
- 关键字传递:关键字参数可以不按照形参定义的顺序去传递,⽽根据参数名进⾏传递
1).默认值传递
def fn(a=10): # a = 10 是默认值参数print('a = ',a)
fn() # 没有实参的传递,则打印默认值参数
fn(3) # 3 是实参,则,把三赋值给函数中的 形参a 默认值不打印
-我们把在函数fn ( a=10)中,10称之为参数的默认值,如果在调用函数fn的过程中,你没有传参进去,那么函数中形参a将被默认值赋值,并执行函数调用,输出相应结果
-如果你传参进去,则实参将付给形参,而默认值参数则失效
2). 位置传递
def fn1(a,b,c):print('fn1函数中,a =',a)print('fn1函数中,b =',b)print('fn1函数中,c =',c)
fn1(3,4,5)
- 每个实参和形参都有以一一对应的关系
如果你多传或者少传了参数,则会进行报错
举个栗子:
def fn1(a,b,c):print('fn1函数中,a =',a)print('fn1函数中,b =',b)print('fn1函数中,c =',c)
fn1(3,4) # TypeError: fn1() missing 1 required positional argument: 'c'def fn2(a,b,c):print('fn1函数中,a =',a)print('fn1函数中,b =',b)print('fn1函数中,c =',c)
fn2(3,4,5,6) # TypeError: fn2() takes 3 positional arguments but 4 were given
- 从图中我们可以看出,fn1函数在进行实参赋值时,少了一个实参,系统进行报错;fn2函数在进行实参赋值时,多传了一个实参,少传了一个形参,系统依旧会报错。
3). 关键字传递
def fn1(a,b,c):print('fn1函数中,a =',a)print('fn1函数中,b =',b)print('fn1函数中,c =',c)
fn1(3,b=1,c=4)
- 在参数调用中,我们把形参b,c分别赋予了详细的实际参数————称之为关键字参数的传递
- 从这写代码中,不难发现,b和c都有关键字传递,a进行位置传递
那么,问题出现了: 我们在进行关键字传递时,可以对不同参数任意顺序排序吗? 咱们来有一码一
def fn1(a,b,c):print('fn1函数中,a =',a)print('fn1函数中,b =',b)print('fn1函数中,c =',c)
fn1(4,b=1,5)
改进之后:
-总结:混合使⽤位置参数和关键字参数的时候必须将位置参数写到关键字参数前⾯
5.函数中的对象
- 代码块一:
def fn(a):a = 50print('a =',a,type(a))
b = 20
print(b)
fn(b)
- 代码块二:
def fn(a):a = 50print('a =',a,type(a),id(a))
b = 20
fn(b)
print(b,id(b))
- 上述代码你一定看得多了,第一个代码块是进行的是一个位置传递,第二个代码块进行的是函数内部的参数重新赋值。
- 但是,这是不可变对象之间的传递,如果我们进行 可变对象 的传递,并在函数中对参数进行再次赋值,会是什么结果呢?
- 代码块三
def fn(a):a[0] = 10print('a =',a,type(a),id(a))
b = [1,2,3,4]
print(b,id(b))
fn(b)
print(b,id(b))
- 从图中,我们可以得到的结果是,这两个变量虽然数值变了,但是所对应的(内存地址(id)),并没有变,我们之前就已经生动形象的举过修牙的例子了,要是还不能够很好理解,可以再思考思考,把思绪捋一捋。
- 再换一种说法:以上述代码块三举例。
我们把 列表b 的值赋值给 参数a ,也就是说,对象b和对象a对应共同的值(value),并且二者的内存地址(id)是相同的。
只要是在原来值的基础上进行修改,其内存地址就不会发生改变 - 我们把对象a中的变量进行修改了,虽然值改变了,但是对应的内存地址还是原来的
- 那么我们得到的反馈是,地址没变,值变了
- 与值相关的对象a,和列表b,定会发生变量的改变
6.不定长参数
1).要点
- 定义函数时,有时会在形参前⾯加⼀个 * ,那么这个带有 (* 号)的形参可以获取到所有的实参,它会将所有的实参保存到⼀个元组中
- 带(* 号)的形参只能有⼀个,可以和其他参数混合使⽤
- (带 * 号的 形参)只能接受位置参数,不能接受关键字参数
- (带 ** 号的 形参)可以接收其他的关键字参数,它会将这些参数统⼀保存到字典当中。字典的key就是参数的名字,字典的value就是参数的值
- (** 形参)只有⼀个,并且必须写在所有类型参数的后⾯
2).( 号参数)和(* 参数)举例
- 下面我们进行举例:
*号参数:
def fn1(a,b,*c): print('fn1函数中,a =',a,type(a))print('fn1函数中,b =',b,type(b))print('fn1函数中,c =',c,type(c))
fn1(3,4,5,6,7)
- *c 的作用是在进行位置传参后,对于多的实参,会把他们打包成一个元组传给形参(*c)
- 基于此,如果你善于思考,但是,你却忘了之前的要点,你可能又会诞生之前的问题
问号1:星号参数可以有多个吗?
问号2:星号参数可以在形参中的任意位置吗?
问号3:星号参数可以接受任意参数吗? - 问号一解答:
#问号1
def fn2(a,*b,*c): #SyntaxError: invalid syntax(只能有一个(*参数))print('fn1函数中,a =',a,type(a))print('fn1函数中,b =',b,type(b))print('fn1函数中,c =',c,type(c))
fn2(3,4,5,6,7)
- 问号二解答:
#问号2
def fn3(a,*b,c):print('fn1函数中,a =',a,type(a))print('fn1函数中,b =',b,type(b))print('fn1函数中,c =',c,type(c))
fn3(3,4,5,6,7) # TypeError: fn3() missing 1 required keyword-only argument: 'c'
- 问号二延伸:怎么才能让我们的星号参数在形参的任意位置呢?
#问号2延伸
def fn3(a,*b,c,d):print('fn1函数中,a =',a,type(a))print('fn1函数中,b =',b,type(b))print('fn1函数中,c =',c,type(c))print('fn1函数中,d =',d, type(d))
fn3(3,4,5,6,c=7,d=8)
-
通过代码,敏锐的你一定发现了,星号参数可以在形参中的任意位置,那么相应的,你需要在调用参数时,将星号参数后面的参数以 关键字参数的形式 进行调用。
-
问号三解答:
#问号三
def fn4(a,c,*b):print('a =',a,type(a))print('b =',b,type(b))print('c =',c,type(c))
fn4(3,4,5,1,2)
fn4(c=3,a=4,g=5,d=1,e=2) # TypeError: fn4() got an unexpected keyword argument 'g'
**号参数:
- 我们进行举一反三?问题差不多,按照*号参数的问题逻辑,我们在来探讨一下 **号参数
- 问号1:双星参数可以有多个吗?
问号1解答:
def fn4(a,**b,**c): #SyntaxError: invalid syntaxprint('a =', a, type(a))print('b =',b,type(b))print('c =',c,type(c))
fn4(1,2,3,4,5,76)
从图中,我们能够认识到 双星参数只能有一个
- 问号2:双星参数可以在形参中的任意位置吗?
问号2解答:
#问号二
def fn4(a,**b,c): # SyntaxError: invalid syntaxprint('a =',a,type(a))print('b =',b,type(b))print('c =',c,type(c))
fn4(1,2,3,4,5)
双星参数只能在形参最后一个
- 问号3:双星参数可以接受关键字参数吗?
问号3解答:
#问号三
def fn4(a,c,**b): print('a =',a,type(a))print('b =',b,type(b))print('c =',c,type(c))
fn4(3,4,g=5,d=1,e=2)
双星参数可以接受关键字参数,且会将参数打包到字典
7.参数的解包
- 传递实参时,也可以在序列类型的参数前添加星号,这样它会⾃动的将序列中元素依次作为参数传递
- 要求序列中的元素的个数必须和形参的个数⼀致
def fn4(a,b,c,d):print('a =',a,type(a))print('b =',b,type(b))print('c =',c,type(c))print('c =',d,type(d))
fn4(*[1,3,4,5])
这篇关于python练习生|函数三部曲——初来乍到(初识函数参数)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!