函数 Function
1. 函数定义 Def
函数定义需要用def声明,注意声明函数名、参数后需要加冒号“:”。
函数中可以有return,也可以没有。这一点与Java中需要明确返回值类型是不同的,感觉更加简洁。
#Example_1
#无参数型
def simpleFunc():
print("This is a simple function example.")
#Example_2
#有参数型
def printEach(a,b,c):
print("a",a)
print("b",b)
print("c",c)
对于一个先建好、但还没有确定好具体函数内容的,可以用pass占位。
#Example_3
def undecided(a,b,c):
pass
这一点,和Java相比也比上来在必须要最后加一个return XXX(返回对应类型值)好很多。
2. 参数-向函数传递信息
通过参数,我们可以向函数传递信息,并由此进行一定的处理、输出。
以def中的有参数型函数为例:
#Example_2
def printEach(a,b,c)
print("a",a)
print("b",b)
print("c",c)
printEach(1,2,3)
这里a,b,c就是需要我们输入的参数,而printEach(1,2,3)
则是我们在函数外通过输入3个参数来调用函数。
2.1. 实参和形参
在2.2展示的Example_2中,定义函数时参数位a,b,c,而在调用时使用的1,2,3,这里,a,b,c是形参(形式参数)——函数完成其工作所需的一项信息;调用时的1,2,3则为实参(实际参数)——调用函数时传递给函数的信息。
**在调用函数时,我们输入的实参将传递给形参,值被存储在形参中。
3. 函数调用
3.1. 函数调用
【语法】
函数名(实际参数)
【调用函数注意】
1、函数调用是一个表达式;
2、函数中如果没有return语句,则无论运行结果如何,都返回None值对象;
3、如果需要返回其他值,需要用return语句返回;
4、函数调用作为模块内部语句(和函数在同一py文件中),必须先定义函数,后调用函数。
3.2. 函数返回
【语法】
return [表达式]
#[]表达式可选,可直接返回某对象
【return语句作用】
结束当前函数的执行,返回到调用该函数的地方,同时返回一个值对象的引用关系。
【return语句注意】
1、return语句后跟的表达式可以省略,省略后相当于return None;
2、如果函数内没有return语句,则函数执行完最后一条语句后返回None(相当于最后自动加了一条 return None)
3、函数的调用可以返回一个值或一组值。
#Example_4
#输出一个值
def difer(a,b):
return a-b
#Example_5
#输出一组值
def max_min(a,b,c):
if a>=b:
max=a
min=b
else:
max=b
min=a
if c>max:
max=c
elif c<min:
min=c
return (max,min)
4. 传递实参
仍然以Eaxmple_2为例,printEach
函数中有3个参数,当然也存在其他函数有着多个参数,那么向这些函数传递实参的方式就有很多种了。
包括了:位置实参、关键字实参、序列传参、字典关键字传参、综合传参等。
4.1. 位置传参
相对简单的实参传递方式——要求实参顺序和形参顺序相同。
还是以Example_2为例,在调用函数中依次输入1,2,3则依次对应了a,b,c,那么1就传递到了a,2传递到了b…
在使用位置传参时,每个实际参数的位置就非常重要了,一旦位置错误/颠倒,就会导致参数传递的错误。
4.2. 关键字传参
除了通过位置顺序传递参数,还可以通过参数名称-值对来传递,这也就是关键字实参。
#Example_6
def printWithKey(a,b,c):
print("a",a)
print("b",b)
print("c",c)
printWithKey(b=3, a=2, c=4)
在Example_4中,printWithKey(b=3, a=2, c=4)
在给出实参时明确了每个形参对应的值,这样可以在任意顺序进行传递。
4.3. *序列传参
序列类型(list, tuple, str)作为参数列表传递,其要求是:序列的元素个数必须与参数个数相同。
#Example_7
#序列传参
def listTrans(a,b,c):
print("a-->", a)
print("b-->", b)
print("c-->", c)
#列表
s1=[11, 22, 33]
listTrans(*s1)
#等同于listTrans(s1[0], s1[1], s1[2])
#元组
s2=(1.1, 2.2 3.3)
listTrans(*s2)
#str字符串
s3="ABC"
listTrans(*s3)
4.4. **字典关键字传参
字典和形参通过字典进行传递和匹配,字典的值传递给键对应的形参
【注意】
1、字典传参的key一定要和形参名一致!
2、键名(Key)必须为字符串
3、键名要在形参中存在
#Example_8
d1={"c":33, "a":11, "b":22}
listTrans(**d1)
#CounterExample_1
d2={"c":33, "a":11, "b":22, "d":100}
listTrans(**d2)
## Error!! d不存在
d3={"c":33, "a":11}
listTrans(**d3)
#Error!! 缺少argument
4.5. 综合传参
以上几种参数的混合使用
#Example_9
listTrans(100, *(200, 300))
5. 缺省参数(默认值)
对于一些函数中具有共性的/多次调用中有着相同值的参数,我们希望能够设定为默认值,在每次调用函数时能够直接赋值(如果没有实参进行赋值的话)。
【语法】
def 函数名(形参1=默认参数值1, 形参2=默认参数2…):
语句
#Example_10
def printDefault(name,type='human'):
print(name+" is a " +type +".")
printDefault('Kim')
#Output
# Kim is a human.
Example_10中,type设置为‘human’,是对每个人名字对应的“人类”这一共性设置的。这样每个人通过调用函数时都省去了输入type这一参数。
当然,type也可以在调用时重新输入代替默认值——正常给出实参值即可。
#Example_11
def printDefault(name,type='human'):
print(name+" is a " +type +".")
printDefault('Kim', 'Dog')
【缺省参数注意】
对于缺省参数/默认值,有着以下需要注意的:
1、缺省参数必须从右至左存在,若一个参数有缺省参数,则右侧参数必须要有缺省参数;
2、缺省参数可以有0个,也可以有多个——甚至所有参数皆为缺省参数都可以。
针对注意点1的反例:
# 反例
#Counterexample_2
def printEach(a,b=2,c):
#错误,当b赋予默认值2后,后续参数必须要有默认值
pass
#Counterexample_3
def printEach_2(a=1,b,c=3):
#当出现第一个赋予默认值的参数后,后续所有参数都需要有默认值
pass
6. 不定长参数
不定长参数有两种:
1、星号元组形参
2、双星号字典形参
6.1. 星号元组形参
【语法】
def 函数名(*元组形参名):
语句块
#Example_12
def star(*args):
print("实参个数是:",len(args))
print(args)
6.2. 命名关键字形参
命名关键字形参类似于星号元组和双星号字典的重睑形态
【语法】
def 函数名(*,命名关键字形参名):
语句块
或
def 函数名 (*args, 命名关键字形参名):
语句块
【注意】
*只起到分隔作用,前面的正常赋值,后面的命名关键字需要通过关键字显式赋值。
如果为args,则args为星号元组,后面的为关键字。
#Example_13
def namedKey(a, b, *, c):
print(a,b,c)
namedKey(1,2,c=3) #Right
namedKey(11,22,33) #Wrong
namedKey(11,c=33,b=22) #Right,只要c通过关键字赋值即可,其他的都可以
6.3. 双星号字典形参
【语法】
def 函数名(**字典形参名):
语句
#Example_14
def dict(**zdargs):
print("参数个数:", len(zdargs))
for k,v in zdargs.items():
print(k, "-->>", v)
#调用
dict(name="Helen", age=20) #Right
dict(1,2,3) #Wrong
dict(name="Dany", 30) #Wrong
7. 函数的参数列表顺序
位置形参、缺省参数、星号元组形参、双星号字典形参、命名关键字参数可以混合使用
函数参数自左至右的顺序为:
位置形参,星号元组形参,命名关键字参数,双星号字典形参,缺省参数
#Example_15
def seq(a, b, *args, c, **zdargs):
pass
seq(100, 200, 300, 400, c="C", d="D", e="E")
#100赋值给a,位置形参
#200赋值给b,位置形参
#300,400赋值给*args,为星号元组
#c="C"赋值给c,为命名关键字参数
#d="D", e="E"为双星号字典参数
若有遗忘,见help(“def”)
8. 可变/不可变类型 的实参的参数传递的区别
【可变类型】
列表list,集合set,字典dict
list虽然在确定后,指向内存地址不变,但是内值可以改变,也是可变的
【不可变类型】
frozenset, tuple, str, numbers
numbers为不可变,是因为一旦一个number(假设为a)的值改变了,原先值的内存空间需要被释放,在申请新的空间存放新的值,并指向新的空间,实际上a位置改变了。
!!可变、不可变并非指内容可变/不可变,而是能否在不改变地址的情况下改变内容。
#Example_16
X=100
def fn(x):
x=200
fn(X)
print(X) #100
【区别】
不可变类型的数据作为函数参数传入时,函数内部不会改变变量的原数据值,是安全的。
可变类型的数据作为参数传入时,函数内部可以改变原数据,多用来返回更多数据结果。