一、函数的概念
在编程中,我们经常调用相同或者类似的操作,这些相同或者类似的操作是由同一段代码完成的,而函数的出现,可以帮助我们避免重复编写这些代码。函数的作用就是把相对独立的某个功能抽象出来,使之成为一个独立的实体。
例如,我们开发一个支持人与人之间对话的社交网站,“对话”这个功能实现起来比较复杂,我们可以将它封装为一个函数,每次调用函数就可以发起对话。大型网站都有日志功能,所有重复操作都会被记录日志记录,而日志处理则需要由多行Python文件操作的相关代码组成,将这些代码组装为函数,每次写日志调用此函数即可。
Python在全世界被广泛使用的一个原因,就是Python中有大量的内置函数,这些内置函数可以帮助我们快速构建各种场景的网站,下面开始讲解Python函数。
二、函数的定义
定义一个函数只需要以“def”开头即可。
def function_name(arg1,arg2):
function body
dunction value
- 函数名(function_name):和Python中的其他的标识符命名规则相同,有效的函数名以字母或下划线开头,后面可以跟字母、数字、下划线,函数名应该反映函数所执行的任务。
注意:Python的函数名要区分大小写,字母相同但是大小写不同的函数视为两个不同的函数。
- 函数参数(arg1,arg2):调用一个函数时可以传递的参数。参数可以有一个或者多个,也可以没有参数。
- 函数内容(function body):任何有效的代码都可以出现在函数内部。主义函数内容相对于定义函数的“def”行需要缩颈四个空格。
- 函数返回值(return value):函数执行完成后返回的值。也可以不返回任何内容,不返回内容可是视为“None”。
def introduce(name):
print("Hello",name)
introduce("world!")
# 打印输出Hello world!
introduce("零壹快学")
# 打印输出Hello 零壹快学
函数名为"introduce",接受一个参数“name”,没有返回值。一共调用了两次函数,每次都会输出如注释中的文字。
三、函数参数
在创建函数时,可以设置参数,也可以不设置参数。对于设置参数的函数,当调用函数需要时,要向函数传递参数,被传入的参数称为实参,而函数定义时的参数为形参。
Python中的函数参数可以分成以下几种类型:
- 必须参数;
- 关键字参数;
- 默认参数;
- 可变参数;
- 组合参数;
1.1 必须参数
必须参数,顾名思义就是函数定义的参数调用时必须传入,并且在调用的时候数量和顺序必须和定义函数时的参数保持一致。例如:
def add(a,b):
print("a+b=",a+b)
add(1,2)
# a+b=3
上式中,如果我们少传入一个参数:
def add(a,b):
print("a+b=",a+b)
add(1)
会出现如下报错:
TypeError: add() missing 1 required positional argument: 'b'
这一行执行结果告诉我们,调用函数时函数缺少一个必要参数。如果我们多传入一个参数,即不会出现此错误。
1.2 关键字参数
使用关键字参数可以不按函数定义的顺序来传入参数,但是调用时给了两个参数。
通过以上例子我们可以发现,调用函数时必须保证参数的数量与定义函数时的参数数量一致
def hello(name,age):
print("姓名:",name)
print("年龄:",age)
# 按顺序传递参数
hello(name="零壹",age=18)
# 不按顺序传参
hello(age=21,name="一零")
执行结果如下:
姓名:零壹
年龄:18
姓名:一零
年龄:21
但是这里需要注意的是,不能传入没有定义的参数;
def person_name(name):
print("姓名",name)
# age参数定义
person_name(name="小明",age=22)# 错误
1.3 默认参数
在定义函数的时我们可以给参数添加默认值,如果调用函数的时候没有传入参数,函数即会使用默认值,并且不会像必须参数那样报错。
# 设置了默认参数的函数
def default_value(name="小明",age=18):
print("我的名字是:",name)
print("我的年龄是:",age)
default_value() # 我的名字是:小明
# 我的年龄是:18
default_value(name="王五")# 我的名字是:王五
# 我的年龄是:18
default_value(name="李四",age=22)# 我的名字是:李四
# 我的年龄是:22
这里我们可以明显看出添加了默认参数的函数调用在调用时,无论是传入一个参数或者两个参数,亦或是不传参都可以正常打印输出,但是这里需要注意的是,不能传参入没有的参数,否则会报错。
还存在以下使用,无参与默认参数的混合使用(即是:组合参数)
# 设置了无参与默认参数的函数
def default_value(name,age=18):
print("我的名字是:",name)
print("我的年龄是:",age)
default_value(name="小明") # 我的名字是:小明
# 我的年龄是:18
default_value(name="王五")# 我的名字是:王五
# 我的年龄是:18
default_value(name="李四",age=22)# 我的名字是:李四
# 我的年龄是:22
default_value()# 报错
default_value(age=22)# 报错
'''
无参函数必须设置参数,未设置参数会报错。
'''
1.4 可变参数
在某些情况下我们不能定义函数的时候就确定参数的数量和内容,这时候就可以使用可变参数。可变参数和前面介绍的例子有些许不同,可变参数声明时不会命名。基本语法如下:
some_func(*args,**kwargs)
参数说明:
- some_func为函数名。
- “args”和“kwargs”为可变参数。
让我们来看一下“args”会输出什么。
def foo(*args):
print(args)
foo()
foo(1,2)
foo("零壹快学","shanghai","22")
'''
执行结果:
()
(1,2)
("零壹快学","shanghai",22)
'''
从例子中可以看出,"*args"参数获取到的是一个元组,这也正是它能作为可变参数的原因。
让我们再看一下“**kwargs”会输出什么。
def foo(**kwargs):
print(kwargs)
foo()
foo(name="零壹快学")
{}
{name:"零壹快学"}
这里我们可以看出,“**kwargs”参数获取到的是一个字典。所以我们在调用函数时也必须使用关键字参数的方式来传递参数。
在日常使用中,“*args”、“**kwargs”经常出现,用于解决一些未知的情况。
def calculate_sum(*args,**kwargs):
s = 0
for i in args:
s += i
print("输入的数字之和是",s)
for k,v in kwargs.items():
print(k,v)
calculate_sum(1,2,3,4,5,姓名="零壹")
'''
打印输出结果是:
输入的数字之和是15
姓名零壹
'''
正如上面所示,在我们不知道有多少数字需求求和的情况下巧妙使用了可变参数来获取参数中所有数字的和。
不只在函数定义时可以使用“*”和“**”来声明参数,在函数调用时也能使用相同的方式来传递未知的参数。
def exp(*args,**kwargs):
print(args)
print(kwargs)
1 = [1,2,3,4]
d = {"参数1":"arg1","参数2":"arg2"}
exp(*1,**d)
'''
打印输出
(1,2,3,4)
{"参数1":"arg1","参数2":"arg2"}
'''
由此我们可以看到,无论是参数调用还是函数定义参数都能用到以some_func(*args,**kwargs)的形式调用。
1.5 组合参数
组合参数即是:无参与默认参数的混合使用
# 设置了无参与默认参数的函数
def default_value(name,age=18):
print("我的名字是:",name)
print("我的年龄是:",age)
default_value(name="小明") # 我的名字是:小明
# 我的年龄是:18
default_value(name="王五")# 我的名字是:王五
# 我的年龄是:18
default_value(name="李四",age=22)# 我的名字是:李四
# 我的年龄是:22
default_value()# 报错
default_value(age=22)# 报错
'''
无参函数必须设置参数,未设置参数会报错。
'''
四、变量的作用域
变量的作用域其实就相当于变量的命名空间,赋值过的变量并不是在哪里都可以使用的。如何定义变量决定了变量可以在哪里使用。Python中变量赋值的位置决定了哪些范围的对象可以访问这个变量,这个范围就被称为作用域。
Python中有两种最基本的变量作用域:局部变量和全局变量。小编将分别对他们进行介绍
1、局部变量
一般情况下,在函数内赋值的变量,不做特殊声明的都是局部变量。顾名思义,局部变量的作用域是局部的,在当前函数赋值则只能在当前函数使用。
如果在函数体中第一次出现的,就是局部变量,例如:
def foo():
x = "hello"
print(x)
foo()
# hello
可以看到,函数内正确打印出了“x”变量的内容。“x”是在函数体内被赋值的,所以“x”是局部变量。局部变量只能在函数体内被访问,超出函数体的返回就不能正常执行了,例如:
def foo():
x = "hello"
print(x)
foo()
print(x)
'''
执行结果如下:
hello
Namerror : name 'x' is not defined
'''
从执行结果中我们可以发现:在函数体内的“print(x)”成功执行,但是函数体外的“print(x)”执行失败,并且收到错误信息:"x"没有定义。
不只在函数体内赋值的变量是局部变量,函数定义时的参数也是局部变量,例如:
def foo():
print(x)
foo("hello")
print(x)
'''
执行结果:
hello
NameError : name 'x' is not defined
'''
可以看到,这个例子和上一个例子都得到了相同的结果:在函数体内的“print(x)”成功执行,在函数体外的“print(x)”执行失败,并且收到错误信息:“x”没有定义。这说明函数声明时的参数也是局部变量,只能在函数体内使用。
2、全局变量
在函数外赋值的变量就是全局变量,全局变量可以在整个程序范围内被访问。例如:
x = "hello"
def foo():
print(x)
foo()
# 执行结果:hello
从执行结果可以发现,函数foo中的“print(x)”被正常执行了,说明在函数体外的变量可以正常地在函数体内访问。但是,函数体内的重新赋值的相同函数名字变量并不会改变函数体外的全局变量。
x = "函数体外"
def foo():
x = "函数体内"
print(x)
foo()
print(x)
执行结果如下:
函数体内
函数内外
从执行结果中我们可以发现,函数foo()对“x”进行赋值操作时并没有改变函数体外的“x”变量。说明如果在函数体内对“x”进行“修改”(其实就是创建了一个新的变量,只是名字与函数体外的“x”变量相同),并不会修改函数体外的“x”。
哪如果想对函数体外的变量进行修改,我们应该如何处理呢?这时候就可以使用“global”关键字,例如:
x = "函数体外"
def foo():
global x
x = "函数体内"
foo()
print(x)
执行结果为:
函数体内
函数体内
从执行结果可以发现,在函数体内修改全局变量“x”为“函数体内”,函数体外的全局变量“x”也变成了“函数体内”。所以如果要在函数体内修改全局变量,就一定要添加“global”关键字。
五、函数返回值
如果想要获取函数中的局部变量,就可以使用“return”关键字返回。例如:
def foo():
x = "局部变量"
return x
result = foo()
print(result)
执行结果如下:
局部变量
从执行结果可以发现,“return x”成功地返回了局部变量“x”的内容。如果不写“return”或者只有“return”而后面没有变量,那会出现什么情况呢?
def no_return():
print("没有return")
def no_return_value():
print("有return返回值")
return
def has_return():
x = "局部变量"
print("有return有返回值")
return x
result1 = no_return()
print(result1)
result2 = no_return_value()
print(result2)
result3 = has_return()
print(result3)
执行结果如下:
没有return
None
有return没有返回值
None
有return有返回值
局部变量
从执行结果中我们可以看到,没有“return”和有“return”但是没有返回值,两者都会获得“None”。但如果有“return”并且带了返回值,就可以通过赋值的方式获取函数的返回值。
其实Python的返回值还有更高级的特性。Python可以返回不止一个值,例如:
def multi_value():
r1 = "第一个返回值"
r2 = "第二个返回值"
r3 = "第三个返回值"
r4 = "第四个返回值"
r5 = "第五个返回值"
return r1,r2,r3,r4,r5
s = multi_value()
print(s)
执行结果如下:
('第一个返回值','第二个返回值','第三个返回值','第四个返回值','第五个返回值')
从执行结果中我们可以看出,有多个返回结果时,Python会返回一个元组;当Python返回了元组,就可以赋值给多个变量。
def two_value():
return "第一个返回值","第二个返回值"
r1,r2 = two_value()
print(r1)
print(r2)
执行结果如下:
第一个返回值
第二个返回值
从执行结果中可以看到,函数中的两个返回值成功地赋值给了两个变量“r1”和“r2”。
六、Lambda表达式
Lambda表达式也成称作匿名函数,自然这类函数的特点就是不需要特别去定义函数的名字。通常需要一个函数、但又不想费神去命名他的时候,就可以使用匿名函数。
先来看一个简单的例子:
def add(x,y):
return x + ylambda x, y : x + y
例子中,add函数的作用就是返回两个参数“x”和“y”的和,改写成lambda表达式就是“lambda x,y:x+y”。以“lambda”开头,表示这是个lambda表达式,之后的内容由“:”分为两部分:“:”左边的是函数的参数,在例子中就是“x”和“y”,与定义一般函数时括号中的参数一致;“:”右边的就是要返回的值,lambda表达式不需要用“return”关键字返回内容,函数默认会返回“:”右边的值。注意例子中的lambda表达式并没有函数名。
我们也可以把lambda表达式赋值给变量,例如:
f = lambda x, y : x + y
print(f)
z = f(1,2)
print(z)
执行结果如下:
<function <lambda> at 0x0000011DD80F2C80>
3
既然lambda表达式没有函数名字,那么在什么时候会用到lambda表达式呢?一般有以下两种情况:
(1)程序只执行一次,不需要定义函数名,使用lambda表达式方便定义,并且节省了内存中变量的定义;
(2)在某些函数中必须以函数作为参数,但是函数本身十分简单而且只在一处使用。
a1 = [1,2,3,4,5,6,7,8]
a2 = [item for item in filter(lambda x : x>5,11)]
print(a2)
执行结果如下:
[6,7,8]
filter为Python的内置函数,用于过滤序列,即过滤掉不符合条件的元素。filter函数的第一个参数需要传入另一个函数,传入的函数用来作为筛选的条件,满足条件的返回“True”,否则返回“False”。在这个例子中使用lambda表达式会使程序变得更加简洁。
七、知识拓展
1、文档字符串
在使用“def”关键字定义一个函数时,其后必须跟有函数名和包括形式参数的圆括号。从函数体的下一行开始,必须要缩进。
函数体的第一行可以是字符串,这个字符串就是文档字符串。
def add(x,y):
"""
返回参数x和y两数之和
Parameters
----------
x:int
第一个参数
y:int
第二个参数
Returns
-------
int
返回 x + y
"""
return x + y
print(add(1,2))
print(add.__doc__)
执行结果如下:
3
返回参数x和y两数之和
Parameters
----------
x:int
第一个参数
y:int
第二个参数Returns
-------
int
返回 x + y
从执行的结果可以看出,文字字符串可以使用__doc__(注意是双下划线)获取。如果你已经在Python中使用过help(),就说明你已经看过文档字符串的使用了,它所做的只是抓取函数的__doc__属性,然后整洁地展示给你。例如:
def add(x,y):
"""
返回参数x和y两数之和
Parameters
----------
x:int
第一个参数
y:int
第二个参数
Returns
-------
int
返回 x + y
"""
return x + y
help(add)
执行结果为:
Help on function add in module __main__:
add(x, y)
返回参数x和y两数之和
Parameters
----------
x:int
第一个参数
y:int
第二个参数
Returns
-------
int
返回 x + y
自动化工具也能以同样的方式从你的程序中提取文档,例如Python发行版附带的pydoc命令可以根据文档字符串快速创建文档。
2、内置函数
Python解释器内置了许多不同功能和类型的函数,可以直接使用。本小节列出了Python内置的所有函数。
内置函数 | ||||
abs() | dict | help() | min() | setattr() |
all() | dir() | hex() | next() | slice() |
any() | divmod() | id() | object() | sorted() |
ascii() | enumerate() | input() | oct() | staticmethod() |
hin() | eval() | int() | open() | str() |
bool() | exec() | isinstance() | ord() | sum() |
bytearray() | filter() | issubclass() | pow() | super() |
bytes() | float() | iter() | print() | tuple() |
callable() | format() | len() | property() | type() |
chr() | frozenset() | list() | range() | vars() |
classmethod() | getattr() | locals() | repr() | zip() |
compile() | globals() | map() | reversed() | __import__() |
compile() | hasattr() | max() | round() | |
complex() | hash() | memoryview() | set() |
3、函数注释
函数注释是一个可选功能,它允许在函数参数和返回值中添加任意的元数据。无论是Python本身还是标准库,都使用了函数注释,第三方项目可以很方便地使用函数注释来进行文档编写和类型检查,或者用于其他用途。Python2中缺少了对函数参数和返回值进行注释的标准方法,很多工具和库为了填补这一空白,各自使用了不同的方式,但是由于机制和语法上的广泛差异,会在一定程度上引起混乱。为了解决这一问题,Python3引入了函数注释(PEP-3107),旨在提供一种单一、标准的方法来将元数据与函数参数和返回值相关联。
函数注释定义如下:
def function_name(a:expression,b: expression) -> expression:
function body
return value
函数注释的使用方法是:在定义函数参数时添加“:”来对参数进行注释,并在结尾添加“->”和表明返回值的注释。例如:
def compile(soure: "something compilable",
filename:"where the compilable thing comes from",
mode: "is this a single statement or a suite or a suite?")-> bool:
return True
print(compile.__annotations__)
执行结果:
{'soure': 'something compilable', 'filename': 'where the compilable thing comes from', 'mode': 'is this a single statement or a suite or a suite?', 'return': <class 'bool'>}
这个例子中添加了参数“soure” “filename”和“mode”的注释来说明这些参数的作用,并且定义了返回值是个bool类型。想要获取函数注释可以使用__annotations__方法。许多文本编辑器可以自动读取函数注释中的类型定义(如例子中的返回值bool类型),帮助并提示用户传入或获取正确类型的参数,以减少程序错误。