本篇索引

(3)作用域

(4)递归

(1)函数基本

● 函数是第一类对象

Python中万物皆对象,所有对象都是第一类的(first class),函数也不例外,也是第一类对象。既然是对象,那就可以当作普通的对象数据处理,比如:拥有自己的属性、可赋值给一个变量、可放入容器存储、可作为另一个函数的参数或是返回值等等。当你在使用 def 定义函数时,就相当于生成了一个函数对象。

下例中,将3个内置函数放入一个序列,并用迭代器取出后分别调用:

line = 'abc,1,3.14'

fun_list = [str, int, float]

para_list = line.split(',')

obj_list = [f(val) for f, val n zip(fun_list, para_list)]

# obj_list结果为:['abc', 1, 3.14]

● 文档字符串

通常,将函数def后的第一行,作为描述函数用途的“文档字符串”,保存在函数的__doc__属性中。用内置函数 help(函数名) 也可以查看函数的描述文档。

当函数使用装饰器时,有可能会破坏与文档字符串相关的帮助功能,解决这个问题的办法是 在装饰器函数定义中,手工设置函数的文档字符串和函数名称,如下例所示:

def mywrap(func):

def callf(*args, **kwargs):

return func(*args, **kwargs)

callf.__doc__ = func.__doc__

callf.__name__ = func.__name__

return callf

@mywrap

def myfunc():

"""文档字符串内容"""

...

由于这是一个常见问题,因此 functools 模块提供了函数装饰器函数 wraps,用于自动复制这些属性, 使用方法如下例所示:

from functools import wraps

def mywrap(func):

@wraps(func)

def callf(*args, **kwargs):

return func(*args, **kwargs)

return callf

@mywrap

def myfunc():

"""文档字符串内容"""

...

● 可调用类型

可调用类型表示支持函数调用操作的对象,包括:用户定义的函数、内置函数、实例方法、类、提供了可调用接口的实例。可以使用内置函数 callable() 来检查一个对象是否是可调用的。

类都是是可以调用的,调用类时,会自动将入参传递给类的__init__()方法,用以创建一个新实例。

实例对象一般是不可调用的,但是如果这个实例实现了__call__()方法,那么这个实例就可直接调用。例如:若x是某个实例,执行 x(args) 就相当于调用 x.__call__(args) 方法。

● 函数的属性

函数作为一种对象,理论上可以给函数添加任意属性。函数属性主要保存在函数的__dict__属性中, __dict__属性是一个字典。函数也有一些内部默认的属性,见下列各表。

“内置函数”具有以下属性

属性描述

__doc__

文档字符串。

__name__

函数名称。

__self__

与方法相关的实例。 说明:对于像len()这样的内置函数,__self__为None(表明未绑定);而像s.append()这样的内置方法,__self__为列表对象s。

“用户定义函数”具有以下属性

属性描述

__doc__

文档字符串。

__name__

函数名称。

__dict__

包含函数属性的字典。

__code__

编译后的代码。

__defaults__

包含默认参数的元组。

__globals__

函数应用时对应的全局命名空间的字典。

__clousre__

闭包(包含与嵌套作用域相关数据的元组)

“实例方法”具有以下属性

属性描述

__doc__

文档字符串。

__name__

方法名称。

__class__

定义该方法的类。

__func__

实现方法的函数对象

__self__

与方法相关的实例(如果是非绑定方法,则为None)

和文档字符串一样,函数属性也有混合装饰器后丢失的问题。因为使用装饰器包装函数, 实际上是访问装饰器函数的属性而不是原始函数的属性。解决方法也是类似的, 用户在装饰器函数中需要手动把原始函数的属性赋值给装饰器函数,如下例所示:

def mywrap(func):

def callf(*args, **kwargs):

return func(*args, **kwargs)

callf.__doc__ = func.__doc__

callf.__name__ = func.__name__

callf.__dict__.update(func.__dict__)

return callf

@mywrap

def myfunc():

"""文档字符串内容"""

...

● 绑定与非绑定方法

通过实例调用方法时,有绑定和非绑定两种用法。绑定方法封装了成员函数和一个对应实例,调用绑定方法时,实例会作为第一个参数self自动传递给方法。而非绑定方法仅封装了成员函数,并没有实例,用户在调用非绑定方法时,需要显式地将实例作为第一个参数传递进去。详见下面2个例子

绑定用法(bound method):

class Foo():

def meth(self, a):

print(a)

obj = Foo() # 创建一个实例

m = obj.meth # 将meth方法绑定到obj这个实例上

m(2) # 调用这个方法时,Python会自动将obj作为self参数传递给meth()方法

非绑定用法(unbound method):

class Foo():

def meth(self, a):

print(a)

obj = Foo() # 创建一个实例

um = Foo.meth # 非绑定,仅仅是将这个方法赋值给um,并不需要实例存在

um(obj, 2) # 调用这个非绑定方法时,用户需要显式地传入obj实例作为第一个参数。

● 匿名函数

使用 lambda 语句可以创建表达式形式的匿名函数,其用途是指定短小的回调函数。语法为:

lambda 参数 : 表达式

lambda匿名函数中不能出现非表达式语句、也不能出现多行语句。

下例定义一个匿名函数:

a = lambda x,y: x*y

b = a(2,5) # b的结果为:10

下例在序列的sort()方法中传入lambda匿名函数:

[('b',2),('a',1)].sort(key=lambda x:x[1]) # 结果为 [('a',1),('b',2)]

# 说明:使用匿名函数对列表元素进行了预处理,将原本的元组('a',1)预处理为:取出元组中后一个元素(即:1),所以能够进行排序。

(2)函数参数

● 位置参数、关键字参数、返回值

在调用函数时,传入参数的顺序数量必须与函数定义匹配,否则会引发TypeError异常。如果在调用时指定参数名,这样就不必遵照函数定义中的参数顺序了,这样可大大增加调用时的可读性。这种指定参数名传入的参数叫做关键字参数,一般的未指定参数名的参数叫做位置参数。关键字参数只能放在所有的位置参数后面。

函数参数都是按值传递的,但是如果传递的是对象(即非单纯数字),所谓按值传递就是函数参数仅仅是将对象的地址值复制一下传过去而已,所以在函数中其实是可以改变外面对象中的内容的。一般最好避免使用这种风格,而且在涉及线程和并发的程序中,使用这类函数的效率很低,因为通常需要使用线程锁来防止副作用的影响。

若省略return语句、或单一个return关键字,就会返回 None 对象。

● 参数的默认值

在函数定义时,可以为某些参数指定默认值,这样在调用时可以不提供这个参数了。一旦出现带默认值的参数,此参数后续的参数都必须带默认值,否则会引发SyntaxError异常。

建议不要使用可变对象作为默认值(如空列表),这样可能导致意外的bug,如下例所示:

def fun(x, seq=[]):

seq.append(x)

return seq

fun(1) # 返回[1]

fun(2) # 返回[1,2]

fun(3) # 返回[1,2,3]

上例的本意是若未传入seq参数,则新建一个列表,并将x放入新列表。但是事实上会产生bug。这种情况建议使用seq=None,再在函数中建新列表。

● 单星号 *

在函数定义时,参数前加单星号的意思为:收集其余的位置参数,并将它们放入同一个元组中。这样在函数调用时,用户就可以提供任意多个参数了。如下例所示:

def fun(x, *y):

print(x)

print(y)

fun('a', 1, 2, 'c') # 结果为:a 和 (1,2,'c')

fun(3) # 结果为:3 和 ()

单星号亦可反转使用,即:在调用函数时使用*,自动将一个元组展开为若干个指定名字的关键字参数。

def myadd(x, y):

return x+y

t = (1,2)

myadd(*t) # 调用时,单星号自动将元组(1,2)展开为 x, y

● 双星号 **

在函数定义时,参数前加双星号的意思为:收集其余的关键字参数,并将它们放入一个字典中。这样在调用函数时,可以传入大量可扩充的配置项作为参数。

def fun(x, **z):

print(x)

print(z)

fun(x=1, y=2, z=3) # 结果为:1 和 {'y':2, 'z':3}

双星号亦可反转使用,在调用函数时使用**,自动将一个字典拆分为若干个指定名字的关键字参数。

def myadd(x, y):

return x+y

d = {'x':1, 'y':2}

myadd(**d) # 调用时,双星号自动将字典d展开为:x=1, y=2

单星号和双星号可组合使用,用于同时收集位置参数和关键字参数,**参数必须出现在*参数的后面。

def fun(*args, **kwargs):

print(args)

print(kwargs)

fun(1,2,3, x=4,y=5,z=6)

# 结果为:(1,2,3) 和 {'x':4, 'y':5, 'z':6 }

(3)作用域

每次执行一个函数时,就会创建一个新的局部命名空间。这个命名空间(又叫作用域),就像其内部有一个“不可见”的字典,其中包含本函数参数的名称和所有在函数内部定义的局部变量。

除了每个函数有有一个局部作用域以外,还有一个全局作用域。可以通过内置函数locals()和globals()查看局部和全局作用域字典,内建函数vars(obj)可以返回某个实例的作用域字典。

x = 1

def fun():

y = 2

print(locals())

print(globals())

fun()

# locals()的显示为:{'y':2}

# globals()除了显示全局变量x以外,还会显示很多全局默认的变量:

{ 'x': 1,

'fun':

'__name__': '__main__',

'__doc__': None,

'__package__': None,

……

}

● 在函数内访问全局变量

Python解释器解析变量时,会先搜素局部作用域,若找不到就会搜索全局作用域,若再找不到就会搜索内置命名空间,如果仍然找不到,就会引发NameError异常。

此种方法虽然可以访问全局变量,但不能对全局变量赋值,若要赋值全局变量,需要使用global关键字。

在函数中修改全局变量:

x = 1

def fun():

global x

x = 2

fun() # 运行fun()后,全局变量 x 的值变为2

函数中局部变量名和全局变量名重复时:

x = 1

def fun():

x = 2 # 此处创建一个名为 x 的局部变量

globals()['x'] = 3 # 使用globals()内置函数,可通过字典的方式直接操作全局变量

fun() # 运行fun()后,全局变量 x 的值变为3

注意:Python中不支持访问在函数中访问上级调用函数的局部作用域,如下例的代码会引发NameError异常

def fun_inner():

print(x)

def fun_outer():

x = 2

fun_inner()

fun_outer() # 调用时会引发NameError异常,因为在fun_inner()函数中,不能访问外层调用函数fun_outer()的局部作用域中的 x

● 嵌套作用域

Python3支持在嵌套定义的函数中,使用外层定义函数(不是外层调用函数)的局部作用域中的变量,这个称为:动态作用域(dynamic scoping),需要使用nonlocal关键字,如下例所示:

def fun_outer():

x = 4

def fun_inner():

nonlocal x # 声明绑定到外层定义的x

x = 5

print(x)

fun_inner()

print(x)

fun_outer() # 会先运行fun_inner()中的print语句,打印出5;然后运行外层中的print语句,可看到x确实被修改成了5

(4)递归

函数调用自身称为递归(recursion)。递归最典型的使用场景是,可以把一个大问题分解成重复的小问题来解决,而且这个小问题的解决结构非常简洁。虽然大部分的递归都可以用循环来替代解决,但有时用递归写函数要比循环可读性更高,看起来也更优雅。

用递归计算n的阶乘:

def factorial(n):

if n <= 1:

return 1

else:

return n * factorial(n-1)

用递归实现序列的二分法查找:

def search(seq, num, lower, upper):

if lower == upper:

return upper

else:

middle = (lower + upper) // 2

if num > seq[middle]:

return search(eq, num, middle+1, upper)

else:

return search(seq, num, lower, middle)

seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

seq.sort()

search(seq, num=2, lower=0, upper=9) # 使用二分法查找比起遍历查找,可以很快地找到num

● 递归的深度

Python对递归函数调用的深度做了限制,函数 sys.getrecursionlimit() 返回当前最大的递归深度, 而函数 sys.setrecursionlimit() 可以修改递归深度(默认值为1000)。当超出递归深度时, 就会引发 RuntimeError 异常。Python不会进行尾递归优化。

还要小心混合使用递归和装饰器的问题,如果对递归函数使用装饰器, 所有内部的递归调用都会通过装饰后的版本进行。如果使用装饰器的目的是进行一些系统管理 (如同步或锁定),最好不要使用递归。

python函数速查_Python语法速查: 7. 函数基础相关推荐

  1. python语法基础知识案例_Python 语法速览与实战清单

    本文是对于 现代 Python 开发:语法基础与工程实践的总结,更多 Python 相关资料参考 Python 学习与实践资料索引:本文参考了 Python Crash Course - Cheat ...

  2. python函数的作用域_python学习第五篇 函数 变量作用域

    原博文 2019-07-18 23:40 − 函数 函数是组合好的,可以重复使用的,用来实现单一或相关联功能的代码片段作用 能提高应用的模块性和代码的重复利用率函数的创建 第一函数的规则 1.函数代码 ...

  3. python函数调用的例子_Python案例|混用C函数

    今天,我们来解决python的速度问题.我们平时所说的Python是指在PC机上的CPython,换句话说,Python的解释器是用C语言编写的.为Python编写C扩展模块能够显著提高Python的 ...

  4. python函数不定参数_python如何定义不定参数函数

    *args,可以传入任意多个参数 **args,以字典形式传入任意多个参数 元组形式: 1.定义函数 def test1(*args): print('################test1### ...

  5. python 运行时间 装饰器_python 装饰器统计某个函数的运行时间

    import datetime def count_time(func): def int_time(*args, **kwargs): start_time = datetime.datetime. ...

  6. python高阶函数看不懂_Python进阶:高阶函数的详细说明

    这篇文章讲述了Python进阶:高阶函数的详细说明有需要的朋友可以参考 函数式编程 函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单 ...

  7. Matlab自学笔记四:调用函数基本方法、传递参数、函数工作区、命令语法、调用匿名函数

    1.调用函数的基本方法 题目:创建函数y=x²+x+1 首先,使用上一篇的创建函数文件的方法(推荐先看上一篇:Matlab创建函数文件.函数语法定义.匿名函数.多元函数.局部函数等知识串联理解),创建 ...

  8. python录音详解_Python实现电脑录音(含音频基础知识讲解)

    咪哥杂谈 本篇阅读时间约为 6 分钟. 1 前言 今天开始进入近期系列文章的第一篇,如何用 Python 来实现录音功能. 在开始"造轮子"之前,个人一直强调一个观点,如果有些东西 ...

  9. python rfind函数用法_Python语法速查:字符串格式简单处理、子串查找与判断方法?...

    这是一篇python基础知识分享型文章,对学习python感兴趣的朋友们可以仔细看看 字符串常用方法 Python3中,字符串全都用Unicode形式,所以省去了很多以前各种转换与声明的麻烦.字符串属 ...

最新文章

  1. 视频数据复用光端机故障排除方法
  2. 【js】数组置空的其他方式及使用场景
  3. github比sourceforge好在哪?
  4. Mail.Ru Cup 2018 Round 1 virtual participate记
  5. Mysql CONCAT FIND_IN_SET用法
  6. 企业计算机仿真技术应用,基于计算机仿真技术的企业生产物流系统优化研究
  7. 几款开源的Windows界面库
  8. SaxReader解析xml文件--绕过多层for循环
  9. demonstration记忆_单词记忆法
  10. 免费杀毒软件真的能杀木马吗?
  11. Android 系统(93)---android 怎么判断手机号是移动还是联通还是电信
  12. 阿里云天池机器学习训练营(Day7, Day8):机器学习算法(三):K近邻(k-nearest neighbors)初探
  13. 治疗口腔溃疡的穴位按摩方法
  14. 微电子新手入门之 Origin画图
  15. ie的js调试工具companion.js使用方法
  16. H3C--网络设备基本配置与调试
  17. Luma推出分布式路由器,哪里有WIFI死角放哪里
  18. DIY:制作一个语音识别的空调遥控器
  19. Intel Distiller工具包-量化实现3
  20. ICH4芯片LAN控制器寄存器

热门文章

  1. 机器学习中,有哪些特征选择的工程方法?
  2. 框架+重量级+轻量级
  3. 对偶问题(dual problem)
  4. A networkIntegration Approach for Drug-Target interaction Prediction and ComputationalDrug Repositio
  5. go语言----函数 结构体 接口 多态
  6. java 邮件 内容_JAVA怎么获取邮件内容
  7. php正则过滤html标签_空格_换行符的代码,php正则过滤html标签、空格、换行符的代码(附说明)_php技巧...
  8. javascript es6 module 模块 的使用
  9. tkinter的函数与实例
  10. Jupyter Notebook中cell操作