第五章 Python函数你知多少

简介: 第五章 Python函数你知多少

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

5.1 语法

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

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

5.2 函数定义与调用

# 定义函数
>>> def func():
...   print "Hello world!"
...   return "Hello world!" 
...
# 调用函数
>>> func()
Hello world!
'Hello world!' 

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

顺便说下print和return区别:

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

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

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

#!/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可以打印。

#!/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 接受参数

>>> def func(a, b):    
    ...   print a + 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错误。

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

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

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

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

  5.3.2 函数参数默认值

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

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

  5.3.3 接受任意数量参数

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

  单个星号使用:

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

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

  两个星号使用:

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

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

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

>>> 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} 

  与普通参数一起使用:

>>> def func(a, b, *c):    
    ...   print a + b
    ...   print c
    ...
    >>> func(1,2,3,5,6)
    3
    (3, 5, 6)
    >>> def func(a, b, **c):
    ...   print a + 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)

先看看全局和局部变量:

>>> 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声明:

>>> 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)不带参数

>>> 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 x 
...   return func2
...
>>> func()()
2
>>> func2()
2 

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

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

2)带参数

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

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

>>> 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 闭包

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

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

>>> def func(a):
...   def func2(b):
...     return a * 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都是高阶函数,后面会讲解。

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

举例说明下高阶函数:

>>> def f(x):
...   return x * 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)本身是一个函数,包装另一个函数或类,它可以让其他函数在不需要改动代码情况下动态增加功能,装饰器返回的也是一个函数对象。

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

#!/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 

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

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

#!/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 

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

#!/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:函装饰器函数装饰函数    
    #!/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)带参数装饰器

#!/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 a + b
    f1(2, 2)
    # python test.py
    function name: f1
    Hello world!
    4 

  3)不固定参数

#!/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 a + b
    f1(1,2)
    # python test.py
    function name: f1
    f1() run.
    3 

   4)装饰器加参数

#!/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 a + b
    f1(1,2)
    # python test.py
    info - function name: f1
    f1() run.
    3 

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

#!/usr/bin/python    
    # -*- coding: utf-8 -*-
    def fontColor(color):
        begin = "\033["
        end = "\033[0m"
        d = {
            '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函数定义函数区别:

# 普通函数
>>> def func():
...   return "Hello world!"
...
>>> func()
>>> def func(a, b):
...   return a * 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

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

  例如:

>>> 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

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

  例如:过滤列表中的奇数

>>> 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()是一个二元运算函数,所以只接受二元操作函数。

  例如:计算列表总和

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

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

相关文章
|
7天前
|
缓存 监控 程序员
Python中的装饰器是一种特殊类型的声明,它允许程序员在不修改原有函数或类代码的基础上,通过在函数定义前添加额外的逻辑来增强或修改其行为。
【6月更文挑战第30天】Python装饰器是无侵入性地增强函数行为的工具,它们是接收函数并返回新函数的可调用对象。通过`@decorator`语法,可以在不修改原函数代码的情况下,添加如日志、性能监控等功能。装饰器促进代码复用、模块化,并保持源代码整洁。例如,`timer_decorator`能测量函数运行时间,展示其灵活性。
16 0
|
12天前
|
Python
python之print函数
python之print函数
15 0
|
2天前
|
Python
python解包字典到函数参数
【7月更文挑战第5天】
6 2
|
14天前
|
测试技术 开发者 Python
Python中的装饰器:提升函数的灵活性和可重用性
在Python编程中,装饰器是一种强大的工具,它可以在不修改函数本身的情况下,动态地扩展函数的功能。本文将介绍装饰器的工作原理及其在实际开发中的应用,帮助读者更好地理解和利用这一特性。
|
11天前
|
分布式计算 算法 Python
Python函数进阶:四大高阶函数、匿名函数、枚举、拉链与递归详解
Python函数进阶:四大高阶函数、匿名函数、枚举、拉链与递归详解
|
13天前
|
存储 Python
在Python中,匿名函数(lambda表达式)是一种简洁的创建小型、一次性使用的函数的方式。
【6月更文挑战第24天】Python的匿名函数,即lambda表达式,用于创建一次性的小型函数,常作为高阶函数如`map()`, `filter()`, `reduce()`的参数。lambda表达式以`lambda`开头,后跟参数列表,冒号分隔参数和单行表达式体。例如,`lambda x, y: x + y`定义了一个求和函数。在调用时,它们与普通函数相同。例如,`map(lambda x: x ** 2, [1, 2, 3, 4, 5])`会返回一个列表,其中包含原列表元素的平方。
26 4
|
14天前
|
JSON 数据格式 索引
Python内置函数如`print()`输出信息,`len()`计算长度
【6月更文挑战第23天】Python内置函数如`print()`输出信息,`len()`计算长度,`type()`识别类型,`range()`生成序列,`sum()`求和,`min()`和`max()`找极值,`abs()`取绝对值,`round()`四舍五入,`sorted()`排序,`zip()`和`enumerate()`组合及遍历,`map()`和`filter()`应用函数。标准库如`os`用于操作系统交互,`sys`处理解释器信息,`math`提供数学运算,`re`支持正则表达式,`json`处理JSON数据。学习这些能提升编程效率。
27 5
|
13天前
|
Python
在Python中,高阶函数是指那些可以接受一个或多个函数作为参数,并返回一个新的函数的函数。
【6月更文挑战第24天】Python的高阶函数简化代码,增强可读性。示例:`map()`检查用户名合法性,如`[&quot;Alice&quot;, &quot;Bob123&quot;, &quot;Charlie!&quot;, &quot;David7890&quot;]`;`reduce()`与`lambda`结合计算阶乘,如1到10的阶乘为3628800;`filter()`找出1到100中能被3整除的数,如[3, 6, 9, ..., 99]。
19 3
|
14天前
|
分布式计算 大数据 调度
MaxCompute产品使用问题之为什么用python写的udf函数跑起来比本地还要慢
MaxCompute作为一款全面的大数据处理平台,广泛应用于各类大数据分析、数据挖掘、BI及机器学习场景。掌握其核心功能、熟练操作流程、遵循最佳实践,可以帮助用户高效、安全地管理和利用海量数据。以下是一个关于MaxCompute产品使用的合集,涵盖了其核心功能、应用场景、操作流程以及最佳实践等内容。