从函数对象到名称空间与作用域
函数对象
函数(Function)作为程序语言中不可或缺的一部分。
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。
函数拥有对象的三个通用属性:值、id、类型
def pr_Hi():
print('Hello World!')
print(id(foo)) # 21301264
print(type(foo)) # <class 'function'>
print(foo) # <function pr_Hi at 0x01450810>
函数可以被引用
def pr_Hi():
print('Hello World!')
pr_Hi2 = pr_Hi #引用,赋值
print(pr_Hi2) # <function pr_Hi at 0x03020810>
print(pr_Hi) #<function pr_Hi at 0x03020810>
pr_Hi2() # Hello World!
函数可以放入容器类数据类型
容器对象(list、dict、set等)中可以存放任何对象,函数也可以作存放到容器对象中
def pr_Hi():
print('from pr_Hi')
dic={'func':pr_Hi}
pr_Hi() # from pr_Hi
print(dic['func']) # <function pr_Hi at 0x00BE0810>
dic['func']() # from pr_Hi
函数还可以当做参数
def pr_Hi():
print('from pr_Hi') # from pr_Hi
def bar(func): ##执行函数
print(func) # <function pr_Hi at 0x014E0810>
func() ##调用上面地址的函数
bar(pr_Hi) ##第一步,先调用bar()函数
函数可以作为返回值
def pr_Hi():
print('from pr_Hi')
def bar(func):
return func
f=bar(pr_Hi)
print(f) # <function pr_Hi at 0x00CA0810>
f() # from pr_Hi
函数还可以嵌套
def f1():
def f2():
print('from f2')
return f2
abc = f1() # f1()拿到函数的返回值,函数的返回值是f2, abc就相当于f2
abc() # from f2
匿名函数
python 使用 lambda 来创建匿名函数。
所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda 只是一个表达式,函数体比 def 简单很多。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
- 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
语法
lambda [arg1 [,arg2,.....argn]]:expression
例如
sum = lambda arg1, arg2: arg1 + arg2
# 调用sum函数
print ("相加后的值为 : ", sum( 10, 20 )) # 30
print ("相加后的值为 : ", sum( 20, 20 )) # 40
## 这样就是一个逻辑简单的匿名函数了,不过匿名函数一般不这么使用
匿名函数的用法
匿名函数一般和内置函数一起使用,例如:sort,sorted一起使用
lt = [('a', 3000), ('i', 2600), ('o', 15000), ('d', 6500), ('p', 3000)]
lt.sort()
print(lt) # [('a', 3000), ('d', 6500), ('i', 2600), ('o', 15000), ('p', 3000)]
##这样只能让列表里面的元祖以元祖的第一个元素进行排序
##所以我们需要自己定义一个规则来进行排序
def abc(i): ##你们千万不要学我,我实在想不出来用什么好,就随便使用了
return i[1]
lt = [('a', 3000), ('i', 2600), ('o', 15000), ('d', 6500), ('p', 3000)]
lt.sort(key=abc) ## 可以通过他的key来改变它排序的规则
print(lt) # [('i', 2600), ('a', 3000), ('p', 3000), ('d', 6500), ('o', 15000)]
## 发现使用函数想不出函数名这个比较尴尬,还是使用匿名函数好些
lt = [('a', 3000), ('i', 2600), ('o', 15000), ('d', 6500), ('p', 3000)]
lt.sort(key=abc) ## 可以通过他的key来改变它排序的规则,这样就可以不用考虑函数名
print(lt) # [('i', 2600), ('a', 3000), ('p', 3000), ('d', 6500), ('o', 15000)]
函数空间与作用域
一扯到函数的嵌套就不由自主的要开始扯函数空间与作用域了。
函数空间:存放名字的地方,准确的说名称空间是存放名字与变量值绑定关系的地方
- 内置名称空间:python自带的名字,在python解释器启动时产生,存放一些python内置的名字
- 全局名称空间:在执行文件时,存放文件级别定义的名字
- 局部名称空间:在执行文件的过程中,如果调用了函数,则会产生该函数的名称空间,用来存放该函数内定义的名字,该名字在函数调用时生效,调用结束后失效
名称空间的顺序
- 加载顺序:内置名称空间 --> 全局名称空间 --> 局部名称空间
- 查找顺序:局部名称空间 --> 全局名称空间 --> 内置名称空间
作用域: 产生作用的范围
- 全局作用域:全局存活,全局有效
- 局部作用域:局部存活,局部有效
x = 1
def f2():
x = 2 # x=2只能在f2中使用
print(x)
print(x) # 1
f2() # 2
如果实在要改变全局的变量或者是局部的变量
改全局名称
x = 1
def f1():
global x
x = 2
f1()
print(x) # 2
改局部名称
x = 0
def f1():
x = 1
def f2():
x = 2
def f3():
nonlocal x #改的是函数正上方的值
x = 3
print('f3',x) # f3 3
f3()
print('f2',x) # f2 3
f2()
print('f1',x) # f1 1
print(x) # 0
f1()
推荐使用引用类型
lt = [1,2,3] # 作用域关系仅适用不可变数据类型,不适用于可变数据类型
def f1():
lt.append(4)
print(lt) # [1, 2, 3]
f1()
print(lt) # [1, 2, 3, 4]