函数概述
函数的基本概念
函数用于程序中分离不同的任务。在程序设计过程中,如果可以分离任务,则建议使用函数分别实现分离后的子任务。
函数为代码复用提供了一个通用的机制。定义和使用函数是python程序设计的重要组成部分
函数允许程序的控制在调用代码和函数代码之间切换。也可以把控制转换到自身的函数,即函数自己调用本身,此过程称为
递归调用
函数的功能
函数是模块化程序的基本构成单位,使用函数,具有如下优点。
- 实现结构化程序设计。通过把程序分割为不同的功能模块,可以实现自顶向下的结构化设计
- 减少程序的复杂度。简化程序的结构,提高程序的可阅读性
- 实现代码复用。一次定义多次调用,实现代码的可重用性
- 提高代码的质量。实现分割后子任务的代码相对简单,易于开发、调试、修改和维护
- 协作开发。大型项目分割成不同的子任务后,团队多人可以分工合作,同时进行协作开发
- 实现特殊功能。递归函数可以实现许多复杂的算法
Python函数分类
- 内置函数。Python语言内置了若干常用的函数,在程序中可以直接使用
- 标准库函数。Python语言安装程序同时会安装若干标准库,通过
import
语句,可以导入标准库,然后使用其中的定义函数 - 第三方库函数。Python社区提供了许多其他高质量的库,下载安装这些库之后,通过
import
语句,可以导入库,然后使用其中定义的函数 - 用户自定义函数
函数的声明和调用
函数对象的创建
Python语言中,函数也是对象,使用
def
语句创建,其语法格式如下:
def 函数名([形参列表]):
函数体
函数的调用
函数的调用,根据需要,可以指定实际传入的参数值。函数的调用语法格式如下:
函数名([实参列表])
作为对象的函数
Python语言中,函数也是对象,故函数对象可以赋值给变量,函数对象也可以作为参数传递给函数,还可以作为函数的返回值。
Lamda表达式和匿名函数
Lmbda
是一种简便的、在同一行中定义函数的方法。Lambda
实际上生成一个对象,即匿名函数。基本表达式如下:
lambda arg1,arg2...:<expression>
其中,
arg1
、arg2
为函数的参数,<expression>
为函数的语句,其结果为函数的返回值。lambda x,y:x+y
,生成一个函数对象,函数参数为x,y
,返回值为x+y
函数的副作用
大多数函数接收到一个或多个参数,通过计算,返回一个值。这种类型的函数又称
纯函数
:给定同样的实际参数,其返回值唯一,且不会产生其他的可观察的副作用相对于纯函数,产生副作用的函数也有一定的应用场景。一般情况下,产生副作用的函数相当于其他程序设计语言中的过程。在这些函数中,可以省略
return
语句:当python执行完函数的最后一条语句,将控制权返回给调用者编写同时产生副作用和返回值的函数通常被认为是不良的编程风格。但有一个例外,即读取函数
input
,input()函数返回一个值,同时产生副作用(从标准输入中读取并消耗一个字符串)
函数的传递
形式参数和实际参数
函数的声明可以包含一个形参列表,而函数调用时则通过传递实参列表,以允许函数体中的代码引用这些参数变量
声明函数时所声明的参数,即为形式参数,简称
形参
;调用函数时,提供函数所需要的参数的值,即为实际参数,简称实参
实际参数值默认按位置顺序依次传递给形式参数。如果参数个数不对,会产生错误。
形式参数变量和对象引用传递
声明函数时声明形式参数,等同于函数体中的局部变量,在函数体中任何位置都可以使用。
局部变量和形式参数变量的区别在于,局部变量在函数体中绑定到某个对象:而形式参数变量则绑定到函数调用代码传递的对应实际参数对象
Python参数传递方法是传递对象引用,而不是传递对象的值
传递不可变对象的引用
调用函数,如果传递的是不可变对象的引用,则如果函数体修改对象的值,其结果实际上就是创建了一个新的对象
传递可变对象的引用
调用函数时,如果传递的是可变对象的引用,则函数体中可以直接修改对象的值
可选参数
在声明函数时,如果希望函数的一些参数是可选的,可以声明函数时为这些参数指定默认值。调用该函数时,如果没有传入对应的实参值,则函数使用声明时指定的默认参数数值
注意:必须先声明没有默认值的形参,然后声明有默认值的形参,这是因为函数调用时,默认是按位置传递实际参数值的
位置参数和命名参数
函数调用时,实参默认是按位置顺序传递形参。按位置传递的参数称为位置参数
函数调用是,也可以通过名称(关键字)指定传入的参数
按名称指定传入的参数称为命名参数
,也称关键字参数
。使用关键字参数具有三个优点:
- 参数按名称意义明确
- 传递参数与顺序无关
- 如果有多个可选参数,则可以选择指定某个参数值
在带星号的参数后面声明参数强制为
命名参数
,如果这些参数没有默认值,且调用时必须使用命名参数赋值,则引发错误
如果不需要带星号的参数,只需要强制命名参数,则可以简单地使用一个星号
可变参数
在声明函数时,通过带星的参数,如
*param1
,允许向函数传递可变数量的实参。调用函数时,从那一点后所有的参数被收集为一个元组。在声明函数时,也可以通过带双星的参数,如
**param2
,允许向函数传递可变数量的实参,调用函数时,从那一点后所有的参数被收集为一个字典带星或双星的参数必须位于形参列表的最后位置
强制命名参数
在带星号的参数后面申明参数会导致强制命名参数
(Keyword-only)
。调用时必须显示使用命名参数传递,因为按位置的传递参数默认收集为一个元组,传递给前面带星号的可变参数。如果不需要带星的可变参数,只想使用强制命名参数,可以简单地使用一个星号
参数类型检查
通常意义上,函数定义时既要指定定义域也要指定值域,即指定形式参数和返回值的类型
基于Python语言的设计理念,定义函数时,不用限定其参数和返回值的类型,这种灵活性可以实现多态性,即允许函数适用于不同类型的对象
当使用不支持的类型参数调用函数时,则会产生错误
函数的返回值
return语句和函数返回值
在函数体中使用
return
语句,可以实现从函数中返回一个值并跳出函数的功能
多条return语句
return
语句可以放置在函数中的任何位置,当执行到第一个return
语句时,程序返回调用程序
返回多个值
在函数体中使用
return
语句,可实现从函数返回一个值,并跳出函数,如果需要返回多个值,则可以返回一个元组
变量的作用域
变量声明的位置不同,其可以被访问的范围也不同。变量的可被访问范围称为变量的作用域。变量按其作用域大致分为:
全局变量
、局部变量
、类型成员变量
全局变量
在一个源代码文件中,在函数和类定义之外声明的变量称为全局变量。全局变量的作用域为其定义的模块,从定义的位置起,直到文件结束位置
通过
import
语句导入模块,也可以通过全限定名称模块名.变量名
访问,或者通过from...import
语句导入模块中变量并访问。不同的模块都可以访问全局变量,这会导致全局变量的不可预知性。如果多个语句同时修改一个全局变量,则可能导致程序中的错误,且很难发现和更正。
全局变量降低了函数或模块之间的通用性,也降低了代码的可读性,一般情况下,应该尽量避免使用全局变量,全局变量一般作为常量使用
局部变量
在函数体中声明变量(包括函数参数)成为局部变量,其有效范围(作用域)为函数体。
全局代码不能引用一个函数的局部变量或形式参数变量:一个函数也不能引用在另一个函数中定义的局部变量或形式参数变量
如果在一个函数中定义局部变量(或形式参数变量)与全局变量同名,则局部变量(或形式参数)优先,即函数中定义的变量是指局部变量(或形式参数变量),而不是全局变量。
全局语句global
在函数体中,可以引用全局变量,但如果函数内部的变量名是第一次出现且在赋值语句之前(变量赋值),则解释为定义局部变量。
如果要为定义在函数外的全局变量赋值,可以使用global语句,表明变量是在外面定义的全局变量。global语句可以指定多个全局变量
非局部语句nonlocal
在函数体中,可以定义嵌套函数,在嵌套函数中,如果要为定义在上级函数体的局部变量赋值,可以使用
nonlocal
语句,表明变量不是所在块的局部变量,而是上级函数体中定义的局部变量。nonlocal
语句可以指定多个非局部变量
类成员变量
类成员变量在类中声明变量,包括静态变量和实例变量,其有效范围(作用域)为类定义体内,在外部,通过创建类的对象实例,然后通过
对象.实例变量
访问类的实例变量,或者通过类.静态变量
访问静态变量
输出局部变量和全局变量
程序运行中,在上下文中会生成各种局部变量和全局变量,使用内置函数
globals()
和locals()
,可以查看并输出局部变量和全局变量列表
内置函数的使用
内置函数
Python3.8.2所有内置函数
>>> dir(__builtins__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
>>>
>>>
>>> for item in dir(__builtins__):print(item)
...
ArithmeticError
AssertionError
AttributeError
BaseException
BlockingIOError
BrokenPipeError
BufferError
BytesWarning
ChildProcessError
ConnectionAbortedError
ConnectionError
ConnectionRefusedError
ConnectionResetError
DeprecationWarning
EOFError
Ellipsis
EnvironmentError
Exception
False
FileExistsError
FileNotFoundError
FloatingPointError
FutureWarning
GeneratorExit
IOError
ImportError
ImportWarning
IndentationError
IndexError
InterruptedError
IsADirectoryError
KeyError
KeyboardInterrupt
LookupError
MemoryError
ModuleNotFoundError
NameError
None
NotADirectoryError
NotImplemented
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
PermissionError
ProcessLookupError
RecursionError
ReferenceError
ResourceWarning
RuntimeError
RuntimeWarning
StopAsyncIteration
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TimeoutError
True
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
WindowsError
ZeroDivisionError
_
__build_class__
__debug__
__doc__
__import__
__loader__
__name__
__package__
__spec__
abs
all
any
ascii
bin
bool
breakpoint
bytearray
bytes
callable
chr
classmethod
compile
complex
copyright
credits
delattr
dict
dir
divmod
enumerate
eval
exec
exit
filter
float
format
frozenset
getattr
globals
hasattr
hash
help
hex
id
input
int
isinstance
issubclass
iter
len
license
list
locals
map
max
memoryview
min
next
object
oct
open
ord
pow
print
property
quit
range
repr
reversed
round
set
setattr
slice
sorted
staticmethod
str
sum
super
tuple
type
vars
zip
>>>
eval函数
使用内置的
eval
函数,可以动态表达式进行求值,其语法格式为:eval(expression,globals=None,local=None)
其中
expression
是动态表达式的字符串;globals
和locals
是求值时使用的上下文环境的全局变量和局部变量,如果不指定,则使用当前上下文
eval
函数的功能是将字符串生成语句执行,如果字符串包含不安全的语句(例如删除文件语句),则存在注入安全隐患
exec函数
使用内置
exec
函数,可以执行动态语句,其语法格式为:
exec(str[,globals[,locals]])
其中,
str
是动态语句的字符串;globals
和locals
是使用的上下文环境的全局变量和局部变量,如果不指定,则使用当前运行的上下文通常,
eval
用于动态表达式求值,返回一个值,exec
用于动态语句的执行,不返回值。同样,exec
也存在注入安全隐患
compile函数
使用内置的
compile
函数,可以编译为代码对象,其语法格式:compile(source,filename,mode) #返回对象
其中,
source
为代码语句的字符串;如果是多行语句,则每一行的结尾必须有换行符\n
。filename
为包含代码的文件;mode
为编译模式,可以为exec
(用于语句系列执行),eval
(用于表达式求值)和single
(用于单个交互语句)编译后的代码对象可以通过
eval
函数或exec
函数执行,因为编译为代码对象,所以可以提高效率