Python函数与参数

Python函数与参数

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。调用函数会执行其内部包含的语句,函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()、input()。Python用户也可以根据自己需求创建函数,称为用户自定义函数。

5.1 函数

在实际项目开发中,常会把需要重复使用的功能定义成为一个个的函数,这样可以减少代码的重复开发,提高工作效率。

5.1.1 函数的定义

def是用于自定义函数的关键字,语法如下:

def 函数名(参数1, 参数2...):
	函数语句
    return 返回值

说明:

  • def – 自定义函数的关键字;
  • 函数名-- 自定义函数的名称,命名规则同变量命名规则,不能使用关键字做函数名;
  • 圆括号()-- 函数必须有圆括号;
  • 参数 – 形式参数,可以省略,多个参数之间用逗号间隔;
  • 冒号: – 表示自定函数的开始
  • 函数语句 – 函数代码块。
  • return – 返回语句,可以省略。

示例如下:

# 自定义函数
>>> def add_num(a, b): # 有参数
... 	c = a + b
... 	return c # 有返回值

5.1.2 函数的调用

调用函数语法如下:

函数名(参数1, 参数2...)

通过函数名称调用函数,函数名后必须加圆括号,将实际参与运算的参数放入括号中,示例如下:

# 自定义函数
>>> def add_num(a, b): # 有参数
... 	c = a + b
... 	return c # 有返回值

# 调用函数
print(add_num(2, 3))
5

5.2 函数参数

5.2.1 参数的定义和分类

函数参数根据出现的位置不同可以分为形式参数和实际参数。定义函数过程中出现的参数为形式参数,形式参数起到占位和传递的作用。调用函数时传递到函数中实际参与运算的具体的值为实际参数。实际参数可以是变量、常量或表达式。Python中的变量保存的是对对象的引用,实参为变量时,参数传递会将实参对对象的引用赋值给形参。

示例如下:

# 自定义函数
>>> def add_num(a, b): # a, b为形式参数
... 	c = a + b
... 	return c # 有返回值

# 调用函数
print(add_num(2, 3)) # 调用函数时实际参与运算的是2和3为实际参数
5
形式参数分类

形式参数根据有无默认值可以分为位置形参,默认形参(缺省形参),序列形参,字典关键字形参四种:

  1. 位置形参

    根据参数的先后位置接收参数,位置形参必须放在其他形参的左边,如果有实参传入首先传递给位置形参,不然会引起错误。

  2. 默认形参也叫缺省形参

    在定义函数时给某些形式参数传入默认值,在调用函数时如果有实际参数传入就使用传入的值,如果没有实参传入就使用形式参数的默认值参与运算,这样也不会引发报错。默认形参必须放在位置形参的右边,如果放在位置形参的左边会引发错误。示例如下:

# 定义函数
>>> def func(a, b, c=0): # 位置形参必须放在默认形参的左边,否则会引发错误
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)

# 调用函数,仅传入两个实际参数
func(1, 2) # 仅为a,b传入实参,函数也是可以正常运行
a: 1
b: 2
c: 0

# 调用函数,传入三个实际参数
func(1, 2, 3) # 传入三个实参,形参c会优先使用传入的值
a: 1
b: 2
c: 3
  1. 序列形参

    定义函数时,在形式参数前面使用星号(*),可以将传入的多个参数打包成一个元组,带星号的形参称为序列形参,也可以叫作星号元组形参,示例如下:

# 定义函数
>>> def func(a, b, *c): # c为序列形参
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)

# 调用函数
func(1,2,3,4,5,6,7,8,9) # 位置形参a,b接收完后剩余的多个实参均由*c接收打包成元组
a: 1
b: 2
c: (3, 4, 5, 6, 7, 8, 9)

注意:序列形参必须放在位置形参的右边,传入的实参需要优先满足位置形参,剩余的部分才由序列形参打包为元组,如果序列形参放在位置形参的左边,则会引起错误,示例如下:

# 定义函数
>>> def func(a, *args, c): # args为序列形参
... 	print('a:', a)
...     print('args:', args)
...     print('c:', c)

# 调用函数
func(1,2,3,4,5,6,7,8,9) # 位置形参a接收完后剩余的多个实参均由*args接收打包成元组,c没有值接收,因此引发错误
Traceback (most recent call last):
  File "test.py", line 9, in <module>
    func(1, 2, 3, 4, 5, 6, 7, 8, 9)
TypeError: func() missing 1 required keyword-only argument: 'c'

注意:通常序列形参使用*args表示。

  1. 字典关键字形参

除了使用序列形参外,定义函数时还可以使用两个星号(**)将传入的多个关键字实参按照键值对映射的关系打包成字典的形式,称作字典关键字形参或者双星号字字典关键字形参,示例如下:

# 定义函数
>>> def func(a, b, **kwargs): # kwargs为字典关键字形参
... 	print('a:', a)
...     print('b:', b)
...     print('kwargs:', kwargs)

# 调用函数
func(1, 2, c=3, d=4, e=5) # 位置形参a, b接收完后剩余的多个关键字实参均由**kwargs接收打包成字典
a: 1
b: 2
kwargs: {'c': 3, 'd': 4, 'e': 5}
实际参数分类

实际参数也分为四种类型:位置实参、关键字实参、序列实参、字典关键字实参。

  1. 位置实参:根据先后顺序传递参数;
  2. 关键字实参:根据赋值号将值传递给指定的形式参数;
  3. 序列实参:使用字符串、列表、元组等传入实际参数;
  4. 字典关键字实参:使用字典键值对映射的关系将值传递给对应的形参

5.2.2 参数传递方式

调用函数时需要将实际参数传递给函数中的形式参数,传递方式分为一下四中:位置传递、关键字传递、序列传递、字典传递。

  1. 位置传递

函数调用时按照参数的先后顺序将实参传递给形参,示例如下:

# 定义函数
>>> def func(a, b, c):
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)
    
# 调用函数
func(1, 2, 3)
a: 1
b: 2
c: 3

调用函数时会按照先后顺序将实际参数传递给a, b, c。

  1. 关键字传递

Pythono允许为形式参数赋值的方式传递参数,示例如下:

# 定义函数
>>> def func(a, b, c):
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)
    
# 调用函数
func(a=1, c=2, b=3)
a: 1
b: 3
c: 2

通过赋值号直接为形式参数传递,这种传递方式不用考虑位置先后顺序。

  1. 序列传递

需要传递多个参数时,可以把多个参数放在序列中传递,示例如下:

# 定义函数
>>> def func(a, b, c):
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)

# 调用函数
s1 = [1, 2, 3]
func(*s1) # 将列表s1作为实际参数传入
a: 1
b: 2
c: 3

传入的实际参数是一个序列(列表、元组、字符串)时,需要在前面加星号(*),如果不加星号,函数会把序列当作一个整体传入形式参数赋值给第一个形参,其他形式参数由于没有实际参数传入会引发函数语法正常运行而报错,示例如下:

# 定义函数
>>> def func(a, b, c):
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)

# 调用函数
s1 = [1, 2, 3]
func(s1) # 星号(*)起到拆分序列的作用,此处没有星号会导致错误

Traceback (most recent call last):
  File "test.py", line 10, in <module>
    func(s1)
TypeError: func() missing 2 required positional arguments: 'b' and 'c'

在调用函数时星号(*)起到拆解序列的作用,将序列中的元素按照位置传递给形式参数,需要注意的是序列中的元素个数需要与形式参数的个数相等,否则会提示错误。

  1. 字典关键字传参

需要传递多个参数时,还可以把多个参数放入字典中,按照键值对的映射关系为形式参数传递数据,示例如下:

# 定义函数
>>> def func(a, b, c):
... 	print('a:', a)
...     print('b:', b)
...     print('c:', c)

# 调用函数
d1 = {'a': 1, 'c': 2, 'b': 3}
func(**d1) # 使用双星号将字典中的键值对拆解后根据关键字传递
a: 1
b: 3
c: 2

注意:如果此处只使用一个星号,仅会把字典的键拆解出来根据位置传递给形式参数,双星号可以将字典的键值对中的键和值拆解出来,以关键字传递的方式传递。如果缺失双星号会将字典整体传递给第一个形式参数,其他形式参数由于没有接收到实际参数而引发错误。

5.3 函数的返回值

函数可以仅实现某些功能,没有任何返回值,也可以将运行完后产生的一个或者多个值返回。如果需要保存或者调用函数的返回值,需要用到return语句。

return语句

return语句有两个作用:第一:可以退出函数,第二:将返回值返回到调用函数的位置。语法如下:

return 表达式1, 表达式2...

示例如下:

# 自定义函数
def add_num(a, b):
	return a + b # 退出函数,将计算结果返回到调用函数的位置
	
# 调用函数
print(add_num(2, 3))
5

如果return语句后面没有任何表达式时默认返回None,示例如下:

# 定义函数
>>> def test(a):
...	   print('-----')
...    if a == 10:
...         return # 退出函数,返回None
    	#return下方的代码就不会在执行了
...     print('************')
    
# 调用函数,输入10
>>> print(test(10)) # 使用print()函数打印返回值
-----
None

# 调用函数,输入20,不满足if条件,不执行return
>>> test(20)
-----
************

5.4 嵌套函数

5.4.1 嵌套函数的定义

在函数内部定义函数称为内部函数,Python支持函数嵌套,可以在函数内部定义函数,而且现有的作用域和变量生存周期依旧不变。语法如下:

def func1():
    statements
	def func2(): # 定义func2
		statements
		return 
	func2() # 调用func2
    return

5.4.2 嵌套函数的使用

内部函数只能在函数内部被调用,示例如下:

>>> def outer():
...     name="python"
...     def inner():# outer函数内部定义的函数
...         print(name)
...     return inner()#返回该内部函数

>>> outer()
python

嵌套函数还涉及到变量和作用域的知识,在本章中的变量和作用域中也会涉及到嵌套函数。

5.4.3 递归函数

递归函数是指在函数体内部调用函数本身,常用的递归函数如计算阶乘,示例如下:

# 自定义函数计算阶乘
def frac(n):
    if n == 1:
        return 1
    else:
        return n * frac(n-1) # 在内部调用本身

# 调用函数
print(frac(5))

5.5 匿名函数

使用关键字def自定义函数时一般函数体的逻辑都比较复杂,如果该函数仅为了实现简单的功能时这就过于复杂。为了自由、方便的使用自定义函数,Python提供了匿名函数,这样仅需要一个表达式就可以满足需求。

5.5.1 lambda函数的定义

Python 提供了 lambda 来创建匿名函数,不再使用 def 语句这样标准的形式定义一个函数。lambda只是一个表达式,函数体比def定义的函数体简单,仅仅在lambda表达式中封装有限的逻辑代码,函数体只能有一条语句,且不支持赋值语句。lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。lambda匿名函数语法简洁,优雅,可以随时创建和销毁,减少程序耦合度。语法如下:

lambda [arg1 [,arg2,.....argn]]:expression

参数:

  • lambda – lambda表达式的关键字;
  • arg1…argn – 形式参数;
  • expression – 函数功能代码。

其中形式参数和返回值都是可选项,根据参数和返回值的有无情况,可以分为有参数有返回值

有参数无返回值、无参数有返回值、无参数无返回值这四种情况。

5.5.2 有参数有返回值

对于功能简单的函数,可以使用def定义,也可以使用lambda表达式来定义,示例如下:

# 使用def定义函数,有参数有返回值
>>> def func01(a, b):
... 	return a + b
	
# 调用函数
>>> print(func01(2, 3))
5

# 使用lambda表达式定义
>>> func01 = lambda a, b: a + b
>>> print(func01(2, 3))
5

5.5.3 有参数无返回值

有参数传入,但是函数仅为了实现某个简单的功能,没有返回值时,示例如下:

# 使用def定义函数,有参数无返回值
>>> def func02(a, b):
... 	print(a + b) # 实现求和并打印的功能,无返回值

# 调用函数
>>> func02(1, 2)
3

# 使用lambda表达式定义
>>> func02 = lambda a, b: print(a + b)
>>> func02(1, 2)
3

5.5.4 无参数有返回值

无参数传入,但是进行运算后有返回值,示例如下:

# 使用def定义函数,无参数无返回值
def func02():
	return 1000

# 调用函数
print(func02())
1000

# 使用lambda表达式定义
>>> func02 = lambda: 1000
>>> print(func02())
1000

5.5.5 无参数无返回值

仅为了实现某个功能,无须参数参与运算,也没有任何返回值,示例如下:

# 使用def定义函数,无参数无返回值
>>> def func03():
... 	print("Hello Python")

# 调用函数
>>> func03()
Hello Python

# 使用lambda表达式定义
>>> func03 = lambda : print("Hello Python")
>>> func03()
Hello Python

5.6 变量和作用域

变量可使用的范围称为变量的作用域,也称为变量的命名空间。创建变量的位置决定了变量的作用范围。

5.6.1 作用域分类

Python作用域可分为四种:本地作用域、函数嵌套作用域、全局作用域、内置作用域,如图5-1所示:

在这里插入图片描述
图5-1 变量的作用域

  • 本地作用域:没有内部函数时,函数体为本地作用域,函数的参数和函数内通过赋值创建的变量都属于本地作用域。
  • 函数嵌套作用域:函数内部嵌套函数时,相对于内部函数来说,外层函数的函数体为函数嵌套作用域。
  • 模块作用域:也称为文件(.py文件)作用域,即一个.py文件或者一个模块的作用范围。
  • 内置作用域:Python运行时的环境为内置作用域,包含了Python的各项预定义变量和函数,builtins.py文件。

内置作用域和模块作用域可以统称为全局作用域。

根据变量起作用的范围不同可以将变量分为局部变量和全局变量。在内置作用域和模块作用域中定义的变量和函数属于全局变量,在函数嵌套作用域和本地作用域中定义的变量和函数属于局部变量,局部变量也称作本地变量。

本地变量仅在本地作用域中起作用,全局变量可以在本地作用域内起作用,示例如下:

# .py文件中
# 定义函数
>>> g01 = 100 # 全局变量
>>> def func01():
... 	a = 10 # 本地变量,只在函数func01中起作用
... 	print(a)
...     print(g01) # 可以在局部作用域中读取全局变量


# 调用函数
>>> func01()
10
100
>>> print(a) # 超出函数func01就无法使用变量a
Traceback (most recent call last):
  File "test.py", line 11, in <module>
    print(a)
NameError: name 'a' is not defined

作用域外的变量与作用域内的变量名称相同时,会优先使用小范围作用域的变量,外部范围的变量会被屏蔽,称为作用域隔离原则,示例如下:

# .py文件中
>>> g01 = 100
>>> def func02():
... 	g01 = 10 
... 	print(g01) 
    
# 调用函数
>>> func02()
10
>>> print(g01)
100

在本地作用域中为全局变量赋值不成功,函数func01中的g01=10实质为在本地作用域创建了一个与全局变量同名的本地变量g01,全局变量g01的值仍然为100。

5.6.2 global语句

在本地作用域中可以为本地变量赋值,如果需要为全局变量赋值时需使用global语句声明该变量为全局变量,使用global语句声明本地作用域中的g01为全局变量后就可以对其进行重新赋值,示例如下:

# .py文件中
>>> g01 = 100
>>> def func02():
... 	global g01 # 声明g01为全局变量
... 	print('全局变量g01的值为:', g01) # 输出g01的值
... 	g01 = 10 # 为g01重新赋值
... 	print('重新赋值后的g01的值为:', g01) 
    
# 调用函数
>>> func02() 
全局变量g01的值为: 100
重新赋值后的g01的值为: 10

5.6.3 nonlocal语句

在嵌套函数内使用与外层函数同名的变量时也会存在作用域隔离原则的问题,Python会默认为内层函数创建一个同外层函数同名的变量,示例如下:

>>> def func01():
...     a = 100 # 嵌套作用域中的变量a
...     def func02():
...         a = 1000 # 在内层函数创建一个同外层函数同名的变量a
...         print('嵌套函数内层变量a的值为:', a)
...     func02() # 调用内层函数
...     print('嵌套函数外层变量a的值为:', a)

>>> func01()
嵌套函数内层变量a的值为: 1000
嵌套函数外层变量a的值为: 100

内层函数为变量a赋值时,实质上是在内层函数创建了一个同外层函数同名的变量a,外层函数的变量a的值仍然为100,如果需要在内层函数func02中为外层函数创建的变量a重新赋值,需要是使用nonlocal语句对变量a进行声明,示例如下:

>>> def func01():
...     a = 100 # 嵌套作用域中的变量a
...     def func02():
... 		nonlocal a # 声明变量a为外部嵌套作用域中的变量a
...         a = 1000 # 为嵌套作用域中的变量a重新赋值
...         print('重新赋值后变量a的值为:', a)
...     func02() # 调用内层函数
...     print('外层函数变量a的值为:', a)

>>> func01()
重新赋值后变量a的值为: 1000
外层函数变量a的值为: 1000

5.7 模块

Python 模块(Module)是一个 Python 文件,以 .py 结尾,模块中能定义函数,类和变量,模块里也能包含可执行的代码。模块能够有逻辑地组织代码段,在大型系统中,系统功能分别使用多个模块来实现,或者将常用功能集中在一个或多个模块文件中,然后在顶层的主要模块文件中导入使用。Python提供了大量的内置模块,例如:math,csv,os,random等模块不需要安装可以直接导入调用。

5.7.1 导入模块

模块需要先导入然后才能使用其中的变量或者函数,可以使用from或者import语句进行模块导入,语法如下:

import module1[, module2[,... moduleN]]
import module as 别名
from modname import name1[, name2[, ... nameN]]
from modname import name as 别名
from modname import *

5.7.2 import语句

import语句用于导入整个模块,可以用as为导入的模块指定别名。使用import语句导入模块后,模块中的对象均以"模块名称.函数名"的方式来引用,示例如下:

# 导入math模块
>>> import math
>>> print(math.pi)
3.141592653589793

>>> print(math.fabs(-10))
10.0
>>> print(fabs(-10)) # 直接使用模块中的函数会报错
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    print(fabs(-10))
NameError: name 'fabs' is not defined
# 导入math模块并起别名
>>> import math as m
>>> print(m.pi)
3.141592653589793

>>> print(m.fabs(-10))
10.0

在执行import或者from导入语句时,会执行被导入的模块,模块中的赋值语句会创建变量,def语句会创建函数对象,模块中的全部语句都会被执行,而且只能执行一次。再次执行import语句或from语句时,并不会执行模块代码,只会重新建立已经创建的对象引用。所以无论执行多少次import,一个模块只能被导入一次。

5.7.3 from语句

from语句用于导入模块中指定的对象,导入的对象可以直接使用,不需要使用模块名称作为限定符,示例如下:

>>> from math import pi
>>> pritn(pi)
3.141592653589793

>>> from math import fabs
>>> print(fabs(-10))
10.0

>>> from math import fabs as f
>>> print(f(-10))
10.0

from语句还可以使用星号构成from…import *语句,可导入模块顶层的全局变量和函数,示例如下:

>>> from math import *
>>> print(pi)
3.141592653589793
>>> print(fabs(-10))
10.0

在使用import导入模块时,需要使用"模块名.变量名"作为限定词,这样如果同时导入的模块中有同名函数存在时也不会产生歧义。使用from时如果导入的模块中有同名变量存在,使用时需要特别注意。

5.7.4 模块包

在大型系统中,通常会根据代码功能将模块文件放在多个目录中,Python将存放模块文件的目录称为包。包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。在导入这种位于目录中的模块文件时,需要指定目录路径。

包的基本结构如下:

在这里插入图片描述

简单来说,包就是文件夹,但该文件夹下必须存在 __ init __ .py 文件, 该文件的内容可以为空。__ init __.py 用于标识当前文件夹是一个包。

导入包模块时,需要指明包的路径,在路径中使用点号分隔目录,示例如下:

>>> import pytemp.db.test # 导入包中的模块
>>> pytemp.db.test.show() # 调用用模块中的函数
这是db下的test.py中的show()

>>> from pytemp.file.test import show # 从包中导入模块中的函数
>>> show() # 调用函数
这是file下的test.py中的show()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值