1、函数vs过程
过程是简单、特殊、没有返回值的函数。Python的过程就是函数,过程默认返回None。
2、调用函数
(1)函数操作符(())
(2)关键字参数
让调用者通过函数调用中的参数名字来区分参数,允许参数缺失或者不按顺序。
def net_conn(host,port):
net_conn_suite
net_conn('kappa',8080)
net_conn(port=8080,host='chino')
(3)默认参数
默认参数是声明了默认值的参数,允许调用者不向该参数传入值。
(4)参数组
把元组(非关键字参数)或字典(关键字参数)作为参数组传给函数。
func(positional_args,keyword_args,*tuple_grp_nonkw_args,**dict_grp_kw_args)
- positional_args:位置参数
- keyword_args:关键字参数
- tuple_grp_nonkw_args:非关键字参数组(以元组形式体现)
- dict_grp_kw_args:关键字参数组(以字典形式体现)
3、创建函数
(1)def语句
def function_name(args):
"function_documentation_string"
function_body_suite
(2)声明vs定义
Python中,将声明和定义视为一体,函数的子句由声明的标题行以及随后的定义体组成的。
(3)前向引用
Python中不存在前向引用问题(C语言存在,对函数进行的调用必须在函数的定义之后)。
Python中不允许在函数未声明之前,对其进行引用或者调用。
(4)函数属性(2.1)
句点属性标识,命名空间
函数属性是Python中使用句点属性标识并拥有名称空间的一个领域。
def bar():
pass
bar.__doc__='***'
bar.version=0.1
不能在函数的声明中访问属性,因为此时函数体还没有被创建。
(5)内部/内嵌函数(C#内联函数)
在函数体内创建另一个函数(对象)是完全合法的,这种函数叫做内部/内嵌函数。
闭包:内部函数中包含了在外部函数甚至外部函数之外定义的对象的引用,内部函数会被称为闭包。
(6)函数 (与方法)装饰器
装饰器是在函数调用之上的修饰。
@decorator(dec_opt_args)
def func2Bdecorated(func_opt_args):
2.2中静态方法和类方法语法:
class MyClass(object):
def staticFoo():
...
staticFoo = staticmethod(staticFoo)
使用装饰器的代码:
class MyClass(object):
@staticmethod
def staticFoo():
...
装饰器可以“堆叠”起来:
@deco2
@deco1
def func(arg1,arg2,...):
pass
等价于创建 一个组合函数:
def func(arg1,arg2,...):
pass
func=deco2(deco1(func))
有参数和无参数的装饰器:
@deco
def foo():pass
=>foo=deco(foo)
@decomaker(deco_args)
def foo():pass
=>foo=decomaker(deco_args)(foo)
@deco1(deco_arg)
@deco2
def func():pass
=>func=deco1(deco_arg)(deco2(func))
AOP(Aspect Oriented Programming,面向方面编程)
- 引入日志
- 增加计时逻辑来检测性能
- 给函数加入事务的能力
- 时戳装饰/时戳服务器
- 在装饰器中置入通用功能的代码来降低程序复杂度。
4、传递函数
Python中函数就像其他对象,函数可以被引用(访问或者以其它变量作为其别名),也可以作为参数传入函数,以及可以作为列表和字典等容器对象的元素。函数同其他对象区别开来的特征是函数可以调用。
5、Formal Arguments(形参)
Python函数的形参集合包括所有必要参数、关键字参数和所有默认值。
(1)位置参数
无论何时调用函数,都必须提供函数的所有位置参数。可以不按位置地将关键字参数传入函数,给出关键字来匹配其在参数列表中的合适的位置是被准予的。
(2)默认参数
所有位置参数必须出现在任何一个默认参数之前。
6、可变长度的参数
(1)非关键字可变长参数(元组)
使用*符号来指定元组。
可变长的参数元组必须在位置参数和默认参数之后。
(2)关键字变量参数(字典)
使用**符号来指定字典。
1.6之前,变长对象只能通过apply()函数传递给被调用函数。
7、函数式编程
(1)匿名函数与lambda表达式
lambda [arg1 [,arg2,...,argN]]:expression
Python允许用lambda关键字创造匿名函数。
一个完整的lambda语句代表了一个表达式,这个表达式的定义体必须和声明放在同一行。
def true():return True <=>lambda:True
def add(x,y):return x+y <=>lambda x,y:x+y
def usuallyAdd2(x,y=2):return x+y <=>lambda:x,y=2:x+y(支持默认参数)
def showAllAsTuple(*z):return z <=>lambda *z:z(支持可变参数)
(2)内建函数
①apply()
apply(func[,nkw][,kw])未来版本中会消失。
②filter()过滤器函数
filter(func,seq)给定一个对象的序列和一个“过滤”函数,每个序列元素都通过这个过滤器进行筛选,保留函数返回为真的对象。
③map()
map(func,seq1[,seq2…])
map()将函数调用“映射”到每个序列的元素上,并返回一个含有所有返回值的列表。
map(None,[1,3,5],[2,4,6])=>[(1,2),(3,4),(5,6)]
zip([1,3,5],[2,4,6])=>[(1,2),(3,4),(5,6)]
④reduce()
reduce(func,seq[,init])
reduce使用一个二元函数,一个序列和一个可选的初始化器,卓有成效地将那个列表的内容“减少”为一个单一的值。(“折叠”)
reduce(func,[1,2,3])≡func(func(1,2),3)
(3)偏函数应用(2.5)
functools模块
currying概念:一个带n个参数,curried的函数固化第一个参数为固定参数,并返回另一个带有n-1个参数的函数对象。
分别类似于LISP的原始函数car和cdr的行为。currying能泛化成为偏函数应用(partial function application,PFA)这种函数将任意数量(顺序)的参数的函数转化成另一个带剩余参数的函数对象。
add1=partial(add,1)=>add1(x)==add(1,x)
mul100=partial(mul,100)=>mul100(x)==mul(100,x)
使用带关键字参数的PFA。
baseTwo=partial(int,base=2)=>baseTwo('10010')==int('10010',base=2)(关键字参数总是出现在形参之后)
PFA优秀应用:提供“部分GUI模块化”(部分类实例化)
8、变量作用域
(1)全局变量与局部变量
搜索标识符的顺序:先从局部作用域开始,再到全局域。未找到,抛出NameError异常。
(2)global语句
局部变量会覆盖同名的全局变量。
明确地引用一个已命名的全局变量,必须使用global语句。
global var1[,var2][,...varN]
(3)作用域的数字
2.1之前,虽然存在多个函数的嵌套,但不能访问超过两个作用域:一个函数的局部作用域和全局作用域。
(4)闭包
Python的静态嵌套域。内部函数。
在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被称为闭包(closure)。定义在外部函数内的但由内部函数引用或者使用的变量被称为*变量。闭包也是函数,但它们能携带一些额外的作用域。(”流浪”的作用域,不属于全局名称空间域或者局部的)闭包对于安装计算、隐藏状态和在函数对象和作用域中随意地切换是很有用的。闭包在GUI或者很多API支持回调函数的事件驱动编程中也很有些用处。闭包更适合需要一个必需有自己作用域的回调函数的情况。
使用函数的func_closure属性来追踪*变量。
(5)作用域和lambda
一个lambda表达式定义一个只有它自己才能访问的局部作用域。
9、生成器
生成器的动机:在迭代中以某种方式生成下一个值并且返回;协同程序(协同程序是可以运行的独立函数调用,可以暂停或者挂起,并从程序离开的地方继续或者重新开始。)
挂起返回出中间值并多次继续的协同程序被称为生成器。2.2中加入生成器,2.3成为标准,2.5得到提高。
生成器是一个带yield语句的函数,生成器通过yield语句可以暂停并返回一个中间的结果。当达到一个真正的返回或者函数结束没有更多的值返回,一个StopIteration异常就会被抛出。
10、其他
Python不支持函数重载,所以需要使用type()这个内建函数作为代理,来处理有着不同参数类型的函数的多重声明以模拟类C语言的函数重载。
Python支持静态地嵌套域(在2.1中引入并在2.2时成为标准),使得内部函数很有用。这之前的版本中,内部函数没什么意义,因为那些版本只支持全局和一个局部域。