1、定义函数
1.1 格式
定义函数的格式如下:
def 函数名():
函数封装的代码
……
def
是英文define
的缩写- 函数名称 应该能够表达 函数封装代码 的功能,方便后续的调用
- 函数名称 的命名应该 符合 标识符的命名规则
- 可以由 字母、下划线 和 数字 组成
- 不能以数字开头
- 不能与关键字重名
demo:
# 定义一个函数,能够完成打印信息的功能
def printInfo():
print '------------------------------------'
print ' 语雀,一只能言善语的云雀'
print '------------------------------------'
如果想定义一个什么事也不做的空函数,可以用pass语句:
def nop():
pass
pass语句什么都不做,那有什么用?实际上pass可以用来作为占位符,
比如现在还没想好怎么写函数的代码,就可以先放一个pass,让代码能运行起来。
1.2 函数调用
调用函数很简单的,通过 函数名() 即可完成调用
demo:
# 定义完函数后,函数是不会自动执行的,需要调用它才可以
printInfo()
1.3 文档说明
- 在开发中,如果希望给函数添加注释,应该在 定义函数 的下方,使用 连续的三对引号
- 在 连续的三对引号 之间编写对函数的说明文字
- 在 函数调用 位置,使用快捷键
CTRL + Q
可以查看函数的说明信息
注意:因为 函数体相对比较独立,函数定义的上方,应该和其他代码(包括注释)保留 两个空行
def test(a,b):
"""用来完成对2个数求和"""
print("%d" % (a + b))
test(11,22)
如果执行,以下代码help(test)
能够看到test函数的相关说明
Help on function test in module __main__:
test(a, b)
用来完成对2个数求和
1.4 PyCharm 技巧
- F8 Step Over 可以单步执行代码,会把函数调用看作是一行代码直接执行
- F7 Step Into 可以单步执行代码,如果是函数,会进入函数内部
2、函数参数
2.1 作用
- 函数,把 具有独立功能的代码块 组织为一个小模块,在需要的时候 调用
- 函数的参数,增加函数的 通用性,针对 相同的数据处理逻辑,能够 适应更多的数据
- 在函数 内部,把参数当做 变量 使用,进行需要的数据处理
- 函数调用时,按照函数定义的参数顺序,把 希望在函数内部处理的数据,通过参数 传递
2.2 形参和实参
- 形参:定义 函数时,小括号中的参数,是用来接收参数用的,在函数内部 作为变量使用
- 实参:调用 函数时,小括号中的参数,是用来把数据传递到 函数内部 用的
2.3 可变参数
有时可能需要一个函数能处理比当初声明时更多的参数。
这些参数叫做不定长参数,声明时不会命名。
基本语法如下:
def functionname(*args,**kwargs):
pass
# args和kwargs的名字可以任意写,不过建议写成这样通用的写法
加了星号 *`` ``的参数叫可变参数
会以元组(tuple)的形式导入,存放所有未命名的变量参数。
加了两个**的参数叫关键字参数
,以字典的方式保存,需写key名。
In [2]: def test(a,b,c=33,*args, **kwargs):
...: print(a)
...: print(b)
...: print(c)
...: print(args)
...: print(kwargs)
...:
In [3]: test(11,22,33,44,55,task=66,done=77)
11 # a
22 # b
33 # 默认参数c
(44, 55) # 分配完其他的,剩下的所有给args,元组格式
{'task': 66, 'done': 77} # 写了key的,给kwargs,字典格式
2.4 关键字参数
对于关键字参数,函数的调用者可以传入任意不受限制的关键字参数。
至于到底传入了哪些,就需要在函数内部通过kw
检查。
以person()
函数为例,我们希望检查是否有city
和job
参数:
def person(name, age, **kw):
if 'city' in kw:
# 有city参数
pass
if 'job' in kw:
# 有job参数
pass
print('name:', name, 'age:', age, 'other:', kw)
# 但是调用者仍可以传入不受限制的关键字参数:
person('语雀', 18, city='杭州', addr='Chaoyang', zipcode=123456)
name: 语雀 age: 18 other: {'city': '杭州', 'addr': 'Chaoyang', 'zipcode': 123456}
如果要限制关键字参数的名字,就可以用命名关键字参数,例如,
只接收city
和job
作为关键字参数。这种方式定义的函数如下:
def person(name, age, *, city, job):
print(name, age, city, job)
和关键字参数**kw
不同,命名关键字参数需要一个特殊分隔符*
,*
后面的参数被视为命名关键字参数。
调用方式如下:
>>> person('语雀', 18, city='杭州', job='Engineer')
语雀 18 杭州 Engineer
如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*
了:
def person(name, age, *args, city, job):
print(name, age, args, city, job)
命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:
>>> person('语雀', 18, city='杭州', job='Engineer')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given
语雀 18 () 杭州 Engineer
由于调用时缺少参数名city
和job
,Python解释器把这4个参数均视为位置参数,
但person()
函数仅接受2个位置参数。
命名关键字参数可以有缺省值,从而简化调用:
def person(name, age, *, city='杭州', job):
print(name, age, city, job)
由于命名关键字参数city具有默认值,调用时,可不传入city参数:
>>> person('语雀', 18, job='Engineer')
语雀 18 杭州 Engineer
使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个*
作为特殊分隔符。如果缺少*
,Python解释器将无法识别位置参数和命名关键字参数:
def person(name, age, city, job):
# 缺少 *,city和job被视为位置参数
pass
2.5 返回值
所谓“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果。
return 关键字用于退出函数,选择性地向调用方返回一个表达式
In [26]: def sum( arg1, arg2 ):
...: # 返回2个参数的和."
...: total = arg1 + arg2
...: print ("函数内 : ", total)
...: return total
...: # return用来结束函数,后面跟的变量返回给调用者
In [27]: total = sum( 10, 20 ) #有返回值,则需要一个变量来接收保存
('函数内 : ', 30)
In [28]: print ("函数外 : ", total)
('函数外 : ', 30)
函数根据有没有参数,有没有返回值可以相互组合成
四种类型的函数:
有返回值 | 无返回值 | |
---|---|---|
有参数 | 有参,有返回函数 | 有参,无返回函数 |
无参数 | 无参,有返回函数 | 无参,无返回函数 |
3、变量作用域
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种,分别是:
- L (Local) 局部作用域
- E (Enclosing) 闭包函数外的函数中
- G (Global) 全局作用域
- B (Built-in) 内建作用域
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
x = int(2.9) # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域
3.1 全局变量和局部变量
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。如下实例:
total = 0 # 这是一个全局变量
# 可写函数说明
def sum( arg1, arg2 ):
#返回2个参数的和."
total = arg1 + arg2 # total在这里是局部变量.
print ("函数内是局部变量 : ", total)
return total
#调用sum函数
sum( 10, 20 )
print ("函数外是全局变量 : ", total)
以上实例输出结果:
函数内是局部变量 : 30
函数外是全局变量 : 0
3.2 global 和 nonlocal关键字
当内部作用域想修改外部作用域的变量时,就要用到global和nonlocal关键字了。
以下实例修改全局变量 num:
num = 1
def fun1():
global num # 需要使用 global 关键字声明
print(num)
num = 123
print(num)
fun1()
print(num)
以上实例输出结果:
1
123
123
如果要修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量则需要 nonlocal 关键字了,如下实例:
def outer():
num = 10
def inner():
nonlocal num # nonlocal关键字声明
num = 100
print(num)
inner()
print(num)
outer()
以上实例输出结果:
100
100
另外有一种特殊情况,假设下面这段代码被运行:
a = 10
def test():
a = a + 1
print(a)
test()
以上程序执行,报错信息如下:
Traceback (most recent call last):
File "test.py", line 7, in <module>
test()
File "test.py", line 5, in test
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。
修改 a 为全局变量,通过函数参数传递,可以正常执行输出结果为:
a = 10
def test(a):
a = a + 1
print(a)
test(a)
执行输出结果为:11
4、递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n
- 解决办法1:
看阶乘的规律
1! = 1
2! = 2 × 1 = 2 × 1!
3! = 3 × 2 × 1 = 3 × 2!
4! = 4 × 3 × 2 × 1 = 4 × 3!
...
n! = n × (n-1)!
- 解决办法2:
原理
理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归时必须有一个明确的结束条件,不然会导致无限调用。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
把上面的递归函数的参数改成100000
就导致栈溢出!
解决递归调用栈溢出的方法是通过尾递归优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。
5、匿名函数
匿名函数(lambda)是指没有名字的函数,应用在需要一个函数但是又不想费神去命名这个函数的场合。通常情况下,这样的函数只使用一次
lambda函数的语法只包含一个语句:lambda x: x * x
实际上就是:
def f(x):
return x * x
关键字lambda表示匿名函数,冒号前面的x表示函数参数。
匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果
用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
In [4]: f = lambda x: x * x
In [5]: f
Out[5]: <function __main__.<lambda>>
In [6]: f(5)
Out[6]: 25
同样,也可以把匿名函数作为返回值返回,比如:
In [14]: def build(x, y):
...: return lambda: x * x + y * y
...:
In [15]: f = build(10,20) # 用f来接收build返回的函数地址
In [16]: f() #调用函数方法
Out[16]: 500
6、内置函数
6.1 map()
描述
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
语法
map() 函数语法:
map(function, iterable, ...)
参数
function -- 函数,有两个参数
iterable -- 一个或多个序列
返回值
Python 2.x 返回列表。
Python 3.x 返回迭代器。
实例
6.2 filter()
描述
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。
根据返回值是True还是False决定保留还是丢弃该元素
语法
以下是 filter() 方法的语法:
filter(function, iterable)
参数
function -- 判断函数。
iterable -- 可迭代对象。
返回值
Python 2.x 返回列表。
Python 3.x 返回迭代器。
实例
6.3 reduce()
描述
reduce() 函数会对参数序列中元素进行累积。
函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
语法
reduce() 函数语法:reduce(function, iterable[, initializer])
参数
function -- 函数,有两个参数
iterable -- 可迭代对象
initializer -- 可选,初始参数
返回值
返回函数计算结果。
实例:
6.4 zip()
描述
将多个可迭代对象一一对应(不符合则不保留)重新合并成一个zip对象,可迭代的方式
就像拉链,一一对应,如果单独一个则不保留
语法
zip 语法:
zip([iterable, ...])
参数:
iterabl -- 一个或多个迭代器;
返回值
返回一个对象。
实例
6.5 eval()
描述
eval() 函数用来执行一个字符串表达式,并返回表达式的值。
语法
以下是 eval() 方法的语法:
eval(expression[, globals[, locals]])
参数
expression -- 表达式。
globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。
locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。
返回值
返回表达式计算结果。
实例
x = 2
y = 1
num1 = eval('x+y')
print(num1)
def hc():
x = 2
y = 2
num2 = eval('x+y')
print(num2)
num3 = eval('x+y',globals())
print(num3)
num4 = eval('x+y', globals(),locals())
print(num4)
hc()
3
4
3
4