Python函数是一段组织良好且可重用的代码,用于执行单一且相关的动作。函数为您的应用程序提供了更好的模块化,并且具有高度的代码复用性。
自上而下的方法来构建处理逻辑涉及到定义独立的可重用函数块。一个Python函数可以从任何其他函数中通过传递所需数据(称为参数或实参)来调用来。被调用的函数会将其结果返回给调用环境。
Python函数类型
Python提供了以下几种类型的函数:
序号 |
类型 |
描述 |
1 |
内置函数 |
Python的标准库包含了大量的内置函数。一些Python的内置函数包括print() 、int() 、len() 、sum() 等。这些函数总是可用的,因为一旦启动Python解释器,它们就会被加载到计算机的内存中。 |
2 |
在内置模块中定义的函数 |
标准库还捆绑了一些模块。每个模块定义了一组函数。这些函数不是直接可用的。你需要从各自的模块导入它们到内存中。 |
3 |
用户定义的函数 |
除了内置函数和内置模块中的函数外,您还可以创建自己的函数。这些函数被称为用户定义的函数。 |
定义Python函数
您可以定义自定义函数以提供所需的特性。以下是定义Python函数的一些简单规则:
-
函数块以关键字
def
开始,后面跟着函数名和括号()
。
-
任何输入参数或实参都应该放置在这些括号内。您也可以在这些括号内定义参数。
-
函数的第一行可以是一个可选的语句;即函数的文档字符串或docstring。
-
每个函数内的代码块以冒号(
:
)开始,并且是缩进的。
-
return [expression]
语句退出一个函数,可选地向调用者返回一个表达式。没有参数的return
语句等同于return None
。
定义Python函数的语法
def function_name(parameters):
"function_docstring"
function_suite
return [expression]
默认情况下,参数具有位置行为,您需要按照它们被定义的顺序提供它们。
一旦函数被定义了,您可以通过从另一个函数中调用它或者直接从Python提示符调用来执行它。
定义Python函数的例子
下面的例子展示了如何定义一个名为greetings
的函数。括号是空的,因此没有参数。这里,第一行是一个docstring,函数块以return
语句结束。
def greetings():
"这是greetings函数的docstring"
print("Hello World")
return
当这个函数被调用时,会打印出“Hello World”消息。
调用Python函数
定义一个函数只是给它一个名字,指定了函数要包含的参数,并结构化了代码块。一旦函数的基本结构确定了,您就可以通过使用函数名本身来调用它。如果函数需要任何参数,它们应该在括号内传递。如果函数不需要任何参数,则括号应保持为空。
Python函数调用示例
下面是如何调用printme()
函数的例子:
def printme(str):
"这将在函数中打印一个传递的字符串"
print(str)
return;
printme("我是第一次调用用户定义的函数!")
printme("再次第二次调用相同的函数")
当上述代码被执行时,会产生如下的输出:
我是第一次调用用户定义的函数!
再次第二次调用相同的函数
引用传递与值传递
在像C和C++这样的编程语言中,有两种主要的方式将变量传递给函数,即值传递和引用传递(也称为按引用传递和按值传递)。然而,在Python中我们传递变量到函数的方式与其他语言不同。
值传递
当在一个函数调用中传递一个变量时,实际参数的值被复制到形式参数代表的变量中。因此,对形式参数所做的任何更改都不会反映在实际参数中。这种传递变量的方式被称为值传递。
引用传递
在这种方式下,传递的是内存中对象的一个引用。形式参数和实际参数(调用代码中的变量)都指向内存中的同一个对象。因此,对形式参数所做的更改确实会在实际参数中反映出来。
Python使用的是引用传递机制。由于Python中的变量是对内存中对象的标签或引用,因此作为实际参数以及形式参数使用的变量实际上都指向内存中的同一个对象。我们可以通过检查传递变量的id()
来验证这一事实。
示例
在下面的例子中,我们正在检查变量的id()
。
def testfunction(arg):
print("在函数内部的ID:", id(arg))
var = "Hello"
print("传递前的ID:", id(var))
testfunction(var)
如果上述代码被执行,那么传递前和在函数内部的id()
将会显示出来。
传递前的ID: 1996838294128
在函数内部的ID: 1996838294128
行为还取决于传递的对象是否可变。Python中的数字对象是不可变的。当一个数字对象被传递,并且函数改变了形式参数的值时,它实际上是在内存中创建了一个新的对象,而不改变原来的变量。
示例
下面的例子展示了当一个不可变对象被传递给一个函数时的行为。
def testfunction(arg):
print("在函数内部的ID:", id(arg))
arg = arg + 1
print("增量后的新对象", arg, id(arg))
var = 10
print("传递前的ID:", id(var))
testfunction(var)
print("函数调用后的值", var)
它将产生如下的输出:
传递前的ID: 140719550297160
在函数内部的ID: 140719550297160
增量后的新对象 11 140719550297192
函数调用后的值 10
现在让我们传递一个可变对象(例如列表或字典)到一个函数。它同样是以引用的方式传递的,因为在传递前后列表的id()
相同。但是,如果我们修改函数内部的列表,它的全局表示也会反映出这种变化。
示例
这里我们传递一个列表,在列表中追加一个新的项目,并查看原始列表对象的内容,我们会发现它已经发生了变化。
def testfunction(arg):
print("在函数内部:", arg)
print("在函数内部的ID:", id(arg))
arg.append(100)
var = [10, 20, 30, 40]
print("传递前的ID:", id(var))
testfunction(var)
print("函数调用后的列表", var)
它将产生如下的输出:
传递前的ID: 2716006372544
在函数内部: [10, 20, 30, 40]
在函数内部的ID: 2716006372544
函数调用后的列表 [10, 20, 30, 40, 100]
Python函数参数
函数参数是在函数被调用时传入的值或变量。函数的行为通常依赖于传入的参数。
在定义函数时,你在括号内指定了一组变量(称为形式参数)。这些参数作为数据占位符,这些数据将在函数被调用时传递给函数。当函数被调用时,必须为每个形式参数提供实际的值。这些被称为实际参数。
示例
让我们修改greetings函数,并添加一个参数name。作为实际参数传递给函数的字符串成为函数内部的name变量。
def greetings(name):
"这是greetings函数的docstring"
print("Hello {}".format(name))
return
greetings("Samay")
greetings("Pratima")
greetings("Steven")
此代码将产生如下的输出:
Hello Samay
Hello Pratima
Hello Steven
Python函数参数的类型
根据在定义Python函数时参数是如何声明的,它们被分类为以下几类:
位置参数或必需参数
必需参数是在正确的顺序下传递给函数的参数。在这里,函数调用中的参数数量必须与函数定义完全匹配,否则代码会给出语法错误。
示例
在下面的代码中,我们在没有任何参数的情况下调用函数printme()
,这将导致错误。
def printme(str):
"这将在函数中打印一个传递的字符串"
print(str)
return;
printme()
当上述代码被执行时,会产生如下的结果:
Traceback (most recent call last):
File "test.py", line 11, in <module>
printme();
TypeError: printme() takes exactly 1 argument (0 given)
关键字参数
关键字参数与函数调用相关。当你在函数调用中使用关键字参数时,调用者通过参数名识别参数。这允许你跳过某些参数或将它们放在不同的顺序中,因为Python解释器能够使用提供的关键字将值与参数相匹配。
示例1
下面的例子展示了如何在Python中使用关键字参数。
def printme(str):
"这将在函数中打印一个传递的字符串"
print(str)
return;
printme(str = "我的字符串")
当上述代码被执行时,会产生如下的结果:
我的字符串
示例2
下面的例子给出了更清晰的画面。注意参数的顺序并不重要。
def printinfo(name, age):
"这将在函数中打印传递的信息"
print("姓名: ", name)
print("年龄 ", age)
return;
printinfo(age=50, name="miki")
当上述代码被执行时,会产生如下的结果:
姓名: miki
年龄 50
默认参数
默认参数是指在函数调用时如果没有为某个参数提供值,则该参数将采用默认值。
示例
下面的例子展示了默认参数的概念,它在没有传递年龄时打印默认年龄。
def printinfo(name, age=35):
"这将在函数中打印传递的信息"
print("姓名: ", name)
print("年龄 ", age)
return;
printinfo(age=50, name="miki")
printinfo(name="miki")
当上述代码被执行时,会产生如下的结果:
姓名: miki
年龄 50
姓名: miki
年龄 35
仅位置参数
那些只能通过其在函数调用中的位置来指定的参数被称为仅位置参数。它们通过在所有仅位置参数之后放置一个“/”在函数的参数列表中来定义。此功能是在Python 3.8版本发布时引入的。
使用这种类型的参数的好处是它确保了函数按照正确的顺序使用正确的参数被调用。仅位置参数应该作为位置参数而不是关键字参数传递给函数。
示例
在下面的例子中,我们定义了两个仅位置参数,分别是"x"和"y"。这个方法应该按照参数声明的顺序以位置参数的形式被调用,否则我们将得到一个错误。
def posFun(x, y, /, z):
print(x + y + z)
print("评估仅位置参数: ")
posFun(33, 22, z=11)
它将产生如下的输出:
评估仅位置参数:
66
仅关键字参数
那些在调用函数时必须通过其名称来指定的参数被称为仅关键字参数。它们通过在函数的参数列表中关键字参数之前放置一个星号("*")来定义。这种类型的参数只能作为关键字参数而不是位置参数传递给函数。
示例
在下面的代码中,我们定义了一个带有三个仅关键字参数的函数。为了调用这个方法,我们需要传递关键字参数,否则我们将遇到一个错误。
def posFun(*, num1, num2, num3):
print(num1 * num2 * num3)
print("评估仅关键字参数: ")
posFun(num1=6, num2=8, num3=5)
它将产生如下的输出:
评估仅关键字参数:
240
可变长参数或任意参数
您可能需要处理比在定义函数时所指定更多的参数。这些参数被称为可变长参数,并且不像必需参数和默认参数那样在函数定义中命名。
带有非关键字可变参数的函数的语法如下:
def functionname([形式参数,] *可变参数元组):
"函数文档字符串"
函数主体
return [表达式]
在持有所有非关键字可变参数值的变量名前放置一个星号(*)。如果在函数调用中没有指定额外的参数,则这个元组将保持为空。
示例
下面是Python可变长参数的一个简单例子。
def printinfo(arg1, *vartuple):
"这将在函数中打印可变传递的参数"
print("输出是: ")
print(arg1)
for var in vartuple:
print(var)
return;
printinfo(10)
printinfo(70, 60, 50)
当上述代码被执行时,会产生如下的结果:
输出是:
10
输出是:
70
60
50
在接下来的章节中,我们将详细讨论这些函数参数。
Python函数参数的顺序
一个函数可以有上述定义的任何类型的参数。然而,参数必须按照以下顺序声明:
参数列表从仅位置参数开始,后面跟着斜杠(/)符号。
接着是常规的位置参数,可以作为关键字参数调用。
然后可能有一个或多个具有默认值的参数。
接下来是一个前面带单个星号的变量表示的任意位置参数,这被视为一个元组。
如果函数有任何仅关键字参数,则在其名称前放一个星号。一些仅关键字参数可能有一个默认值。
最后在括号里是带有双星号**的参数,用来接受任意数量的关键字参数。
下图显示了形式参数的顺序:
返回值的Python函数
在函数定义中的最后一行使用return关键字表明了函数块的结束,并且程序流程返回到调用函数。尽管减少缩进也是隐式返回的一种方式,但显式使用return是一个好习惯。
除了控制流程外,函数还可以返回一个表达式的值给调用函数。返回表达式的值可以存储在一个变量中以便进一步处理。
示例
让我们定义add()函数。它将传递给它的两个值相加并返回它们的和。返回的值存储在一个名为result的变量中。
def add(x, y):
z = x + y
return z
a = 10
b = 20
result = add(a, b)
print("a = {} b = {} a+b = {}".format(a, b, result))
它将产生如下的输出:
a = 10 b = 20 a+b = 30
匿名函数
当函数不是使用def关键字以标准方式声明时,它们被称为匿名函数。相反,它们使用lambda关键字定义。
Lambda形式可以接受任意数量的参数,但只返回一个表达式的值。它们不能包含命令或多个表达式。
一个匿名函数不能直接调用print,因为lambda需要一个表达式。
Lambda函数有自己的局部命名空间,无法访问除了它们的参数列表之外的变量和全局命名空间中的变量。
尽管看起来lambda像是函数的一行版本,但它们并不等同于C或C++中的内联语句,后者的目的在于通过调用期间绕过函数栈分配来提高性能。
语法
Lambda函数的语法只包含单个语句,如下所示:
lambda [arg1 [,arg2,...argn]]: 表达式
示例
下面的例子展示了lambda形式的函数是如何工作的:
sum = lambda arg1, arg2: arg1 + arg2
print("总值: ", sum(10, 20))
print("总值: ", sum(20, 20))
当上述代码被执行时,会产生如下的结果:
总值: 30
总值: 40
变量的作用域
程序中的所有变量可能不是在程序的所有位置都可以访问的。这取决于你在哪里声明了变量。
变量的作用域决定了程序中可以访问特定标识符的部分。Python中有两种基本的变量作用域:
全局变量与局部变量
在函数体内定义的变量具有局部作用域,而在函数体外定义的变量具有全局作用域。
这意味着局部变量只能在声明它们的函数内部访问,而全局变量可以在程序体内的所有函数中访问。当你调用一个函数时,声明在其中的变量进入作用域。
示例
下面是一个简单的局部和全局作用域的例子:
total = 0
def sum(arg1, arg2):
total = arg1 + arg2
print("函数内部的局部total: ", total)
return total;
sum(10, 20)
print("函数外部的全局total: ", total)
当上述代码被执行时,会产生如下的结果:
函数内部的局部total: 30
函数外部的全局total: 0