一等对象之函数

本文详细介绍了Python中函数作为一等对象的概念,包括它们如何被赋值、作为参数传递和作为返回值。此外,还探讨了高阶函数,如map、sorted、filter和reduce的使用。另外,提到了匿名函数、函数内省以及函数的一些属性,如docstring、user-defined attributes、annotations等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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函数的定义体只能使用纯表达式,即不能赋值、也不能使用trywhile等语句。

创建一个函数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列表换成了counttotal整数类型。当count是数字或其他不可变类型时,count+=1就是count=count+1;在averager定义体中为count赋值,就会把count变成局部变量。当然,total变量也一样。如下所示,因为counttotal被当作(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
>>>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值