Python教程-函数基础

函数基础
在Python中,采用下面的语法定义函数:
def 函数名(参数):
# 内部代码
return 表达式
例如:
def summer(lis):
"""
这里是函数的说明文档,doc的位置
:param lis: 参数列表的说明
:return: 返回值的说明
"""
total = 0
for i in lis:
total += i
return total
在编写函数时,需要注意以下几点:
- 使用关键字
def
开头定义函数,紧接着是一个空格,然后是函数的标识符名称和括号()
,最后加上冒号:
。 - 如果有参数传入,参数需要放置在括号中间。
- 可选择在函数的第一行使用文档字符串来说明函数的用途。
- 函数的代码块以冒号起始,并需要进行缩进。
- 使用
return
语句来结束函数的执行,并返回相应的结果。如果没有指定返回值,则默认返回None
。 return
语句必须在函数体内部使用,不能回退缩进。直到函数的所有代码均编写完毕后,才回退缩进,表示函数体的结束。
如何调用函数?
函数的存在是为了被调用的。要调用一个函数,需要使用函数名后面跟上括号()
的方式进行调用。同时,根据函数的定义,提供相应数量和类型的参数,每个参数之间使用逗号进行分隔。Python作为一种动态语言,语法和词法分析检查的过程中并不会对参数类型进行严格检查。然而,在执行过程中,如果参数的类型不符合函数内部的运行机制,会导致相应的错误出现。例如:
>>> all(0, -1, 3)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
all(0, -1, 3)
TypeError: all() takes exactly one argument (3 given)
>>> all([0, -1, 3])
False
Python内置函数all()
要求提供一个参数,但是我们一开始给了3个参数。不过,我们可以将这三个参数作为一个整体列表提供,这样就没有问题了。
return语句:
return
语句用于表示函数的执行到此结束,并返回return
后面的对象。有时候,函数不需要返回任何值,此时可以省略return
语句。在这种情况下,函数会默认返回None
,并且不会给出任何提示。但在大多数情况下,我们仍然需要使用return
语句来返回一些值。一旦函数执行过程中遇到return
语句,后面函数体内的所有代码都会被忽略,直接跳出函数体的执行,即使当前处于循环中也会立即停止执行。
def func():
pass
return
# 此时,后面的代码其实是永远无法执行的。
# 但从语法和词法层面,这些没有错误。
print(1)
abs(-1)
pass
return可以返回什么?
- 什么都不返回,仅仅return:
return
- 数字/字符串/任意数据类型:
return 'hello'
- 一个表达式:
return 1+2
- 一个判断语句:
return 100 > 99
- 一个变量:
return a
- 一个函数调用:
return func()
- 甚至是返回自己!:
return self
- 多个返回值,以逗号分隔:
return a, 1+2, "hello"
简而言之,函数可以return几乎任意Python对象。
如何接收函数的返回值?
我们在调用函数的时候,可以将函数的返回值保存在变量中。
def func():
pass
return "something"
result = func()
而对于同时返回多个值的函数,需要相应个数的变量来接收,变量之间以逗号分隔:
def func():
return 1, [2, 3], "haha"
a, b, c = func()
参数的传递
函数通常会接受参数,用于将外部的实际数据传递到函数内部进行处理。然而,在处理不同数据类型的参数时,会出现不同的情况。这是由以下两个原因引起的:
- 在Python中,函数参数传递的是实际对象的内存地址。
- Python的数据类型可以分为可变数据类型和不可变数据类型。
看下面的例子
a = 1
def func(a):
print("在函数内部修改之前,变量a的内存地址为: %s" % id(a))
a = 2
print("在函数内部修改之后,变量a的内存地址为: %s" % id(a))
print("函数内部的a为: %s" % a)
print("调用函数之前,变量a的内存地址为: %s" % id(a))
func(a)
print("函数外部的a为:%s" % a)
打印结果为:
调用函数之前,变量a的内存地址为: 1401140288
在函数内部修改之前,变量a的内存地址为: 1401140288
在函数内部修改之后,变量a的内存地址为: 1401140320
函数内部的a为: 2
函数外部的a为:1
为什么当a = 2之后,函数内外的a的内存地址就不一样了呢?也就是说此后函数内外的a是两个不同的对象了。
这是因为当参数a作为实参传递给函数时,实际上是将数字对象1的内存地址传递给了函数内部的形参a。在执行第一句代码时,函数内部的a和外部的a实际上是引用了同一个对象,因此打印出了相同的内存地址。然而,当执行a = 2时,创建了一个新的内部变量a,并将数字对象2的内存地址赋给了这个变量a。需要注意的是,赋值语句在Python中具有创建新变量的功能。此外,函数具有作用域的概念,函数内部的变量在作用域内独立存在,不受外部变量的影响,相当于一个新的命名空间。因此,此时的内部a和外部a是两个不同的变量。同时,由于数字1和2是不可变的数字类型对象,它们是两个独立的、具有不同内存地址的对象,所以再次打印内存地址时会不同。
以上的解释可能不太容易理解。实际上,很多时候,我们对这类问题感到困惑是因为函数参数的命名不够恰当。如果我们将上述参数名改为b,可能就更容易理解了(注意文字的变化)。执行结果是相同的。
a = 1
def func(b):
print("在函数内部修改之前,变量b的内存地址为: %s" % id(b))
b = 2
print("在函数内部修改之后,变量b的内存地址为: %s" % id(b))
print("函数内部的b为: %s" % b)
print("调用函数之前,变量a的内存地址为: %s" % id(a))
func(a)
print("函数外部的a为:%s" % a)
刚才说的是不可变类型参数,如果是可变类型的,比如列表呢?
a = [1, 2, 3]
def func(b):
print("在函数内部修改之前,变量b的内存地址为: %s" % id(b))
b.append(4)
print("在函数内部修改之后,变量b的内存地址为: %s" % id(b))
print("函数内部的b为: %s" % b)
print("调用函数之前,变量a的内存地址为: %s" % id(a))
func(a)
print("函数外部的a为:%s" % a)
执行结果是:
调用函数之前,变量a的内存地址为: 34875720
在函数内部修改之前,变量b的内存地址为: 34875720
在函数内部修改之后,变量b的内存地址为: 34875720
函数内部的b为: [1, 2, 3, 4]
函数外部的a为:[1, 2, 3, 4]
调用函数时将列表对象a的内存地址传递给了函数内部的变量b。当执行b.append(4)
时,根据传入的内存地址,找到了列表对象[1, 2, 3]
,并在其末尾添加了4。
可以观察到,此时的a和b实际上指向同一个对象。这是为什么呢?关键在于b.append(4)
这行代码,它与赋值语句不同,不会创建新的变量。作为可变类型,列表具有append
方法,这个方法只是对列表的一种操作。因此,a和b仍然引用同一个对象。
那么,如果使用数字类型调用append
方法,会发生什么呢?显然,这是不可行的,因为数字类型没有append
方法。并非是Python官方不为数字类型提供append
方法,而是因为数字类型是不可变类型,永远不会有append
方法。