第五章 Python函数你知多少

简介:

函数作用:把一些复杂的代码封装起来,函数一般都是一个功能,用的时候才调用,提高重复利用率和简化程序结构。

5.1 语法

1
2
3
def  functionName(parms1, parms2, ...):
    code block
    return  expression

函数以def关键字开头,空格后跟函数名,括号里面是参数,用于传参,函数代码段里面引用。

5.2 函数定义与调用

1
2
3
4
5
6
7
8
9
# 定义函数
>>>  def  func():
...    print  "Hello world!"
...    return  "Hello world!" 
...
# 调用函数
>>> func()
Hello world!
'Hello world!'

当我们定义好函数,是不执行的,没有任何输出。当输入函数名后跟双小括号才会执行函数里写的代码。

顺便说下print和return区别:

有没有点奇怪!为什么print和return输出一样呢,return就加个单引号,貌似也没啥明显区别啊!其实在解释器下所有的结果都会输出的。

先了解下return作用:结束函数,并返回一个值。如果不跟表达式,会返回一个None。

好,那么我们深入了解下他们区别,举个例子,写个py程序:

1
2
3
4
5
6
7
#!/usr/bin/env python
def  func():
     print  "1: Hello world!"
     return  "2: Hello world!"
func()
# python test.py
1 : Hello world!

明白点了嘛?print是打印对象的值,而return是返回对象的值。也就是说你return默认是将对象值存储起来,要想知道里面的值,可以用print可以打印。

1
2
3
4
5
6
7
8
#!/usr/bin/env python
def  func():
     print  "1: Hello world!"
     return  "2: Hello world!"
print  func()
# python test.py
1 : Hello world!
2 : Hello world!

为什么函数里面不用print就在这里,往往我们定义一个函数是不需要打印的,而是交给其他代码去处理这个函数返回值。当然,print在调试函数代码时会起到很好的帮助。

5.3 函数参数

  5.3.1 接受参数

1
2
3
4
5
6
7
8
9
     >>>  def  func(a, b):    
     ...    print  +  b
     ...
     >>> func( 1 2 )
     3
     >>> func( 1 2 3 )
     Traceback (most recent call last):
       File  "<stdin>" , line  1 in  <module>
     TypeError: func() takes exactly  2  arguments ( 3  given)

   a和b可以理解为是个变量,可由里面代码块引用。调用函数时,小括号里面的表达式数量要对应函数参数数量,并且按传参按位置赋予函数参数位置。如果数量不对应,会抛出TypeError错误。

   当然,函数参数也可以是数组:

1
2
3
4
5
6
7
     >>>  def  func(a):    
     ...    print  a
     ...
     >>> func([ 1 , 2 , 3 ])
     [ 1 2 3 ]
     >>> func({ 'a' : 1 , 'b' : 2 })
     { 'a' 1 'b' 2 }

   如果不想一一对应传参,可以指定参数值:

1
2
3
4
5
     >>>  def  func(a,b):    
     ...    print  +  b
     ...
     >>> func(b = 2 ,a = 1 )
     3

   5.3.2 函数参数默认值

   参数默认值是预先定义好,如果调用函数时传入了这个值,那么将以传入的为实际值,否则是默认值。

1
2
3
4
5
6
7
     >>> def func(a, b=2):    
     ...   print a + b
     ...
     >>> func(1)
     3
     >>> func(1, 3)
     4

   5.3.3 接受任意数量参数

   上面方式固定了参数多个,当不知道多少参数时候可以用以下方式。

   单个星号使用:

1
2
3
4
5
     >>>  def  func( * a):         
     ...    print  a
     ...
     >>> func( 1 , 2 , 3 )
     ( 1 2 3 )

   单个星号存储为一个元组。

   两个星号使用:

1
2
3
4
5
     >>>  def  func( * * a):    
     ...    print  a
     ...
     >>> func(a = 1 , b = 2 , c = 3 )
     { 'a' 1 'c' 3 'b' 2 }

   两个星号存储为一个字典。可见它们都是以数组的形式传入。

   你也许在查资料的时候,会看到这样写的函数参数(*args, **kwargs),与上面只是名字不一样罢了 :

1
2
3
4
5
6
7
     >>>  def  func( * args,  * * kwargs):    
     ...    print  args
     ...    print  kwargs
     ...
     >>> func( 1 , 2 , 3 ,a = 1 ,b = 2 ,c = 3 )
     ( 1 2 3 )
     { 'a' 1 'c' 3 'b' 2 }

   与普通参数一起使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
     >>>  def  func(a, b,  * c):    
     ...    print  +  b
     ...    print  c
     ...
     >>> func( 1 , 2 , 3 , 5 , 6 )
     3
     ( 3 5 6 )
     >>>  def  func(a, b,  * * c):
     ...    print  +  b
     ...    print  c
     ...
     >>> func( 1 , 2 ,a = 1 ,b = 2 ,c = 3 )
     Traceback (most recent call last):
       File  "<stdin>" , line  1 in  <module>
     TypeError: func() got multiple values  for  keyword argument  'a'
     >>> func( 1 , 2 ,c = 3 ,d = 4 ,e = 5 )
     3
     { 'c' 3 'e' 5 'd' 4 }

    抛出异常,是因为传入的第一个参数1,和第三个参数a=1,都认为是传入函数参数a了。请注意下这点。

5.4 作用域

作用域听着挺新鲜,其实很简单,就是限制一个变量或一段代码可用范围,不在这个范围就不可用。提高了程序逻辑的局部性,减少名字冲突。

作用域范围一般是:全局(global)->局部(local)->内置(build-in)

先看看全局和局部变量:

1
2
3
4
5
6
7
8
9
10
>>> a  =  2
>>>  def  func():
...   b  =  3
...
>>> a
2
>>> b
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
NameError: name  'b'  is  not  defined

a变量的作用域是整个代码中有效,称为全局变量,也就是说一段代码最开始定义的变量。

b变量的作用域在函数内部,也就是局部变量,在函数外是不可引用的。

这么一来,全局变量与局部变量即使名字一样也不冲突。

如果函数内部的变量也能在全局引用,需要使用global声明:

1
2
3
4
5
6
7
8
9
10
11
>>>  def  func():
...    global  b
...   b  =  3
...
>>> b
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
NameError: name  'b'  is  not  defined
>>> func()
>>> b
3

有报错,说明一个问题,当函数没引用使用,里面的代码块是没有解释的。

使用global声明变量后外部是可以调用函数内部的变量的。

5.5 嵌套函数

1)不带参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>>  def  func():
...   x  =  2
...    def  func2():
...      return  x
...    return  func2   # 返回func2函数
...
>>> func()()
2
>>> func2()
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
NameError: name  'func2'  is  not  defined
>>>  def  func():   
...   x  =  2         
...    global  func2
...    def  func2():
...      return 
...    return  func2
...
>>> func()()
2
>>> func2()
2

内层函数可以访问外层函数的作用域。内嵌函数只能被外层函数调用,但也可以使用global声明全局作用域。

调用内部函数的另一种用法:

2)带参数

1
2
3
4
5
6
7
8
9
10
>>>  def  func(a):
...    def  func2(b):
...      return  *  b
...    return  func2
...
>>> f  =  func( 2 )    # 变量指向函数。是的,变量可以指向函数。
>>> f( 5 )
10
>>> func( 2 )( 5 )
10

内层函数可以访问外层函数的作用域 。但变量不能重新赋值,举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>>  def  func():
...   x  =  2
...    def  func2():
...      x  =  3
...   func2()
...    return  x
...
>>> func()
2
>>>  def  func():
...   x  =  2
...    def  func2():
...     x  + =  1
...   func2()
...    return  x
...
>>> func()
Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
   File  "<stdin>" , line  5 in  func
   File  "<stdin>" , line  4 in  func2
UnboundLocalError: local variable  'x'  referenced before assignment

5.6 闭包

“官方”的解释是:所谓“闭包”,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。

其实,上面嵌套函数就是闭包一种方式:

1
2
3
4
5
6
7
8
>>>  def  func(a):
...    def  func2(b):
...      return  *  b
...    return  func2
...
>>> f  =  func( 2 )    # 变量指向函数。是的,变量可以指向函数。
>>> f( 5 )
10

func是一个函数,里面又嵌套了一个函数func2,外部函数传过来的a参数,这个变量会绑定到函数func2。func函数以内层函数func2作为返回值,然后把func函数存储到f变量中。当外层函数调用内层函数时,内层函数才会执行(func()()),就创建了一个闭包。

5.7 高阶函数

高阶函数是至少满足这两个任意中的一个条件:

1) 能接受一个或多个函数作为输入。

2)输出一个函数。

abs、map、reduce都是高阶函数,后面会讲解。

其实,上面所讲的嵌套函数也是高阶函数。

举例说明下高阶函数:

1
2
3
4
5
6
7
8
>>>  def  f(x):
...    return  *  x
...
>>>  def  f2(func, y):
...    return  func(y)
...
>>> f2(f,  2 )
4

这里的f2就是一个高阶函数,因为它的第一个参数是一个函数,满足了第一个条件。


博客地址:http://lizhenliang.blog.51cto.com

QQ群:Shell/Python运维开发群 323779636


5.8 函数装饰器

装饰器(decorator)本身是一个函数,包装另一个函数或类,它可以让其他函数在不需要改动代码情况下动态增加功能,装饰器返回的也是一个函数对象。

先举一个例子,说明下装饰器的效果,定义两个函数,分别传参计算乘积:

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/python
# -*- coding: utf-8 -*-
def  f1(a, b):
     print  "f1 result: "  +  str (a  *  b)
def  f2(a, b):
     print  "f2 result: "  +  str (a  *  b)
f1( 1 2 )
f2( 2 2 )
# python test.py
f1 result:  2
f2 result:  4

跟预期的那样,打印出了乘积。

如果我想给这两个函数加一个打印传入的参数,怎么办,应该这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# -*- coding: utf-8 -*-
def  f1(a, b):
     print  "f1 parameter: %d %d"  % (a, b)
     print  "f1 result: "  +  str (a  *  b)
def  f2(a, b):
     print  "f2 parameter: %d %d"  % (a, b)
     print  "f2 result: "  +  str (a  *  b)
f1( 1 2 )
f2( 2 2 )
# python test.py
f1 parameter:  1  2
f1 result:  2
f2 parameter:  2  2
f2 result:  4

按照所想的打印了传入的参数,有没有方法能更简洁点呢,来看看装饰器后的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python
# -*- coding: utf-8 -*-
def  deco(func):
     def  f(a, b):
         print  "%s parameter: %d %d"  % (func.__name__, a, b)
         return  func(a, b)
     return  f
@deco
def  f1(a, b):
     print  "f1 result: "  +  str (a  *  b)
@deco
def  f2(a, b):
     print  "f2 result: "  +  str (a  *  b)
f1( 1 2 )
f2( 2 2 )
# python test.py
f1 parameter:  1  2
f1 result:  2
f2 parameter:  2  2
f2 result:  4

可见用装饰器也实现了上面方法,给要装饰的函数添加了装饰器定义的功能,这种方式显得是不是更简洁呢!

好,那么我们继续深入学习装饰器用法。

   5.8.1 无参数装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
     方式 1 :函装饰器函数装饰函数    
     #!/usr/bin/python
     # -*- coding: utf-8 -*-
     def  deco(func):
         return  func
     def  f1():
         print  "Hello world!"
     myfunc  =  deco(f1)
     myfunc()  
     # python test.py
     Hello world!
     
     方式 2 :使用语法糖 "@" 来装饰函数
     #!/usr/bin/python
     # -*- coding: utf-8 -*-
     def  deco(func):
         return  func
     @deco
     def  f1():
         print  "Hello world!"
     f1()
     # python test.py
     Hello world!

   方式1是将一个函数作为参数传给装饰器函数。

   方式2使用了语法糖,也实现同样效果。

   其实两种方式结果一样,方式1需要每次使用装饰器时要先变量赋值下,而方式2使用装饰器时直接用语法糖"@"引用,会显得更方便些,实际代码中一般也都是用语法糖。

   2)带参数装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     #!/usr/bin/python    
     # -*- coding: utf-8 -*-
     def  deco(func):
         def  f(a, b):
             print  "function name: %s"  %  func.__name__    # __name__属性是获取函数名,为了说明执行了这个函数
             return  func(a, b)    # 用接受过来的func函数来处理传过来的参数
         return  f
     @deco
     def  f1(a, b):
         print  "Hello world!"
         print  +  b
     f1( 2 2 )
     # python test.py
     function name: f1
     Hello world!
     4

   3)不固定参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     #!/usr/bin/python    
     # -*- coding: utf-8 -*-
     def  log(func):
         def  deco( * args,  * * kwargs):
             print  "function name: %s"  %  func.__name__
             return  func( * args,  * * kwargs)
         return  deco
     @log
     def  f1(a, b):
         print  "f1() run."
         print  +  b
     f1( 1 , 2 )
     # python test.py
     function name: f1
     f1() run.
     3

    4)装饰器加参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     #!/usr/bin/python    
     # -*- coding: utf-8 -*-
     # 三层函数,调用log函数返回deco函数,再调用返回的函数deco,则返回值是_deco函数
     def  log(arg):
         def  deco(func):
             def  _deco( * args,  * * kwargs):
                 print  "%s - function name: %s"  %  (arg, func.__name__)  
                 return  func( * args,  * * kwargs)
             return  _deco
         return  deco
     @log( "info" )
     def  f1(a, b):
         print  "f1() run."
         print  +  b
     f1( 1 , 2 )
     # python test.py
     info  -  function name: f1
     f1() run.
     3

    再举一个例子,给函数输出字符串带颜色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
     #!/usr/bin/python    
     # -*- coding: utf-8 -*-
     def  fontColor(color):
         begin  =  "\033["
         end  =  "\033[0m"
         =  {
             'red' : '31m' ,
             'green' : '32m' ,
             'yellow' : '33m' ,
             'blue' : '34m'
         }
         def  deco(func):
             print  begin  +  d[color]  +  func()  +  end
         return  deco
     @fontColor( "red" )
     def  f():
         return  "Hello world!"
     @fontColor( "green" )
     def  f2():
         return  "Hello world!"

   可以看出装饰器处理方式满足了高阶函数的条件,所以装饰器也是一种高阶函数。

   优点:灵活给装饰器增加功能,而不修改函数,提高代码可重复利用性,增加可读性。

5.9 匿名函数

匿名函数:定义函数的一种形式,无需定义函数名和语句块,因此代码逻辑会受到局限,同时也减少代码量,增加可读性。

在Python中匿名函数是lambda。

举例子说明def关键字与lambda函数定义函数区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 普通函数
>>>  def  func():
...    return  "Hello world!"
...
>>> func()
>>>  def  func(a, b):
...    return  *  b
...
>>> func( 2 2 )
4
 
# 匿名函数
>>> f  =  lambda : "Hello world!"
>>> f()
'Hello world!'
>>> f  =  lambda  a, b: a  *  b    # 冒号左边是函数参数,右边是返回值
>>> f( 2 2 )
4

lambda函数一行就写成一个函数功能,省去定义函数过程,让代码更加精简。

5.10 内置高阶函数

   5.10.1 map()

   语法:map(function, sequence[, sequence, ...]) -> list

   将序列中的元素通过函数处理返回一个新列表。

   例如:

1
2
3
     >>> lst  =  [ 1 , 2 , 3 , 4 , 5 ]    
     >>>  map ( lambda  x: str (x) + ".txt" , lst)
     [ '1.txt' '2.txt' '3.txt' '4.txt' '5.txt' ]

   5.10.2 filter()

   语法:filter(function or None, sequence) -> list, tuple, or string

   将序列中的元素通过函数处理返回一个新列表、元组或字符串。

   例如:过滤列表中的奇数

1
2
3
     >>> lst  =  [ 1 , 2 , 3 , 4 , 5 ]    
     >>>  filter ( lambda  x:x % 2 = = 0 , lst)
     [ 2 4 ]

   5.10.3 reduce()

   语法:reduce(function, sequence[, initial]) -> value

   reduce()是一个二元运算函数,所以只接受二元操作函数。

   例如:计算列表总和

1
2
3
     >>> lst  =  [ 1 , 2 , 3 , 4 , 5 ]    
     >>>  reduce ( lambda  x,y:x + y, lst)
     15

    先将前两个元素相加等于3,再把结果与第三个元素相加等于6,以此类推。这就是reduce()函数功能。



本文转自 李振良OK 51CTO博客,原文链接:http://blog.51cto.com/lizhenliang/1856793,如需转载请自行联系原作者

相关文章
|
1天前
|
Python
PyQt---------信号与槽函数的关系
PyQt---------信号与槽函数的关系
9 1
|
1天前
|
Python
Python闭包函数和计时器
本文介绍了闭包函数的概念,它允许内部函数引用外部作用域的变量但无法修改它们。示例展示了如何使用闭包来封装函数。接着,文章讨论了如何在函数调用时添加开始和结束的打印语句,通过传递函数作为参数实现。然后,文章引入装饰器,通过闭包定义了一个`timer`装饰器,用于在函数执行前后打印消息。最后,给出了一个练习,实现了一个计算函数执行时间的装饰器,处理了带有参数的被装饰函数。
12 1
|
1天前
|
测试技术 开发者 Python
Python检查函数和方法的输入/输出
【5月更文挑战第5天】Python检查函数和方法的输入/输出
11 1
|
1天前
|
Python
在Python中,利用`os模块`的`path.exists()`函数可判断文件是否存
【5月更文挑战第12天】在Python中,利用`os模块`的`path.exists()`函数可判断文件是否存在,该函数对路径进行检查,存在则返回True,不存在则返回False。示例代码展示了如何检查'example.txt'文件是否存在并相应打印消息。此外,`os.path.isfile()`用于确认路径是否为文件,仅当是文件时返回True,否则返回False,同样配以示例说明其用法。
16 2
|
1天前
|
Python
【Python操作基础】——函数
【Python操作基础】——函数
|
1天前
|
Python
Python的全局变量作用于整个程序,生命周期与程序相同,而局部变量仅限函数内部使用,随函数执行结束而销毁。
【5月更文挑战第11天】Python的全局变量作用于整个程序,生命周期与程序相同,而局部变量仅限函数内部使用,随函数执行结束而销毁。在函数内部修改全局变量需用`global`关键字声明,否则会创建新局部变量。
56 2
|
1天前
|
Java C# 开发者
Python 中的类型注解是一种用于描述变量、函数参数和返回值预期类型的机制
【5月更文挑战第8天】Python的类型注解提升代码可读性和可维护性,虽非强制,但利于静态类型检查(如Mypy)。包括:变量注解、函数参数和返回值注解,使用内置或`typing`模块的复杂类型,自定义类型注解,以及泛型模拟。类型注解可在变量声明、函数定义和注释中使用,帮助避免类型错误,提高开发效率。
20 6
|
1天前
|
存储 Python
【Python 基础】解释reduce函数的工作原理
【5月更文挑战第6天】【Python 基础】解释reduce函数的工作原理
|
1天前
|
Python
【Python 基础】解释map函数的工作原理
【5月更文挑战第6天】【Python 基础】解释map函数的工作原理
|
1天前
|
索引 Python
【Python 基础】解释Range函数
【5月更文挑战第6天】【Python 基础】解释Range函数