Python 函数
- 什么是一等对象
- 高阶函数
- 匿名函数
- 函数内省
1 一等对象
在Python中,函数是一等对象。一等对象定义如下:
- 运行时创建
- 能赋值给变量或数据结构中的元素
- 能作为参数传递给函数
- 能作为函数返回值
即,函数和变量没什么“不同”,都是一等对象。
2 函数即对象
def factorial(n):
"""return n!"""
return 1 if n<2 else n*factorial(n-1)
>>> fact = factorial#把函数赋值给变量
>>> fact(5)
120
>>> list(map(factorial,range(7)))#将函数作为参数传递
[1, 1, 2, 6, 24, 120, 720]
3 高阶函数
接受函数为参数,或者把函数作为结果返回的函数是高阶函数。Python常见的高阶函数有:
map
sorted
filter
reduce
3.1 map函数
map(func, *iterables)
,第一个参数func
为函数,第二个参数iterables
为一个或多个序列。
map
函数作用为对指定序列做映射:把序列中的每个元素传入func
,并返回映射后的序列。
>>> map(factorial,range(7))#返回一个迭代器
<map object at 0x...>
>>> list(map(factorial,range(7)))#list获取迭代器内容
[1, 1, 2, 6, 24, 120, 720]#序列为0-6传入factorial函数的返回值:0!-6!
3.2 sorted函数
sorted(iterable, / , *, key=None, reverse=False)
sorted
函数作用为对指定序列排序,参数key
可传入“单参数函数”作为排序依据,reverse=False
表示顺序排序(从小到大)。
>>> words = ['world','apple','hello','banana','fig']
>>> sorted(words) #默认从小到大排序
['apple', 'banana', 'fig', 'hello', 'world']
>>> sorted(words, key=len) #按单词长度排序
['fig', 'world', 'apple', 'hello', 'banana']
>>> sorted(words, key=len, reverse=True) #按单词长度逆序排序
['banana', 'world', 'apple', 'hello', 'fig']
>>>
>>> d = dict(one=1,two=2,three=3,four=4,fife=5,six=6)
>>> d
{'fife': 5, 'two': 2, 'four': 4, 'six': 6, 'one': 1, 'three': 3}
>>> sorted(d.items(),key=lambda x:x[1]) #给字典排序,按值从大到小排序
[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('fife', 5), ('six', 6)]
>>>
3.3 filter函数
filter(function or None, iterable) --> filter object
filter
函数作用为筛选出序列中的真值;第一个参数可为function
(函数)或None
,第二个参数iterable
为可迭代对象。
>>> list(filter(None,[0,1,2,3,2,1,0]))#筛选出列表中的真值
[1, 2, 3, 2, 1]
>>> list(map(factorial, filter(lambda n:n%2, range(6))))
[1, 6, 120#筛选出0-5中的奇数,并把它们代入factorial函数(阶乘)
3.4 reduce函数
reduce(function, sequence[, initial]) -> value
reduce
第一个参数func
为一个函数,第二个参数sequence
为一个序列。
rudece
函数作用是把sequence
中的第1个和第2个元素代入func
,得到一个值,并把这个值和sequence
第3个元素作为参数传入func
,以此类推…最终得到一个值。
>>> from functools import reduce#导入reduce,Python3 reduce函数不是内置函数
>>> from operator import add#导入add函数
>>> reduce(add,range(100))#利用reduce计算0-99的和
4950
>>> sum(range(100))#可用规约函数sum代替求序列的和
4950
4 匿名函数
lambda
关键字用于创建匿名函数;
lambda
函数的定义体只能使用纯表达式,即不能赋值、也不能使用try
和while
等语句。
创建一个函数func,参数是一个序列,返回值是参数反转序列。
>>> func = lambda x:x[::-1]
>>> func([1,2,3])
[3,2,1]
以反转后的序列排序:
>>> sorted(words, key=lambda word:word[::-1])
['banana', 'world', 'apple', 'fig', 'hello']
5 函数内省
函数对象有许多属性,使用dir
函数可以查看函数属性。
def factorial(n):
"""return n!"""
return 1 if n<2 else n*factorial(n-1)
>>> dir(factorial)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
大多数熟悉是Python对象共有的,我们只讨论与函数看作对象相关的几个属性:
名称 类型 说明
__annotations__ dict 参数和返回值的注解
__call__ method-wrapper 实现()运算符
__closure__ tuple 函数闭包,即自由变量的绑定
__code__ code 编译成字节码的函数元数据和函数定义体
__doc__ str 文档字符串,通常是函数功能的解释
__dict__ dict 存储用户属性
__defaults__ tuple 形式参数的默认值
__globals__ dict 函数所在模块中的全局变量
__kwdefaults__ dict 仅限关键字形式参数的默认值
5.1 __doc__
属性
__doc__
是函数文档字符串属性,内置函数help()
也可查看对应内容。
>>> factorial.__doc__
'return n!'
>>> help(factorial)
Help on function factorial in module __main__:
factorial(n)
return n!
>>>
5.2 __dict__
属性
函数使用__dict__
属性存储赋予它的用户属性(相当于注解);
为函数随意赋予属性不是很常见的做法,但Django框架这么做了:Django把short_description属性赋予一个方法,Django后台使用这个方法时,记录列表会出现指定描述文本:
def get_poem_num(self):
return self.poem_set.all().count()
get_poem_num.short_description = '诗歌数量'
下面这个例子定义了生成斐波拉契数列的函数fibonacci
,并为函数赋予author属性,查看函数的__dict__
属性即可获取author属性信息。
def fibonacci(n):
"""斐波那契数列"""
return n if n<2 else fibonacci(n-1)+fibonacci(n-2)
>>> fibonacci.author = 'lzzwwen'
>>> fibonacci.__dict__
{'author': 'lzzwwen'}
5.3 __annotations__
属性
Python3提供了一种句法,用于为函数声明中的参数和返回值附加元数据:函数声明中参数可在:
之后增加注释;如果参数有默认值,则把注释添加在参数与=
之间;返回值注释,在函数声明末尾的:
之间添加->
和一个表达式,表达式可以是任何类型(代表返回值类型)。
定义一个函数,实现将一个字符串前n个字符变成大写。
def upper_length(text,n=10):
text = text[:lens].upper() + text[lens:]
return text
>>> upper_length('hello world')
'HELLO WORLd'
>>> upper_length.__annotations__
{}
默认的,函数并无注解信息;下面再来看添加注解的版本:
def upper_length(text:str,n:'int>0'=10)->str:
text = text[:lens].upper() + text[lens:]
return text
>>> upper_length('hello world')
'HELLO WORLd'
>>> upper_length.__annotations__
{'return': <class 'str'>, 'n': 'int>0', 'text': <class 'str'>}
5.4 __defaults__
及__kwdefaults__
-
__defaults__
:记录形式参数(位置/定位参数,即POSITIONAL_OR_KEYWORD)的默认值。下面例子定义了power幂函数,计算x^n,默认n为2;很明显,查看该函数
__defaults__
属性,记录的是参数n的默认值2。def power(x, n=2): """return x^n""" sum = 1 for i in range(n): sum *= x return sum
>>> power.__defaults__ (2,)
-
__kwdefaults__
:仅限关键字形式参数(KEYWORD_ONLY)的默认值。仅限关键字参数,此参数前面存在定位参数元组(VAR_POSITIONAL)类型的参数;
def tag(name, *content, cls=None, **attrs): """html标签 name: <name></name> or <name /> content: <name>content</name> cls: <name class="cls"></name> attrs: such as "id=xxx" """ if cls is not None: attrs['class'] = cls if attrs: attr_str = ''.join(' %s="%s"'%(attr,value) for attr,value in sorted(attrs.items())) else: attr_str = '' if content: return '\n'.join('<%s%s>%s</%s>'%(name,attr_str,c,name) for c in content) else: return '<%s%s />'%(name,attr_str)
>>> tag.__kwdefaults__ {'cls': None}
tag函数用于生成html代码,其中参数
cls
是仅限关键字参数,所以tag.__kwdefaults__
属性值是{'cls': None}
。
5.5 __code__
对象有自己的作用域,即有自己的命名空间,每一个命名空间对应一个__code__
。
__code__
包含已编译函数字节码的代码对象。
定义person函数,用于记录个人信息。
def person(name,*args,gender='male',**kwargs):
person_name = name
properitys = [arg for arg in args]
desc = {k:v for k,v in kwargs}
查看person函数的__code__
属性:
>>> help(person.__code__)
... ...
Data descriptors defined here:
|
| co_argcount #位置参数的个数
|
| co_cellvars #函数内局部变量,单元格变量名称的元组(由包含范围引用)
|
| co_code #原始编译字节码的字符串
|
| co_consts #函数内部的常量
|
| co_filename #创建此代码对象的文件的名称
|
| co_firstlineno #函数体第一行所在行号
|
| co_flags #CO_*标志的位图
|
| co_freevars #自由变量名称元组
|
| co_kwonlyargcount #仅限关键字参数个数
|
| co_lnotab #编码的行号到字节码索引的映射
|
| co_name #函数名
|
| co_names #当前函数内部使用的其他函数名称,局部变量名称的元组
|
| co_nlocals #局部变量的数量
|
| co_stacksize #虚拟堆栈空间
|
| co_varnames #参数名称和局部变量的元组
查看__code__
对象中的一些属性:
>>> code = person.__code__
>>> code.co_argcount#定位参数:name
1
>>> code.co_cellvars
()
>>> code.co_kwonlyargcount#仅限关键字参数:gender
1
>>> code.co_name#函数名
'person'
>>> code.co_nlocals#局部变量数
7
>>> code.co_varnames#参数及局部变量名称
('name', 'gender', 'args', 'kwargs', 'person_name', 'properitys', 'desc')
5.6 __closure__
闭包:指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。
闭包是一种函数,它会保留定义函数时存在的自由变量的绑定,这样调用函数时,虽然定义作用域不可用了,但仍能使用那些绑定(自由变量)。
函数闭包,即自由变量的绑定。
自由变量:未在本地作用域绑定的变量。
下面给出一个例子,函数make_avg
用于计算递增的系列值的均值:scores
变量是average
函数绑定的自由变量(不在average
函数内的变量,经绑定后可在函数内使用)。
def make_avg():
#闭包的开始
scores = []
def average(score):
scores.append(score)#自由变量(即socres)绑定
return sum(scores)/len(scores)
return average
#闭包结束
>>> avg = make_avg()
>>> avg(10)
10.0
>>> avg(20)
15.0
>>> avg(30)
20.0
>>> avg.__closure__[0].cell_contents#记录自由变量中的内容
[10, 20, 30]
>>> avg.__code__.co_freevars#自由变量名称元组
('scores',)
第二版make_avg
:不同的是将scores
列表换成了count
、total
整数类型。当count
是数字或其他不可变类型时,count+=1
就是count=count+1
;在averager
定义体中为count
赋值,就会把count
变成局部变量。当然,total
变量也一样。如下所示,因为count
、total
被当作(averager
中)局部变量,使用时又发现其没有被赋值,所以就抛出UnboundLocalError
异常。
>>> def make_avg():
count = 0
total = 0
def averager(value):
count += 1
total += value
return total/count
return averager
>>> avg = make_avg()
>>> avg(5)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'count' referenced before assignment
>>>
nonlocal
关键字可以把不可变数据类型标记为自由变量,如nonlocal count,total
;如函数没有声明这句,则报错:局部变量没有声明就赋值。
def make_avg():
count = 0
total = 0
def average(score):
nonlocal count,total#声明为自由变量,不加此句运行会报错
total += score
count += 1
return total/count
return average
>>> avg = make_avg()
>>> avg(10)
10.0
>>> avg(20)
15.0
>>> avg(30)
20.0
>>> avg.__closure__[0].cell_contents#自由变量count的值
3
>>> avg.__closure__[1].cell_contents#自由变量total的值
60
>>>