本文是对python中的函数语法的一个简单总结回顾,主要包括命名空间和作用域,匿名函数 闭包 等 。
目录
命名空间和作用域
匿名函数
闭包
扩展调用语法和args 、**kwargs
内置函数
enumerate
zip函数
id函数
sorted函数
函数是Python中最主要也是最重要的代码组织和复用手段。
函数使用def关键字声明,用return关键字返回值; 同时拥有多条return语句也是可以的。如果到达函数末尾时没有遇到任何一条return语句,则返回None。
def my_function(x, y, z=1.5):
if z > 1:
return z * (x + y)
else:
return z / (x + y)
# 函数可以有一些位置参数(positional)和一些关键字参数(keyword)。关键字参数通常用于指定默认值或可选参数。在上面的函数中,x和y是位置参数,而z则是关键字参数。
print(my_function(5,6,z=20))
print(my_function(3.14,7,3.5))
print(my_function(10,20))
命名空间和作用域
函数可以访问两种不同作用域中的变量:全局(global)和局部(local)。Python有一种更科学的用于描述变量作用域的名称,即命名空间(namespace)。 注意命名空间描述的是变量的作用域。
任何在函数中赋值的变量默认都是被分配到局部命名空间(local namespace)中的。局部命名空间是在函数被调用时创建的,函数参数会立即填入该命名空间。在函数执行完毕之后,局部命名空间就会被销毁(会有一些例外的情况,具体请参见后面介绍闭包的那部分)。
返回多个值,python的函数可以返回多个值 。
def f():
a,b,c=5,6,7
return a,b,c
x,y,z=f()
print(x,y,z)
# 该函数其实只返回了一个对象,也就是一个元组,最后该元组会被拆包到各个结果变量中
输出: (5, 6, 7)
由于Python函数都是对象,因此,在其他语言中较难表达的一些设计思想在Python中就要简单很多了。
案例: # 为了得到一组能用于分析工作的格式统一的字符串,需要做很多事情:去除空白符、删除各种标点符号、正确的大写格式等。
states = [' Alabama ', 'Georgia!', 'Georgia', 'georgia', 'FlOrIda','south carolina##', 'West virginia?']
#做法之一是使用内建的字符串方法和正则表达式re模块:
import re
def clean_strings(strings):
result=[]
for value in strings:
value=value.strip() # 去除空白符
value=re.sub('[!#?]','',value) # 注意 re.sub去除里面的标点符号
value=value.title() # 转换为标题形式的,开头大写
result.append(value)
return result
print(clean_strings(states))
#输出:
['Alabama',
'Georgia',
'Georgia',
'Georgia',
'Florida',
'South Carolina',
'West Virginia']
做法2:
# 其实还有另一种不错的办法 ,将需要在一组给定字符串上执行的所有运算 做成一个列表
def remove_punction(value):
return re.sub('[!#?]','',value)
clean_ops=[str.strip,remove_punction,str.title]
def clean_strings(strings,ops):
result=[]
for value in strings:
for function in ops:
value=function(value)
result.append(value)
return result
clean_strings(states,clean_ops)
函数可以作为参数传过去,因为在python 里本质上是对象。按上述做法2 相较于做法1 ,这种多函数模式使你能在很高的层次上轻松修改字符串的转换方式。此时的clean_strings也更具可复用性 。
还可以将函数用作其他函数的参数,比如内置的 map函数,它用于在一组数据上应用一个函数:
for x in map(remove_punction,states):
print(x)
# 输出
Alabama
Georgia
Georgia
georgia
FlOrIda
south carolina
West virginia
匿名函数
Python支持一种被称为匿名的、或lambda函数。它仅由单条语句组成,该语句的结果就是返回值。它是通过lambda关键字定义的。如:
def short_function(x):
return x * 2
#用lambda等价表达
equiv_anon = lambda x: x * 2 #
直接传入lambda函数比编写完整函数声明要少输入很多字(也更清晰),甚至比将lambda函数赋值给一个变量还要少输入很多字,更简洁干净。
# 假设有一组字符串,你想要根据各字符串不同字母的数量对其进行排序:
strings = ['foo', 'card', 'bar', 'aaaa', 'abab']
# 传入一个 lambda 函数到列表的 sort 方法
strings.sort(key=lambda x: len(set(list(x))))
print(strings)
输出: ['aaaa', 'foo', 'abab', 'bar', 'card']
闭包
返回函数的函数 。闭包并不是什么很可怕的东西,如果用对了地方,他们其实可以很强大。简而言之,闭包就是由其他函数动态生成并返回的函数。其关键性质是: 被返回的函数可以访问其创建这的局部命名空间中的变量
示例:
def make_closure(a):
def closure():
print('I know the secret: %d '%a)
return closure
closureBibao=make_closure(5)
print(closureBibao) #<function closure at 0x7f3a75adf2a8>
print(closureBibao())
#输出:
<function closure at 0x7f3a75adf410>
I know the secret: 5
None
注意: 闭包和标准python函数之间的区别在于 : 即使其创建者已经执行完毕,闭包仍能继续访问其创建者 的局部命名空间。因此,在上面的这种情况中,对于返回的闭包只要调用将可打印出 ”I know the secret: 5 “。 虽然闭包的内部状态(在本例中,只有值a)一般都是静态的,但也允许使用可变对象(如字典、集合、列表等可以被修改的对象)。 例如下面这个函数可以返回一个能够记录其参数(曾经传入的一切参数)的函数。
# 这个函数可以返回一个能够记录其参数(曾经传入的一切参数)的函数. 注意这个例子,可以好好回味下
def make_watcher():
have_seen={}
def has_been_seen(x):
if x in have_seen:
return True
else:
have_seen[x]=True
return False
return has_been_seen
# 对一组整数使用该函数,可以得到
watcher=make_watcher()
vals=[5,6,1,5,1,6,3,5]
[watcher(x) for x in vals] # 对于闭包,可以继续调用 。仍然能访问闭包内部创建的局部命名空间 have_seen ,因此所有被传进去的值都在这个字典里 被记录下来了。
# 输出 [False, False, False, True, True, True, False, True]
# 需要注意一个技术限制 , 虽然可以修改任何内部状态对象(比如向字典添加键值对),但不能绑定外层函数作用域中的变量。一个解决办法是: 修改字典或列表,而不是绑定变量。
def make_counter():
count=[0]
def counter():
# 增加并返回当前的count
count[0]+=1
return count[0]
return counter
counter=make_counter
print(counter)
# <function make_counter at 0x7f3a75adf758>
注意 闭包到底有什么用? 在实际工作中,可以编写带有大量选项的非常一般化的函数,然后再组装出更简单更专门化的函数。
def format_and_pad(template,space):
def formatter(x):
return (template %x ).rjust(space)
return formatter
# 这样,可以创建一个始终返回15位字符串的浮点数格式化器,如下图所示 :
fmt=format_and_pad('%.4f',15)
print(fmt(1.756))
# 输出
' 1.7560'
对于闭包的调用,实际上可以通过类来实现,虽然会更啰嗦点。
扩展调用语法和args 、**kwargs
在python中,函数参数的工作方式其实比较简单,当编写func(a,b,c,d=some,e=value)时,位置和关键字参数其实分别是被打包成元组和字典的。函数实际接收到的是一个元组args和一个字典kwargs,并在内部完成转换。
位置参数和关键字参数示例:
def my_function(x, y, z=1.5):
if z > 1:
return z * (x + y)
else:
return z / (x + y)
# 函数可以有一些位置参数(positional)和一些关键字参数(keyword)。关键字参数通常用于指定默认值或可选参数。在上面的函数中,x和y是位置参数,而z则是关键字参数。
print(my_function(5,6,z=20))
print(my_function(3.14,7,3.5))
print(my_function(10,20))
# 输出:
220
35.49
45.0
允许将位置参数 当成关键字参数那样进行指定(即使它们在函数定义中并不是关键字参数) ,arg和kwargs的判定
def say_hello_then_call(f,*args,**kwargs):
print 'arg is ',args # args对应的是未带等于号的,(1, 2)
print 'kwargs is ',kwargs # kwargs对应的是 带等于号的 {'z': 5.0}
print ("Hello ! I'm going to call %s"% f)
return f(*args,**kwargs)
def g(x,y,z=1,h=3):
return (x+y)/z
# 调用
say_hello_then_call(g,1,2,z=5.)
print(say_hello_then_call(g,1,2,z=5.,h=20))
# 输出
arg is (1, 2)
kwargs is {'z': 5.0}
Hello ! I'm going to call <function g at 0x7f3a75adf578>
arg is (1, 2)
kwargs is {'h': 20, 'z': 5.0}
Hello ! I'm going to call <function g at 0x7f3a75adf578>
0.6
内置函数
enumerate
enumerate(sequence, [start=0]) 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
实例:
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
print(list(enumerate(seasons)))
#输出:
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
print(list(enumerate(seasons, start=1)) ) # 小标从 1 开始,可以指定开始的索引,start
# 输出
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]
seq = ['one', 'two', 'three']
for i, c in enumerate(seq):
print(i, c)
#输出
0 one
1 two
2 three
zip函数
函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表对象,这样做的好处是节约了不少的内存。利用 * 号操作符,可以将元组解压为列表。
实例:
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 返回一个对象
>>> zipped
<zip object at 0x103abc288>
>>> list(zipped) # list() 转换为列表
[(1, 4), (2, 5), (3, 6)]
>>> list(zip(a,c)) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> a1, a2 = zip(*zip(a,b)) # 与 zip 相反,zip(*) 可理解为解压,返回二维矩阵式.zip(a,b)得到的是[(1, 4), (2, 5), (3, 6)],zip(*x)
>>> list(a1)
[1, 2, 3]
>>> list(a2)
[4, 5, 6]
id函数
id方法的返回值就是对象的内存地址。
mapper[id(pHead)] = nHead #id方法的返回值就是对象的内存地址。
sorted函数
函数sorted,可以对所有可迭代的对象进行排序操作。 形式sorted(iterable, key=None, reverse=False)
参数: iterable,可以看到其中第一个参数是可迭代对象;
key,主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序;
reverse,是否反转,默认情况下不反转;
例子:
sorted()的理解,对list的排序为例:
def test_sorted():
L = [('b', 2), ('a', 1), ('c', 3), ('d', 0)]
print(sorted(L,key=lambda x:x[0])) # 用第一项排序
print(sorted(L,key=lambda x:x[1])) # 用第二项排序
test_sorted()
#输出:
[('a', 1), ('b', 2), ('c', 3), ('d', 0)]
[('d', 0), ('a', 1), ('b', 2), ('c', 3)]
鸣谢与参考
《python数据分析》