# coding = utf-8
'''
函数声明:
def name([arg,... arg = value,... *arg, **kwarg]):
suite
1. 当编译器遇到 def,会生成创建函数对象指令。
也就是说 def 是执⾏行指令,⽽不仅仅是个语法关键字。
可以在任何地⽅方动态创建函数对象。
2. 可以使用默认参数、可变参数和关键字参数
arg = value 是默认参数
*args是可变参数,args接收的是一个tuple;
**kwargs是关键字参数,kwargs接收的是一个dict。
lambda函数
不同于⽤用 def 定义复杂函数,lambda 只能是有返回值的简单的表达式。使⽤用赋值语句会引发语法 错误,可以考虑⽤用函数代替。
'''
'''
*****************************************************
1. 函数创建
函数是第一类对象,可作为其他函数的实参或返回值。
函数总是有返回值。就算没有 return,默认也会返回 None。
*****************************************************
'''
def test1(name):
'''
>>> test1('a').__name__ # 查看函数返回函数的名字
'a'
>>> test1('b').__name__ # 查看函数返回函数的名字
'b'
>>> test1('a')() # 调用函数返回的函数
call function a
>>> print(test1('c')) # 调用函数,返回2个值:整型和字符串
(0, 'test')
'''
if name == "a":
def a():
print('call function a')
return a
elif name == "b":
def b(): pass
return b
else:
return 0, 'test'
'''
*************************************************************
2. 参数
2.1 函数的传参方式灵活多变,可按位置顺序传参,也可不关⼼顺序⽤命名实参。
*************************************************************
'''
def test21(a, b):
'''
>>> test21(1,2) # 位置参数
1 2
>>> test21(b=3,a=4) # 命名参数
4 3
'''
print(a, b)
'''
*************************************************************
2.2 ⽀持参数默认值。不过要⼩⼼,
默认值对象在创建函数时生成,所有调用都使⽤同⼀对象。
如果该默认值是可变类型,那么就如同 C 静态局部变量。
*************************************************************
'''
def test22(x, lst=[]):
'''
>>> test22(1)
[1]
>>> test22(2) # 保持了上次调⽤用状态。
[1, 2]
>>> test22(3, []) # 显式提供实参,不使⽤用默认值。
[3]
>>> test22(3) # 再次使⽤用默认值,会继续使用默认的列表对象
[1, 2, 3]
'''
lst.append(x)
return lst
'''
*************************************************************
2.3 默认参数后⾯不能有其他位置参数,除非是变参。
SyntaxError: def test23(a, b=1, c): pass
2.4 用 *args 收集 "多余" 的位置参数,**kwargs 收集 "额外" 的命名参数。
这两个名字只是惯例,可 ⾃自由命名。
*args是可变参数,args接收的是一个tuple;
**kwargs是关键字参数,kwargs接收的是一个dict。
*************************************************************
'''
def test24(a, b=1, *args, **kwargs):
'''
>>> test24(0)
0
1
()
{}
>>> test24(0,2,3,4)
0
2
(3, 4)
{}
>>> test24(0,2,3,4,x=5)
0
2
(3, 4)
{'x': 5}
# 可 "展开" 序列类型和字典,将全部元素当做多个实参使⽤用。如不展开的话,那仅是单个实参对象。
>>> test24(*range(5), **{'x': 10, 'y': 11})
0
1
(2, 3, 4)
{'x': 10, 'y': 11}
>>> test24(range(5))
range(0, 5)
1
()
{}
# 单个 "*" 展开序列类型,或者仅是字典的主键列表。
# "**" 展开字典键值对。但如果没有变参收集, 展开后多余的参数将引发异常。
>>> p = dict(a=20,b=21)
>>> test24(p)
{'a': 20, 'b': 21}
1
()
{}
>>> test24(*p) # 仅展开 keys(),test("a"、"b")
a
b
()
{}
>>> test24(**p) # 展开 items(),test(a = 1, b = 2)。
20
21
()
{}
'''
print(a)
print(b)
print(args)
print(kwargs)
'''
*************************************************************
3. 作用域
3.1 函数形参和内部变量都存储在 locals 名字空间中。
*************************************************************
'''
def test31(a, b=1):
'''
>>> test31(100)
{'s': 'Hello Python', 'b': 1, 'a': 100}
'''
s = 'Hello Python'
print(locals())
'''
*************************************************************
3.2 除⾮使用 global、nonlocal 特别声明,
否则,在函数内部使用赋值语句,总是在 locals 名字空间中 新建一个对象关联。
注意:"赋值" 是指名字指向新的对象,⽽⾮通过名字改变对象状态。
名字查找顺序: locals -> enclosing function -> globals -> __builtins__
• locals: 函数内部名字空间,包括局部变量和形参。
• enclosing function: 外部嵌套函数的名字空间。
• globals: 函数定义所在模块的名字空间。
• __builtins__: 内置模块的名字空间。
如果不修改全局变量也可以不使用global关键字。
Python3 提供了 nonlocal 关键字,用来修改外部 嵌套函数名字空间, python2.7 没有。
nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。
*************************************************************
'''
x, y = 1, 2 # 在模块级别直接定义的全局变量
def test321():
global x # 引用全局变量
x = 11
y = 21 # 这里的 y 是局部变量
global z # 在函数内部定义一个全局变量,而没有在模块级别定义
z = 3
print(' in test321(): x=%d; y=%d; z=%d' % (x, y, z))
def test322():
print(' in test322(): x=%d; y=%d ; z=%d' % (x, y, z)) # 直接访问所有全局变量,包括变量z
# 测试
print(' out 1: x=%d; y=%d; z=undefine' % (x, y))
test321()
test322()
print(' out 2: x=%d; y=%d ; z=%d' % (x, y, z))
'''
*************************************************************
3.3 闭包
闭包(closure)是函数式编程的重要的语法结构。
闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
*************************************************************
'''
def lineConfig(a,b):
'''
这个例子中,函数line与环境变量a,b构 成闭包。
在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,
这样我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。
我们只需要变换参数a,b,就可以获得不同的直线表达函数。
由此,我们可以看到,闭包也具有提高代码可复用性的作用。
>>> y1 = lineConfig(1, 1) # 定义直线 y = x + 1
>>> p1 = y1(2) # 计算y坐标,获取x=2时y1的值
>>> print(p1)
3
>>> lstPoint = [y1(x) for x in range(3)] # 计算y坐标列表
>>> print(lstPoint)
[1, 2, 3]
>>> y2 = lineConfig(4, 5) # 定义直线 y = 4x + 5
>>> p2 = y2(x=2) # 计算y坐标,获取x=2时y2的值
>>> print(p2)
13
'''
def line(x):
return a*x + b
return line
def newCounter(i=0):
'''
两个独立的计数器
>>> c1 = newCounter()
>>> c2 = newCounter(2)
>>> print(c1(),c1(),c1())
1 2 3
>>> print(c2(),c2(),c2())
3 4 5
'''
def counter():
nonlocal i
i = i + 1
return i
return counter
if __name__ == '__main__':
import doctest
doctest.testmod(verbose=True)