Python——函数设计与使用
文章目录
- 函数定义
- 注意事项:
- 形参与实参
- 验证
- 参数类型
- 必备参数
- 默认值参数
- 位置参数
- 关键字参数
- 可变长度参数
- *args的用法
- **kw的用法
- 参数传递的序列解包
- return语句
- 变量作用域
函数定义
提示:这里可以添加本文要记录的大概内容:
①、函数是最基本的一种代码抽象的方式,将需要反复执行的代码封装为函数,并在需要该功能的地方进行调用,不仅可以实现代码复用,更重要的是可以保证代码的一致性,只需要修改该函数代码则所有调用均受到影响。
②、设计函数时,应注意提高模块的内聚性,同时降低模块之间的隐式耦合。
③、在编写函数时,应尽量减少副作用,尽量不要修改参数本身,不要修改除返回值以外的其他内容。
应充分利用Python函数式编程的特点,让自己定义的函数尽量符合纯函数式编程的要求。
def 函数名([参数列表]):
函数体
[return [表达式]]
注意事项:
函数形参不需要声明其类型,也不需要指定函数返回值类型
即使该函数不需要接收任何参数,也必须保留一对空的圆括号
括号后面的冒号必不可少
函数体相对于def关键字必须保持一定的空格缩进
Python允许嵌套定义函数
return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的
return相当于返回 None。
提示:下面案例可供参考
【例1】长方形面积函数定义和调用
def area(width, height):
'''计算长方形面积'''
return width * height
w = 4
h = 5
print("width =", w, " height =", h, " area =", area(w, h))
【例2】如果想定义一个什么事也不做的空函数,可以用pass语句
>>> def nop():
pass
pass可以用来作为占位符,比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
【例3】生成斐波那契数列的函数定义和调用
def fib(n):
a, b = 1, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
fib(1000)
提示:下面案例可供参考
形参与实参
1、函数定义时括弧内为形参,一个函数可以没有形参,但是括弧必须要有,表示该函数不接受参数。
函数调用时向其传递实参,将实参引用(地址)传递给形参。
def fib(n): #n形参
a, b = 1, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
fib(1000) #1000实参
2、在定义函数时,对参数个数并没有限制,如果有多个形参,需要使用逗号进行分隔。
编写函数,接受两个整数,并输出其中最大数。
def printMax(a, b):
if a>b:
pirnt(a, 'is the max')
else:
print(b, 'is the max')
3、对于绝大多数情况下,在函数内部直接修改形参的值不会影响实参。例如:
>>> def addOne(a):
print(a)
a += 1
print(a)
>>> a = 3
>>> addOne(a)
3
4
>>> a
3
可以看出,函数内部修改了形参a的值,但当函数运行结束后,实参a的值并未被修改
4、在有些情况下,可以通过特殊的方式在函数内部修改实参的值,例如下面的代码。
>>> def modify(v): #修改列表元素值
v[0] = v[0]+1
>>> a = [2,4]
>>> modify(a)
>>> a
[3,4]
>>> def modify(v, item): #为列表增加元素
v.append(item)
>>> a = [2]
>>> modify(a,3)
>>> a
[2, 3]
>>> def modify(d): #修改字典元素值或为字典增加元素
d['age'] = 38
>>> a = {
'name':'jack', 'age':37, 'sex':'Male'}
>>> a
{
'age': 37, 'name': 'jack', 'sex': 'Male'}
>>> modify(a)
>>> a
{
'age': 38, 'name': 'jack', 'sex': 'Male'}
也就是说,如果传递给函数的是可变序列,并且在函数内部使用下标或可变序列自身的方法增加、删除元素或修改元素时,修改后的结果是可以反映到函数之外的,实参也得到相应的修改。
验证
传不可变对象:不会改变外面实参值
def changeInt( a ):
a = 10
b = 2
ChangeInt(b)
print( b ) # 结果是 2
不可变对象:数字类型、元组、字符串
不可变类型:类似 c 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a)内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可变类型:类似 c的指针传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
画图解释
传可变对象实例:会改变外面实参值
def changeList( blist ):
blist.append([4,5,6])
print ("函数内取值: ", blist)
# 结果是 [1,2,3,4,5,6]
alist = [1,2,3]
changeList(alist)
print( alist ) # 结果是 [1,2,3,4,5,6]
可变对象:列表、字典、集合等
当一个引用传递给函数的时候,函数自动复制一份引用。这个函数里的引用和外边的引用没有任何关系了.所以函数把引用指向了一个不可变对象,当函数返回的时候,外面的引用没半毛关系.
而第函数内的引用指向的是可变对象,对它的操作就和定位了指针地址一样,在内存里进行修改.
传不可变对象:函数内部直接修改形参的值不会影响实参
传可变对象:函数内部修改实参的值会影响到实参
形参和实参变量各自有不同的存储单元,当一个引用传递给函数的时候,函数自动复制一份引用
参数类型
在Python中,函数参数有很多种:可以为普通参数、默认值参数、关键参数、可变长度参数等。
Python在定义函数时不需要指定形参的类型,完全由调用者传递的实参类型以及Python解释器的理解和推断来决定。类似于重载和泛型。
Python函数定义时也不需要指定函数的返回值类型,这将由函数中的return语句来决定,如果没有return语句或者return没有得到执行,则认为返回空值None。
必备参数
必需参数须以正确的顺序传入函数。调用时的数量、参数类型、位置必须和声明时的一样。否则会出现语法错误。
def add( x , y ):
return x+y
默认值参数
默认值参数:在定义函数时为形参设置默认值。
默认值参数必须出现在函数参数列表的最右端,任何一个默认值参数右边不能有非默认值参数。
def 函数名(...,形参名=默认值):
函数体
调用带有默认值参数的函数时,可以不对默认值参数进行赋值,也可以通过显式赋值来替换默认值,具有很大的灵活性。
>>> def say( message, times =1 ):
print(message * times)
>>> say('hello')
hello
>>> say('hello',3)
hello hello hello
>>> say('hi',7)
hi hi hi hi hi hi hi
注意:
默认值参数的赋值只在函数定义时被解释一次
>>> i = 3
>>> def f(n=i): #参数n的值仅取决于i的当前值
print(n)
>>> f()
3
>>> i = 5 #函数定义后修改i的值不影响参数n的默认值
>>> f()
3
默认参数必须指向不变对象!
1、可以使用“函数名.defaults”查看所有默认参数的当前值,返回值为元组,其中元素依次表示每个默认值参数的当前值。
>>> say.__defaults__
(1,)
>>> f.__defaults__ #查看函数默认值参数的当前值
(3,)
2、默认值参数必须出现在函数参数列表的最右端,且任何一个默认值参数右边不能有非默认值参数。(为了避免歧义)
>>> def f(a=3,b,c=5):
print(a,b,c)
>>> def f(a=3,b):
print(a,b)
>>> def f(a,b,c=5):
print(a,b,c)
>>> def f(a,b,c=5,d=5):
print(a,b,c,d)
>>> def f(a,b,c=5,d,e=8):
print(a,b,c,d)
3、默认值参数如果使用不当,会导致很难发现的逻辑错误,例如:
def demo(newitem,old_list=[]):
old_list.append(newitem)
return old_list
print(demo(5,[1,2,3,4]))
A [1, 2, 3, 4, 5] B [1, 2, 3, 4] C 报异常
print(demo('aaa',['a','b']))
A ['a', 'b', 'aaa'] B ['a', 'b'] C 报异常
print(demo('a'))
A ['a'] B [] C 报异常
print(demo('b'))
A ['b'] B ['a', 'b'] C 报异常
#正确代码如下:
def demo(newitem,old_list=None):
if old_list is None:
old_list=[]
old_list.append(newitem)
return old_list
print(demo('5',[1,2,3,4]))
print(demo('aaa',['a','b']))
print(demo('a'))
print(demo('b') )
位置参数
位置参数(positional arguments)是比较常用的形式,调用函数时实参和形参的顺序必须严格一致,并且实参和形参的数量必须相同。
>>> def demo(a, b, c):
print(a, b, c)
>>> demo(3, 4, 5) #按位置传递参数
3 4 5
>>> demo(3, 5, 4)
3 5 4
>>> demo(1, 2, 3, 4) #实参与形参数量必须相同
TypeError: demo() takes 3 positional arguments but 4 were given
关键字参数
关键字参数主要指实参,即调用函数时的参数传递方式。
通过关键字参数,按照参数名字传递,实参顺序可以和形参顺序不一致,但不影响传递结果,避免了用户需要牢记位置参数顺序的麻烦。
>>> def demo(a,b,c=5):
print(a,b,c)
>>> demo(3,7)
3 7 5
>>> demo(a=7,b=3,c=6)
7 3 6
>>> demo(c=8,a=9,b=0)
9 0 8
可变长度参数
可变长度参数主要有两种形式:*args
用来接受多个实参并将其放在一个元组中**kw
接受关键字参数并存放到字典中
可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。
而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict。
*args的用法
接收多个参数放元组里
加了星号 * 的参数会以元组(tuple)的形式导入
>>> def demo(*p):
print(p)
>>> demo(1,2,3)
(1, 2, 3)
>>> demo(1,2,3,4,5,6,7)
(1, 2, 3, 4, 5, 6, 7)
**kw的用法
接受关键参数并存放到字典中
两个星号 ** 的参数会以字典的形式导入
>>> def demo(**p):
for item in p.items():
print(item)
>>> demo(x=1,y=2,z=3)
('y', 2)
('x', 1)
('z', 3)
参数传递的序列解包
传递参数时,可以通过在实参序列前加一个星号将其解包,然后传递给多个单变量形参。
>>> def demo(a, b, c):
print(a+b+c)
>>> seq = [1, 2, 3]
>>> demo(*seq)
6
>>> tup = (1, 2, 3)
>>> demo(*tup)
6
注意:调用函数时如果对实参使用一个星号*进行序列解包,这么这些解包后的实参将会被当做普通位置参数对待,并且会在关键参数和使用两个星号**进行序列解包的参数之前进行处理。
return语句
return语句用来从一个函数中返回一个值,同时结束函数。
如果函数没有return语句,或者有return语句但是没有执行到,或者只有return而没有返回值,Python将认为该函数以return None结束。
def maximum( x, y ):
if x>y:
return x
else:
return y
在调用函数或对象方法时,一定要注意有没有返回值,这决定了该函数或方法的用法
>>> a_list = [1, 2, 3, 4, 9, 5, 7]
>>> print(sorted(a_list))
[1, 2, 3, 4, 5, 7, 9]
>>> print(a_list)
[1, 2, 3, 4, 9, 5, 7]
>>> print(a_list.sort())
None
>>> print(a_list)
[1, 2, 3, 4, 5, 7, 9]
变量作用域
变量起作用的代码范围称为变量的作用域,不同作用域内变量名可以相同,互不影响。
一个变量在函数外部定义和在函数内部定义,其作用域是不同的。
在函数内部定义的普通变量只在函数内部起作用,称为局部变量。当函数执行结束后,局部变量自动删除,不再可以使用。
局部变量的引用比全局变量速度快,应优先考虑使用。
形参属于局部变量
如果想要在函数内部给一个定义在函数外的变量赋值,那么这个变量就不能是局部的,其作用域必须为全局的,能够同时作用于函数内外,称为全局变量,可以通过global来定义。这分为两种情况:
一个变量已在函数外定义,如果在函数内需要为这个变量赋值,并要将这个赋值结果反映到函数外,可以在函数内用global声明这个变量,将其声明为全局变量。
在函数内部直接将一个变量声明为全局变量,在函数外没有声明,该函数执行后,将增加为新的全局变量。
>>> def demo():
global x
x = 3
y = 4
print(x,y)
>>> x = 5
>>> demo()
3 4
>>> x
3
>>> y
NameError: name 'y' is not defined
>>> del x
>>> x
NameError: name 'x' is not defined
>>> demo()
3 4
>>> x
3
>>> y
NameError: name 'y' is not defined
注意:在某个作用域内只要有为变量赋值的操作,该变量在这个作用域内就是局部变量,除非使用global进行了声明。
>>> x = 3
>>> def f():
print(x) #本意是先输出全局变量x的值,但是不允许这样做
x = 5 #有赋值操作,因此在整个作用域内x都是局部变量
print(x)
>>> f()
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
f()
File "<pyshell#9>", line 2, in f
print(x)
UnboundLocalError: local variable 'x' referenced before assignment
如果局部变量与全局变量具有相同的名字,那么该局部变量会在自己的作用域内隐藏同名的全局变量。
>>> def demo():
x = 3 #创建了局部变量,并自动隐藏了同名的全局变量
>>> x = 5
>>> x
5
>>> demo()
>>> x #函数执行不影响外面全局变量的值
5
还没有评论,来说两句吧...