Python之函数高级:函数深度剖析,一次性搞懂函数对象
off999 2024-09-21 20:49 31 浏览 0 评论
引言
在之前的系列文章中,我们介绍过了函数的常见用法,以及Python中一切皆对象,函数也是对象的概念。在函数高级的前两篇文章中,我们回顾并补充论述了函数对象是一等公民,以及Python中的高阶函数的概念。
为了为后续对Python中闭包内容的介绍打基础,今天的文章中,我打算换个角度,深入剖析下函数对象的内部构成。
本文的主要内容有:
1、函数对象的主要属性
2、函数对象的主要方法
3、函数对象中的代码在哪里
函数对象的主要属性
函数在Python中是作为对象的存在的,对象一般会有相应的属性和方法,我们首先来看下Python中有哪些主要的属性,从而更加清晰地理解函数对象。
首先看下函数对象所属类的定义:
从定义中可以看出,Python中的函数对象有这么几个属性,简单列举说明一下:
1、__name__:字符串类型,存储的是函数对象的名称。
2、__qualname__:字符串类型,存储的是方法对应的函数对象的全限定名。
3、__module__:函数对象所在的模块的名称
3、__defaults__:以元组形式存储函数对象中位置参数的默认值。
4、__kwdefaults__:以字典形式存储函数对象中的强制关键字参数的默认值。
5、__globals__:property属性,存储函数对象所在的全局命名空间的名字绑定的字典。
6、__closure__:property属性,存储函数对象构建的闭包中的自由元素(自由变量、内部函数对象)。
7、__code__:存储函数编译后的字节码和其他相关信息。
接下来演示下相关函数对象的属性,直接看代码:
首先看入口文件中定义的函数对象的相关属性情况:
from rich.pretty import pprint as print
a = 20
def add(n=1000, *, m=23000):
b = 10
c = 20
return a + n
print(add.__name__)
print(add.__qualname__)
print(add.__module__)
print(add.__defaults__)
print(add.__kwdefaults__)
print(add.__code__)
print(add.__closure__)
print(add.__globals__)
# 与globals()返回的全局命名空间中的名字对象绑定的字典对象是同一个对象
print(add.__globals__ is globals())执行结果:
然后,我们定义一个名为m1.py的模块:
a = 8888
def add(a, b):
return a + b
class Test:
def hello(self):
print(self)然后,我们在入口文件导入该模块,查看m1模块中的add()函数的属性:
import m1
a = 100
print(m1.add.__name__)
print(m1.add.__qualname__)
print(m1.add.__module__)
print(m1.add.__defaults__)
print(m1.add.__kwdefaults__)
print(m1.add.__code__)
print(m1.add.__closure__)
# 与globals()返回的全局命名空间中的名字对象绑定的字典对象是同一个对象
print(m1.add.__globals__ is globals())
# __globals__中的内容有点多,只看下我们关心的内容
print({kk: vv for kk, vv in m1.add.__globals__.items() if not kk.startswith('_')})
# globals()中的内容,只看我们关心的内容
print({kk: vv for kk, vv in globals().items() if not kk.startswith('_')})
执行结果:
最后我们再看一下特殊的函数对象,即类中方法的相关属性:
import m1
a = 100
test = m1.Test()
print(test.hello.__name__)
print(test.hello.__qualname__)
print(test.hello.__module__)
print(test.hello.__defaults__)
print(test.hello.__kwdefaults__)
print(test.hello.__code__)
print(test.hello.__closure__)
# 与globals()返回的全局命名空间中的名字对象绑定的字典对象是同一个对象
print(test.hello.__globals__ is globals())
# __globals__中的内容有点多,只看下我们关心的内容
print({kk: vv for kk, vv in test.hello.__globals__.items() if not kk.startswith('_')})
# globals()中的内容,只看我们关心的内容
print({kk: vv for kk, vv in globals().items() if not kk.startswith('_')})
执行结果:
从入口文件中定义的函数、导入模块中的函数,以及类中的方法,这三种不同的函数对象的实际代码演示,我们再稍微总结一下函数对象中的属性相关内容:
1、__name__在三种函数对象中都是函数定义时的名称
2、__qualname__只有在类中定义的方法中,变为“类名.方法名”的形式,其他情况都与__name__属性相同。
3、__defaults__和__kwdefaults__的区别在于,前者存储位置参数的默认值,是一个元组形式,而后者存储的是函数定义中,在*之后定义的强制关键字形式传递的参数的默认值,是以字典形式存在的。
4、__globals__属性,是定义该函数对象的全局命名空间对应的字典对象,所以,入口文件中定义的函数的__globals__属性与入口文件中的globals()返回的字典是同一个对象。而模块中定义的函数的__global__属性存储的是模块对应的全局命名空间所对应的字典对象,与入口文件的globals()返回的字典对象不同。
5、__closure__属性,会用于存储闭包中的自由变量及内部函数对象,从而使得闭包得以建立,这个会在后面的闭包内容中进一步展开。
6、__code__属性,存储函数定义相关代码编译为的字节码对象,本文会稍后进一步展开。
函数对象的主要方法
看完了函数对象中的比较重要的属性,接下来,我们看下函数对象的两个方法:__init__()和__call__()方法。
首先看下__init__()方法的描述:
从函数对象的__init__()初始化方法的相关定义,我们是否能通过别的方式初始化一个函数对象呢?
接下来,我们尝试基于一个已经定义好的函数的相关属性,作为参数,重新初始化一个新的函数对象。
直接看代码:
a = 10
b = 20
def add(n1, n2):
return a + b + n1 + n2
# 通过函数对象的__class__属性获取函数类,然后进行对象的实例化
# 也可以通过type(add)获取函数对象的类,进行对象的实例化,效果是一样的
# new_add = type(add)(code=add.__code__, globals={'a': 100, 'b': 200}, name='new_add')
new_add = add.__class__(code=add.__code__, globals={'a': 100, 'b': 200}, name='new_add')
print(add)
print(add.__name__)
print(add.__code__)
print(add(1, 2))
print(new_add)
print(new_add.__name__)
print(new_add.__code__)
print(new_add(1, 2))执行结果:
从执行结果可以看出:
1、除了通过def关键字实例化函数对象,我们也可以通过函数对象所属类进行一个函数对象的实例化。
2、两个函数对象可以指向通过一个字节码对象,表示执行相同的计算逻辑。
3、通过def关键字实例化的函数对象的__globals__属性,即为函数定义当前的全局命名空间对应的字典对象,无法修改。
4、通过手动实例化函数对象的方式,我们可以通过globals关键字传参,来手工指定函数对象的__globals__属性,从而实现,函数对象的计算逻辑在不同的命名空间中执行的效果。
函数对象中还有一个__call__()方法,这个方法之前其实已经介绍过,我们调用函数,其实有两种方式,一种是函数名()来进行调用。另外一种可以通过函数名.__call__()的方式进行调用。
这两种方式本质上是一样的。就不再展开了,感兴趣的同学可以自行尝试。
函数对象中的代码在哪里
通过前面内容的介绍,我们可以发现,函数对象的核心在于存储计算逻辑的__code__属性,以及存储函数执行的上下文环境对应的__globals__属性。两者的不同组合,可以实现函数对象的实际执行的多样性。
接下来我们来进一步看一下存储函数对象代码的__code__属性。
首先看代码:
a = 20
def add(n=1000, *, m=23000):
b = 10
c = 20
return a + n
code = add.__code__
print(code.__class__)
print(code.co_name)
print(code.co_argcount)
print(code.co_kwonlyargcount)
print(code.co_nlocals)
print(code.co_varnames)
print(code.co_names)
print(code.co_consts)
print(code.co_stacksize)
print(code.co_code)执行结果:
简单解释一下code对象的相关属性:
1、co_name:存储函数的名字,通常情况下与函数对象的__name__相同。
2、co_argcount:存储函数的位置参数的个数,代码示例中只有一个n。
3、co_kwonlyargcount:存储函数的强制关键字传参的参数个数,代码中只有一个m。
4、co_nlocals:存储函数中的局部变量的个数,代码示例中为4个,局部变量包括函数的参数及函数内部定义的局部变量。
5、co_varnames:存储函数中的局部变量的名称的元组,这里是n, m, b, c。
6、co_names:存储函数中使用的全局变量的名称的元组,这里只有a。
7、co_consts:存储函数中使用的常量的元组,这里有None、10、20。
8、co_stacksize:存储函数执行所需要的栈空间,这里是2。
9、co_code:存储函数执行的代码所编译为的字节码。
总结
本文重点介绍了函数对象中的比较重要的属性,以及通过函数类进行函数对象的实例化,然后展开介绍了函数属性中的__code__属性中的相关属性,从而对Python中的函数对象有了更深入的理解。
感谢您的拨冗阅读,希望对您学习Python能有些许帮助。
相关推荐
- win10家庭中文版下载官网(windows10家庭中文版下载)
-
你好,激活Win10家庭中文版的方法:1.购买正版Win10家庭中文版激活码,然后在计算机上输入激活码,即可完成激活。2.如果您已经安装了Win10家庭中文版,但尚未激活,可以通过以下步骤激活:-...
- 电脑截图在哪里找(电脑截图在哪里找图片win10)
-
截图默认会保存在电脑的剪贴板中,可以通过以下步骤将其保存到本地:1.打开任意一款图片软件,如Paint、Photoshop、Word等。2.按下键盘上的Ctrl+V,或者在软件菜单栏中选择...
- 电脑里一堆microsoft visual
-
按照系统向下兼容原理,保留2010就可以了.1)你安装的时候是不是把创建快捷键的选项框都没选上,导致在开始菜单中没有找到相应的链接?2)去你的安装目录下,找到Microsoftvisualc++...
-
- windows无法识别usb(windows无法识别usb设备)
-
Windows无法识别USB,解决办法如下右键开始菜单打开设备管理器,在通用串行总线控制器中右键点击设备选择“卸载”,完成后重新启动计算机即可解决问题。这有可能是在组策略中禁用了USB口,可以使用快捷键【Win+R】运行gpedit.msc...
-
2025-11-10 11:51 off999
- bios能看到硬盘 开机找不到硬盘
-
bios里可以看到硬盘,说明硬盘已经被主板识别。进系统找不到,可能硬盘没分区,或者硬盘是动态磁盘,还没有导入或激活。按win+r,输入diskmgmt.msc回车,就打开磁盘管理了,在里面可以给新硬盘...
- 无线网有个红叉(无线网有个红叉,搜索不到网络)
-
连接失败,路由坏换路由,外网坏,报修无线网络处出现红叉表示设备无法正常工作。请检查网卡驱动是否正常,无线网络开关是否打开。解决方法:查看电脑是否有无线网络开关,且是否打开。进入设备管理器检查网卡驱动是...
- thinkpad笔记本官网首页(thinkpad官方商城)
-
官方网站 国内:http://www.thinkworld.com.cn 国内用户只需要访问国内即可。 ThinkPad,中文名为“思考本”,在2005年以前是IBMPC事业部旗下的便携式计算机...
- win7什么版本最好用(win7哪个版本最稳定流畅)
-
Windows7旗舰版,最好,最稳定。Windows7,是由微软公司(Microsoft)开发的操作系统,内核版本号为WindowsNT6.1。Windows7可供选择的版本有:简易版(Sta...
- win7自带虚拟光驱怎么使用(win7系统虚拟光驱安装教程)
-
以DAEMONTools为例,360软件管家里面就有最新版的下.安装后使用方法如下:第一种方法:在虚拟光驱界面中,你先按一下中间工具栏最左边“+”符号的按钮,添加镜像文件(可以一次添加多个),这...
- 电脑装系统蓝屏(电脑装系统蓝屏重启开不了机)
-
蓝屏的原因往往集中在不兼容的硬件和驱动程序、有问题的软件、病毒等。解决办法:1、病毒的原因。使用电脑管家杀毒。2、内存的原因。用橡皮擦把内存条的金手指擦拭一下,把氧化层擦掉,确保内存条安装、运行正常。...
- u盘安装软件(u盘安装软件到电视)
-
第一种情况:软件安装包可以直接下载的。在电脑上将软件安装包下载到本地硬盘,然后将下载好软件安装包拷贝到U盘上即可拿到别的电脑上去安装。分可为exe格式的和rar格式,exe格式直接安装,rar格式的解...
- microsoft官网账户注册(microsoft 帐户注册)
-
要创建Microsoft账户,您可以按照以下步骤进行操作:1.打开任意一个支持浏览器的设备,如电脑、手机或平板电脑。2.在浏览器中输入"Microsoft账户注册"或直接访问Mic...
- 显示器闪屏是什么原因(显示器闪屏是哪里坏了)
-
解决方法: 一、接触不良导致的显示器闪屏 先查看主机和显示器的电源线连接,是否松动,重新插拔一下电源线。 二、信号干扰导致的显示器闪屏 1、连接显示器的电缆线是否没有屏蔽线圈,如果没有防干扰的...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
