天天看点

第五章 Python函数你知多少

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

5.1 语法

1

2

3

<code>def</code> <code>functionName(parms1, parms2, ...):</code>

<code>   </code><code>code block</code>

<code>   </code><code>return</code> <code>expression</code>

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

5.2 函数定义与调用

4

5

6

7

8

9

<code># 定义函数</code>

<code>&gt;&gt;&gt; </code><code>def</code> <code>func():</code>

<code>...   </code><code>print</code> <code>"Hello world!"</code>

<code>...   </code><code>return</code> <code>"Hello world!"</code> 

<code>...</code>

<code># 调用函数</code>

<code>&gt;&gt;&gt; func()</code>

<code>Hello world!</code>

<code>'Hello world!'</code>

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

顺便说下print和return区别:

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

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

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

<code>#!/usr/bin/env python</code>

<code>def</code> <code>func():</code>

<code>    </code><code>print</code> <code>"1: Hello world!"</code>

<code>    </code><code>return</code> <code>"2: Hello world!"</code>

<code>func()</code>

<code># python test.py</code>

<code>1</code><code>: Hello world!</code>

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

<code>print</code> <code>func()</code>

<code>2</code><code>: Hello world!</code>

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

5.3 函数参数

  5.3.1 接受参数

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(a, b):    </code>

<code>    </code><code>...   </code><code>print</code> <code>a </code><code>+</code> <code>b</code>

<code>    </code><code>...</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>, </code><code>2</code><code>)</code>

<code>    </code><code>3</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>, </code><code>2</code><code>, </code><code>3</code><code>)</code>

<code>    </code><code>Traceback (most recent call last):</code>

<code>      </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>    </code><code>TypeError: func() takes exactly </code><code>2</code> <code>arguments (</code><code>3</code> <code>given)</code>

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

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

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(a):    </code>

<code>    </code><code>...   </code><code>print</code> <code>a</code>

<code>    </code><code>&gt;&gt;&gt; func([</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>])</code>

<code>    </code><code>[</code><code>1</code><code>, </code><code>2</code><code>, </code><code>3</code><code>]</code>

<code>    </code><code>&gt;&gt;&gt; func({</code><code>'a'</code><code>:</code><code>1</code><code>,</code><code>'b'</code><code>:</code><code>2</code><code>})</code>

<code>    </code><code>{</code><code>'a'</code><code>: </code><code>1</code><code>, </code><code>'b'</code><code>: </code><code>2</code><code>}</code>

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

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(a,b):    </code>

<code>    </code><code>&gt;&gt;&gt; func(b</code><code>=</code><code>2</code><code>,a</code><code>=</code><code>1</code><code>)</code>

   5.3.2 函数参数默认值

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

<code>    </code><code>&gt;&gt;&gt; def func(a, b=2):    </code>

<code>    </code><code>...   print a + b</code>

<code>    </code><code>&gt;&gt;&gt; func(1)</code>

<code>    </code><code>&gt;&gt;&gt; func(1, 3)</code>

<code>    </code><code>4</code>

   5.3.3 接受任意数量参数

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

   单个星号使用:

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(</code><code>*</code><code>a):         </code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>)</code>

<code>    </code><code>(</code><code>1</code><code>, </code><code>2</code><code>, </code><code>3</code><code>)</code>

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

   两个星号使用:

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(</code><code>*</code><code>*</code><code>a):    </code>

<code>    </code><code>&gt;&gt;&gt; func(a</code><code>=</code><code>1</code><code>, b</code><code>=</code><code>2</code><code>, c</code><code>=</code><code>3</code><code>)</code>

<code>    </code><code>{</code><code>'a'</code><code>: </code><code>1</code><code>, </code><code>'c'</code><code>: </code><code>3</code><code>, </code><code>'b'</code><code>: </code><code>2</code><code>}</code>

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

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

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):    </code>

<code>    </code><code>...   </code><code>print</code> <code>args</code>

<code>    </code><code>...   </code><code>print</code> <code>kwargs</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>,a</code><code>=</code><code>1</code><code>,b</code><code>=</code><code>2</code><code>,c</code><code>=</code><code>3</code><code>)</code>

   与普通参数一起使用:

10

11

12

13

14

15

16

17

18

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(a, b, </code><code>*</code><code>c):    </code>

<code>    </code><code>...   </code><code>print</code> <code>c</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>,</code><code>5</code><code>,</code><code>6</code><code>)</code>

<code>    </code><code>(</code><code>3</code><code>, </code><code>5</code><code>, </code><code>6</code><code>)</code>

<code>    </code><code>&gt;&gt;&gt; </code><code>def</code> <code>func(a, b, </code><code>*</code><code>*</code><code>c):</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>,</code><code>2</code><code>,a</code><code>=</code><code>1</code><code>,b</code><code>=</code><code>2</code><code>,c</code><code>=</code><code>3</code><code>)</code>

<code>    </code><code>TypeError: func() got multiple values </code><code>for</code> <code>keyword argument </code><code>'a'</code>

<code>    </code><code>&gt;&gt;&gt; func(</code><code>1</code><code>,</code><code>2</code><code>,c</code><code>=</code><code>3</code><code>,d</code><code>=</code><code>4</code><code>,e</code><code>=</code><code>5</code><code>)</code>

<code>    </code><code>{</code><code>'c'</code><code>: </code><code>3</code><code>, </code><code>'e'</code><code>: </code><code>5</code><code>, </code><code>'d'</code><code>: </code><code>4</code><code>}</code>

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

5.4 作用域

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

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

先看看全局和局部变量:

<code>&gt;&gt;&gt; a </code><code>=</code> <code>2</code>

<code>...   b </code><code>=</code> <code>3</code>

<code>&gt;&gt;&gt; a</code>

<code>2</code>

<code>&gt;&gt;&gt; b</code>

<code>Traceback (most recent call last):</code>

<code>  </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>NameError: name </code><code>'b'</code> <code>is</code> <code>not</code> <code>defined</code>

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

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

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

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

<code>...   </code><code>global</code> <code>b</code>

<code>3</code>

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

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

5.5 嵌套函数

1)不带参数

19

20

21

22

23

<code>...   x </code><code>=</code> <code>2</code>

<code>...   </code><code>def</code> <code>func2():</code>

<code>...     </code><code>return</code> <code>x</code>

<code>...   </code><code>return</code> <code>func2  </code><code># 返回func2函数</code>

<code>&gt;&gt;&gt; func()()</code>

<code>&gt;&gt;&gt; func2()</code>

<code>NameError: name </code><code>'func2'</code> <code>is</code> <code>not</code> <code>defined</code>

<code>&gt;&gt;&gt; </code><code>def</code> <code>func():   </code>

<code>...   x </code><code>=</code> <code>2</code>         

<code>...   </code><code>global</code> <code>func2</code>

<code>...     </code><code>return</code> <code>x </code>

<code>...   </code><code>return</code> <code>func2</code>

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

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

2)带参数

<code>&gt;&gt;&gt; </code><code>def</code> <code>func(a):</code>

<code>...   </code><code>def</code> <code>func2(b):</code>

<code>...     </code><code>return</code> <code>a </code><code>*</code> <code>b</code>

<code>&gt;&gt;&gt; f </code><code>=</code> <code>func(</code><code>2</code><code>)   </code><code># 变量指向函数。是的,变量可以指向函数。</code>

<code>&gt;&gt;&gt; f(</code><code>5</code><code>)</code>

<code>10</code>

<code>&gt;&gt;&gt; func(</code><code>2</code><code>)(</code><code>5</code><code>)</code>

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

<code>...      x </code><code>=</code> <code>3</code>

<code>...   func2()</code>

<code>...   </code><code>return</code> <code>x</code>

<code>...     x </code><code>+</code><code>=</code> <code>1</code>

<code>  </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>5</code><code>, </code><code>in</code> <code>func</code>

<code>  </code><code>File</code> <code>"&lt;stdin&gt;"</code><code>, line </code><code>4</code><code>, </code><code>in</code> <code>func2</code>

<code>UnboundLocalError: local variable </code><code>'x'</code> <code>referenced before assignment</code>

5.6 闭包

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

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

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

5.7 高阶函数

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

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

2)输出一个函数。

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

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

举例说明下高阶函数:

<code>&gt;&gt;&gt; </code><code>def</code> <code>f(x):</code>

<code>...   </code><code>return</code> <code>x </code><code>*</code> <code>x</code>

<code>&gt;&gt;&gt; </code><code>def</code> <code>f2(func, y):</code>

<code>...   </code><code>return</code> <code>func(y)</code>

<code>&gt;&gt;&gt; f2(f, </code><code>2</code><code>)</code>

<code>4</code>

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

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

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

5.8 函数装饰器

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

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

<code>#!/usr/bin/python</code>

<code># -*- coding: utf-8 -*-</code>

<code>def</code> <code>f1(a, b):</code>

<code>    </code><code>print</code> <code>"f1 result: "</code> <code>+</code> <code>str</code><code>(a </code><code>*</code> <code>b)</code>

<code>def</code> <code>f2(a, b):</code>

<code>    </code><code>print</code> <code>"f2 result: "</code> <code>+</code> <code>str</code><code>(a </code><code>*</code> <code>b)</code>

<code>f1(</code><code>1</code><code>, </code><code>2</code><code>)</code>

<code>f2(</code><code>2</code><code>, </code><code>2</code><code>)</code>

<code>f1 result: </code><code>2</code>

<code>f2 result: </code><code>4</code>

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

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

<code>    </code><code>print</code> <code>"f1 parameter: %d %d"</code> <code>%</code><code>(a, b)</code>

<code>    </code><code>print</code> <code>"f2 parameter: %d %d"</code> <code>%</code><code>(a, b)</code>

<code>f1 parameter: </code><code>1</code> <code>2</code>

<code>f2 parameter: </code><code>2</code> <code>2</code>

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

<code>def</code> <code>deco(func):</code>

<code>    </code><code>def</code> <code>f(a, b):</code>

<code>        </code><code>print</code> <code>"%s parameter: %d %d"</code> <code>%</code><code>(func.__name__, a, b)</code>

<code>        </code><code>return</code> <code>func(a, b)</code>

<code>    </code><code>return</code> <code>f</code>

<code>@deco</code>

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

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

   5.8.1 无参数装饰器

<code>    </code><code>方式</code><code>1</code><code>:函装饰器函数装饰函数    </code>

<code>    </code><code>#!/usr/bin/python</code>

<code>    </code><code># -*- coding: utf-8 -*-</code>

<code>    </code><code>def</code> <code>deco(func):</code>

<code>        </code><code>return</code> <code>func</code>

<code>    </code><code>def</code> <code>f1():</code>

<code>        </code><code>print</code> <code>"Hello world!"</code>

<code>    </code><code>myfunc </code><code>=</code> <code>deco(f1)</code>

<code>    </code><code>myfunc()  </code>

<code>    </code><code># python test.py</code>

<code>    </code><code>Hello world!</code>

<code>    </code> 

<code>    </code><code>方式</code><code>2</code><code>:使用语法糖</code><code>"@"</code><code>来装饰函数</code>

<code>    </code><code>@deco</code>

<code>    </code><code>f1()</code>

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

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

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

   2)带参数装饰器

<code>    </code><code>#!/usr/bin/python    </code>

<code>        </code><code>def</code> <code>f(a, b):</code>

<code>            </code><code>print</code> <code>"function name: %s"</code> <code>%</code> <code>func.__name__   </code><code># __name__属性是获取函数名,为了说明执行了这个函数</code>

<code>            </code><code>return</code> <code>func(a, b)   </code><code># 用接受过来的func函数来处理传过来的参数</code>

<code>        </code><code>return</code> <code>f</code>

<code>    </code><code>def</code> <code>f1(a, b):</code>

<code>        </code><code>print</code> <code>a </code><code>+</code> <code>b</code>

<code>    </code><code>f1(</code><code>2</code><code>, </code><code>2</code><code>)</code>

<code>    </code><code>function name: f1</code>

   3)不固定参数

<code>    </code><code>def</code> <code>log(func):</code>

<code>        </code><code>def</code> <code>deco(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):</code>

<code>            </code><code>print</code> <code>"function name: %s"</code> <code>%</code> <code>func.__name__</code>

<code>            </code><code>return</code> <code>func(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs)</code>

<code>        </code><code>return</code> <code>deco</code>

<code>    </code><code>@log</code>

<code>        </code><code>print</code> <code>"f1() run."</code>

<code>    </code><code>f1(</code><code>1</code><code>,</code><code>2</code><code>)</code>

<code>    </code><code>f1() run.</code>

    4)装饰器加参数

<code>    </code><code># 三层函数,调用log函数返回deco函数,再调用返回的函数deco,则返回值是_deco函数</code>

<code>    </code><code>def</code> <code>log(arg):</code>

<code>        </code><code>def</code> <code>deco(func):</code>

<code>            </code><code>def</code> <code>_deco(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs):</code>

<code>                </code><code>print</code> <code>"%s - function name: %s"</code> <code>%</code> <code>(arg, func.__name__)  </code>

<code>                </code><code>return</code> <code>func(</code><code>*</code><code>args, </code><code>*</code><code>*</code><code>kwargs)</code>

<code>            </code><code>return</code> <code>_deco</code>

<code>    </code><code>@log(</code><code>"info"</code><code>)</code>

<code>    </code><code>info </code><code>-</code> <code>function name: f1</code>

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

<code>    </code><code>def</code> <code>fontColor(color):</code>

<code>        </code><code>begin </code><code>=</code> <code>"\033["</code>

<code>        </code><code>end </code><code>=</code> <code>"\033[0m"</code>

<code>        </code><code>d </code><code>=</code> <code>{</code>

<code>            </code><code>'red'</code><code>:</code><code>'31m'</code><code>,</code>

<code>            </code><code>'green'</code><code>:</code><code>'32m'</code><code>,</code>

<code>            </code><code>'yellow'</code><code>:</code><code>'33m'</code><code>,</code>

<code>            </code><code>'blue'</code><code>:</code><code>'34m'</code>

<code>        </code><code>}</code>

<code>            </code><code>print</code> <code>begin </code><code>+</code> <code>d[color] </code><code>+</code> <code>func() </code><code>+</code> <code>end</code>

<code>    </code><code>@fontColor(</code><code>"red"</code><code>)</code>

<code>    </code><code>def</code> <code>f():</code>

<code>        </code><code>return</code> <code>"Hello world!"</code>

<code>    </code><code>@fontColor(</code><code>"green"</code><code>)</code>

<code>    </code><code>def</code> <code>f2():</code>

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

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

5.9 匿名函数

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

在Python中匿名函数是lambda。

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

<code># 普通函数</code>

<code>...   </code><code>return</code> <code>"Hello world!"</code>

<code>&gt;&gt;&gt; </code><code>def</code> <code>func(a, b):</code>

<code>...   </code><code>return</code> <code>a </code><code>*</code> <code>b</code>

<code>&gt;&gt;&gt; func(</code><code>2</code><code>, </code><code>2</code><code>)</code>

<code># 匿名函数</code>

<code>&gt;&gt;&gt; f </code><code>=</code> <code>lambda</code><code>:</code><code>"Hello world!"</code>

<code>&gt;&gt;&gt; f()</code>

<code>&gt;&gt;&gt; f </code><code>=</code> <code>lambda</code> <code>a, b: a </code><code>*</code> <code>b   </code><code># 冒号左边是函数参数,右边是返回值</code>

<code>&gt;&gt;&gt; f(</code><code>2</code><code>, </code><code>2</code><code>)</code>

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

5.10 内置高阶函数

   5.10.1 map()

   语法:map(function, sequence[, sequence, ...]) -&gt; list

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

   例如:

<code>    </code><code>&gt;&gt;&gt; lst </code><code>=</code> <code>[</code><code>1</code><code>,</code><code>2</code><code>,</code><code>3</code><code>,</code><code>4</code><code>,</code><code>5</code><code>]    </code>

<code>    </code><code>&gt;&gt;&gt; </code><code>map</code><code>(</code><code>lambda</code> <code>x:</code><code>str</code><code>(x)</code><code>+</code><code>".txt"</code><code>, lst)</code>

<code>    </code><code>[</code><code>'1.txt'</code><code>, </code><code>'2.txt'</code><code>, </code><code>'3.txt'</code><code>, </code><code>'4.txt'</code><code>, </code><code>'5.txt'</code><code>]</code>

   5.10.2 filter()

   语法:filter(function or None, sequence) -&gt; list, tuple, or string

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

   例如:过滤列表中的奇数

<code>    </code><code>&gt;&gt;&gt; </code><code>filter</code><code>(</code><code>lambda</code> <code>x:x</code><code>%</code><code>2</code><code>=</code><code>=</code><code>0</code><code>, lst)</code>

<code>    </code><code>[</code><code>2</code><code>, </code><code>4</code><code>]</code>

   5.10.3 reduce()

   语法:reduce(function, sequence[, initial]) -&gt; value

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

   例如:计算列表总和

<code>    </code><code>&gt;&gt;&gt; </code><code>reduce</code><code>(</code><code>lambda</code> <code>x,y:x</code><code>+</code><code>y, lst)</code>

<code>    </code><code>15</code>

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

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