博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python之函数
阅读量:6326 次
发布时间:2019-06-22

本文共 6346 字,大约阅读时间需要 21 分钟。

  本篇文章我们来说说Python中的函数,那么什么是函数呢?

  函数是Python为了代码最大程度地重用和最小化代码冗余而提供的基本程序结构。

  函数是一种设计工具,能让程序员将复杂的系统分解为可管理的部件,在需要时能够按需去调用这些部件。

  函数用于将相关功能打包并参数化

  在Python中可以创建4种函数:

    全局函数:定义在模块中

    局部函数:嵌套于其它函数中

    lambda(匿名)函数:仅是一个表达式,可以出现在任何位置,使用的灵活度比函数强得多。它生成一个函数对象,若想多次调用,则应将其赋予某变量名进行调用

    方法:与特定数据类型关联的函数,并且只能与数据类型关联一起使用。说白了就是定义在类(class)中的函数

创建函数,语法如下:

1
2
def 
functionName(parameters):
    
suite

  def是一个可执行语句,因此可以出现在任何能够使用语句的地方,甚至可以嵌套于其它语句,例如if或while中。

  def创建了一个对象并将其赋值给一个变量名(即函数名)。

  return用于返回结果对象,其为可选;无return语句的函数自动返回None对象。返回多个值时,彼此间使用逗号分隔,且组合为元组形式返回一个对象。

  def语句运行之后,可以在程序中通过函数后附加括号进行调用。如:

1
2
3
4
5
6
In [
1
]: 
def 
printName():    
#定义一个函数printName
   
...:     
print 
"hello"
   
...: 
 
In [
2
]: printName()         
#调用函数printName
hello

  Python创建、改变或查找变量名,都是在名称空间中进行的。名称空间(作用域)说白了就是一个变量所能够生效的范围。

  在Python中,变量名在程序代码中被赋值的位置,决定了其能够被访问到的范围。

  函数定义了本地作用域,而模块定义了全局作用域

    每个模块都是一个全局作用域,因此,全局作用域的范围仅限于单个文件;

    每次对函数的调用都会创建一个新的本地作用域,赋值的变量除非声明为全局变量,否则均为本地变量;

    所有的变量名都可以归纳为本地、全局或内置的(由__builtin__模块提供)

  变量名引用分三个作用域进行:首先是本地、之后是函数内、接着是全局,最后是内置。

  变量名解析遵循由内而外的法则,也即LEGB原则(如上图)。变量解析时有几点需要注意:

    先从Local(function)中查找,找到则返回解析结果,若找不到则到Enclosing function locals中查找;

    若在Enclosing function locals中找到了则返回解析结果,若找不到则到Global(module)中查找;

    若在Global(module)中找到了则返回解析结果,若找不到则到Built-in中查找;

    若在Built-in中找到了则返回解析结果,若找不到则返回“名称引用错误”异常

  变量名解析时,作用域越小,其优先级越高。也就是说,若在Local(function)中找到了,则直接返回其解析结果,而不会再到Enclosing function locals上去查找了。


  全局变量:

    全局变量是模块文件内部的顶层变量名;

    如果要在函数内部对全局变量进行赋值的话,必须事先使用global进行声明,global关键字后跟一个或多个由逗号隔开的变量名;

    全局变量名在函数内部不经声明也可以被引用;

    本地变量在函数返回时会消失,而全局变量不会,因此,使用全局变量是保存函数状态信息的最直接办法;

    尽量避免在函数中使用global声明全局变量。


  内嵌函数:

    Python中函数可以嵌套,也即可以在一个函数内部定义一个函数;

    在函数内部定义的函数仅能由外层函数进行调用;

    而如果外层函数直接把内层函数使用return返回,则调用外层函数时赋值的变量名引用的是一个内层函数对象,且此内层函数能够记忆其内部引用的外层函数的变量,这种行为叫做工厂函数,也叫闭合函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [
11
]: 
def 
maker(N):
    
...:     
def 
action(X):
    
...:         
return 
*
* 
N
    
...:     
return 
action
    
...:
 
In [
12
]: f 
= 
maker(
2
)
 
In [
13
]: f(
3
)
Out[
13
]: 
9
 
In [
14
]: f(
4
)
Out[
14
]: 
16
 
In [
15
]: 
type
(f)
Out[
15
]: function

  参数传递:

    参数的传递是通过自动将对象赋值给本地变量实现的

  不可变参数通过“值”进行传递,在函数内部改变形参的值,只是让其引用了另一个对象(如数字)

  可变参数通过“指针”进行传递,在函数内部改变形参的值,将直接修改引用的对象(如列表)


有两种方式可避免可变参数被函数修改:

  1、直接传递可变对象的副本:

1
2
3
4
5
6
7
8
9
10
11
12
In [
16
]: L 
= 
[
1
,
2
,
3
,
4
]
 
In [
17
]: 
def 
changer(X):
    
...:     X[
2
]
+
=
10
    
...:     
return 
X
    
...:
 
In [
18
]: changer(L[:])
Out[
18
]: [
1
2
13
4
]
 
In [
19
]: 
print 
L
[
1
2
3
4
]

  2、在函数内部创建可变参数的副本

1
2
3
4
5
6
7
8
9
10
11
12
13
In [
27
]: L 
= 
[
1
,
2
,
3
,
4
]
 
In [
28
]: 
def 
changer(X):
    
...:     aa 
= 
X[:]
    
...:     aa[
2
]
+
=
10
    
...:     
return 
aa
    
...:
 
In [
29
]: changer(L)
Out[
29
]: [
1
2
13
4
]
 
In [
30
]: 
print 
L
[
1
2
3
4
]


参数匹配模型:

  默认情况下,参数通过其位置进行传递,从左至右,这意味着,必须精确地传递和函数头部参数一样多的参数

  但也可以通过关键字参数、默认参数或参数容器等改变这种机制

    位置:从左至右

    关键字参数:使用“name=value”的语法通过参数名进行匹配

    默认参数:定义函数时使用“name=value”的语法直接给变量一个值,从而传入的值可以少于参数个数

    可变参数:定义是为了整合,调用是为了分解

      定义函数时使用*开头的参数,可用于收集任意多基于位置的参数:

1
2
3
4
5
6
7
8
9
In [
31
]: 
def 
test(
*
x):
    
...:     
print 
x
    
...: 
 
In [
32
]: test(
1
)
(
1
,)
 
In [
33
]: test(
1
,
2
,
3
)
(
1
2
3
)

      定义函数时使用**开头的参数,可用于收集任意多基于关键字参数,并将其转换成字典:

1
2
3
4
5
6
In [
34
]: 
def 
dict
(
*
*
x):
    
...:     
print 
x
    
...: 
 
In [
35
]: 
dict
(x
=
1
,y
=
2
,z
=
6
)
{
'y'
2
'x'
1
'z'
6
}

     混用位置参数与可变参数:

1
2
3
4
5
6
In [
36
]: 
def 
a(x,
*
y):
    
...:     
print 
x,y
    
...: 
 
In [
37
]: a(
1
,
2
,
3
,
4
)
1 
(
2
3
4
)
1
2
3
4
5
6
7
8
In [
38
]: 
def 
b(
*
x,
*
*
y):
    
...:     
print 
x
    
...:     
print 
y
    
...: 
 
In [
39
]: b(
1
,
2
,
3
,
4
,
5
,i
=
3
,j
=
9
,c
=
5
)
(
1
2
3
4
5
)
{
'i'
3
'c'
5
'j'
9
}

    可变参数解包:调用函数时,使用*开头的参数,可用于将参数集合打散,从而传递任意多基于位置或关键字的参数。可以理解为分解参数。

      变量分解赋值:

1
2
3
4
5
6
7
8
9
10
11
12
In [
1
]: l1 
= 
[
'Sun'
,
'Mon'
,
'Tue'
]
 
In [
2
]: x,y,z 
= 
l1
 
In [
3
]: 
print 
x
Sun
 
In [
4
]: 
print 
y
Mon
 
In [
5
]: 
print 
z
Tue
1
2
3
4
5
6
7
8
In [
6
]: l2 
= 
[
'Sun'
,
'Mon'
,
'Tue'
]
 
In [
7
]: 
def 
a(x,y,z):
   
...:     
print 
x,y,z
   
...: 
 
In [
8
]: a(
*
l2)
Sun Mon Tue

  可变参数与位置参数混合使用时,不论是定义还是调用,均使用以下顺序:

    位置参数-->任意位置参数(*)-->关键字参数(**)

匿名函数lambda:

语法如下:

1
lambda 
args: expression

    args:以逗号分隔的参数列表

    expression:用到args中各参数的表达式

  lambda语句定义的代码必须是合法的表达式,不能出现多条件语句(可使用if的三元表达式)和其它非表达式语句,如for和while等。

  lambda的首要用途是指定短小的回调函数。lambda将返回一个函数而不是将函数赋值给某变量名。

注意:

  lambda是一个表达式而非语句;

  lambda是一个单个表达式,而不是一个代码块

1
2
3
4
5
6
7
8
9
In [
9
]: 
lambda 
x,y: 
print 
x,y    
#不能是语句,只能是表达式
  
File 
"<ipython-input-9-323c5186b793>"
, line 
1
    
lambda 
x,y: 
print 
x,y
                    
^
SyntaxError: invalid syntax
 
 
In [
10
]: 
lambda 
x,y: x 
+ 
y        
#返回的是一个函数
Out[
10
]: <function __main__.<
lambda
>>

  因为lambda返回的是一个函数,所以如果要调用它的话,则要将lambda表达式赋予一个变量名,通过变量名来调用:

1
2
3
4
In [
11
]: a 
= 
lambda 
x,y: x 
+ 
y
 
In [
12
]: a(
5
,
6
)
Out[
12
]: 
11

  正因为lambda函数调用时必须赋予给某个变量名,通过变量名来调用,而这个变量名又不固定,故此称之为匿名函数。

  def语句创建的函数将赋值给某变量名,而lambda表达式则直接返回函数。lambda也支持使用默认参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [
13
]: 
def 
testFunc(x,y,z):
    
...:     
return 
+ 
+ 
z
    
...:
 
In [
14
]: testFunc(
4
,
5
,
6
)
Out[
14
]: 
15
 
In [
15
]: f 
= 
lambda 
x,y,z: x
+
y
+
z
 
In [
16
]: f(
4
,
5
,
6
)
Out[
16
]: 
15
 
In [
17
]: f2 
= 
(
lambda 
x,y,z
=
10
: x
+
y
+
z)
 
In [
18
]: f2(
4
,
5
)
Out[
18
]: 
19

  在对某个数据对象作出多种不同处理时,可以把每一种处理机制定义成一个lambda,而后对这个数据对象调用这多个lambda表达式。

1
2
3
4
5
6
7
In [
1
]: l3 
= 
[ (
lambda 
x: x
*
2
),(
lambda 
y: y
*
3
) ]
 
In [
2
]: 
for 
in 
l3:        
#i取到的是lambda表达式,而不是x和y的值
   
...:     
print 
i(
4
)
   
...: 
8
12

Python中的函数递归:

  说到递归,什么是递归呢?

  递归其实就是在运行中一层一层调用自己,直到不符合条件时退出。

  递归对自身的调用有以下几点需要注意:

    需要有一个退出条件(或者说边界条件)能够使自己退出递归状态,否则将会陷入无限式递归;

    需要有一个递归前进段来定义递归怎么一层层往前走;

    需要有一个递归返回段来定义必要时怎么一层层返回回来

  递归的次数不能过多,在python中函数的递归最深层次不能超过1000层,否则将抛出异常

通过递归的方式实现阶乘:

10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1

==> 10 * (10-1) * ((10-1)-1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
In [
4
]: 
def 
factorial(x):
   
...:     
if 
x <
= 
1
:
   
...:         
return 
1
   
...:     
else
:
   
...:         
return 
* 
factorial(x
-
1
)
   
...: 
   
...: 
 
In [
5
]: factorial(
3
#factorial(3) = 3 * factorial(2) = 3 * 2 * factorial(1) = 3 * 2 * 1
Out[
5
]: 
6
 
In [
6
]: factorial(
10
)
Out[
6
]: 
3628800
 
In [
7
]: 
10 
* 
9 
* 
8 
* 
7 
* 
6 
* 
5 
* 
4 
* 
3 
* 
2 
* 
1
Out[
7
]: 
3628800

通过递归的方式实现斐波那契数列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
In [
11
]: 
def 
fab(x):
    
...:     
if 
=
= 
1
:
    
...:         
return 
1
    
...:     
elif 
=
= 
0
:
    
...:         
return 
0
    
...:     
else
:
    
...:         result 
= 
int
(fab(x
-
1
+ 
fab(x
-
2
))
    
...:         
return 
result
    
...: 
 
In [
12
]: 
for 
in 
range
(
10
):
    
...:     
print 
fab(i)
    
...: 
0
1
1
2
3
5
8
13
21
34

Python中的函数设计规范:

  耦合性:

    通过参数接受输入,通过return产生输出以保证函数的独立性,除非必要,不要用print输出太多东西;

    尽量减少使用全局变量进行函数间通信;

    不要在函数中直接修改可变类型的参数;

    避免直接改变定义在另一个模块中的变量

  聚合性:

    每个函数都应该有一个单一的、统一的目标;

    每个函数的功能都应该相对简单,不要让一个函数的功能过多

Python中的函数执行环境:

  函数可以通过多种方法获得输入及产生输出

本文转自 忘情OK  51CTO博客,原文链接:http://blog.51cto.com/itchentao/1884653,如需转载请自行联系原作者
你可能感兴趣的文章
TensorFlow+实战Google深度学习框架学习笔记(10)-----神经网络几种优化方法
查看>>
SECTION 4: THE TIMEQUEST GUI
查看>>
window's chkdsk
查看>>
WCF序列化
查看>>
Host+DLL模式下快捷键的使用
查看>>
notepad++添加Compare插件
查看>>
[游戏学习22] MFC 井字棋 双人对战
查看>>
Qt中的qreal
查看>>
Codeforces Beta Round #95 (Div. 2) D.Subway
查看>>
企业搜索引擎开发之连接器connector(二十)
查看>>
HeadFirst Jsp 09 (JSTL)
查看>>
jquery版小型婚礼(可动态添加祝福语)
查看>>
Centos5.8 安装 PHP5.5 和 memcached
查看>>
第25周六
查看>>
[转]CENTOS LINUX安装并使用NFS共享文件
查看>>
Android AES加密算法及其实现
查看>>
Entity Framework公共的增删改方法
查看>>
hdu1698 Just a Hook 线段树:成段替换,总区间求和
查看>>
dorado spring知识补充
查看>>
Android -- ViewPager、Fragment、状态保存、通信
查看>>