Python高效编程

本文探讨了Python编程的高效性,主要体现在简单易读的语法和万物皆对象的特性。通过分析Python程序的执行过程,包括编译与解释阶段,以及`import`语句的工作原理,揭示了Python解释器如何逐行解释并执行代码。同时,文章讨论了Python中对象的概念,展示了如何通过`__class__`属性验证一切皆为对象,以及如何利用`getattr`和`setattr`实现反射机制。通过对Python对象体系的理解,开发者可以更好地掌握程序行为的控制和调整。

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

"Life is short, I use python"这句脍炙人口的宣传语,在某种程度上表达了使用python开发软件的高效。以笔者目前的能力来说,python开发软件的高效主要体现在两个方面:1. 简单且贴合自然语言的语法设计,这使得python程序的可读性极佳,能够减少将逻辑转换为程序,检查逻辑错误的成本。2. (几乎)一切都是对象的抽象方式,凭此,软件开发者首先可以将物理世界的客观事物都抽象为程序中的对象,然后自由组织及操作。本文将从python程序的执行和python程序中一切皆为对象两个话题,详细阐述笔者对python高效编程的浅见。

因本人才疏学浅,故行文之间恐有纰漏,望诸君海涵,不吝赐教,若能予以斧正,则感激不尽

python程序的执行

图1 first.py

有一个first.py文件, 内容如图1所示。python解释器执行后,依次输出the first file和the main function。由此可知,python解释器是“逐行解释”程序的。

 图2 second.py

在first.py的同级目录下添加一个second.py文件, 内容如图2所示,first.py的内容更新为图3所示。python解释器执行first.py后,依次输出the second file,the first file和the main function。这说明,import语句被最先执行,而且second.py不是主module。此外,同级目录下多了一个__pycache__目录,其内有一个second.cpython-38.pyc文件(笔者使用的是CPython3.8解释器),它是一个binary文件,执行python second.cpython-38.pyc后,打印the second file。由此可知,python解释器是先编译second.py,然后将pyc文件给first.py使用。因此python解释器运行python程序的过程,包含了编译与解释两个阶段。

 图3 更新后的first.py

从上述内容可知,import的语义是导入pyc文件。那么,python解释器抛出ModuleNotFoundError的实质是,__pycache__的父目录没有被加入到python解释器的搜索路径中。一般地,python解释器的搜索路径包括python解释器执行的当前目录,标准库及pip安装目录,环境变量PYTHONPATH以及sys.path.insert/append动态添加的目录。

进一步地,python解释器是如何执行”import second“这条语句的呢?python官方文档在importlib中给出了答案[1]。首先调用__init__.py文件中的import_module函数,然后调用_bootstrap.py文件中的_gcd_import函数,最后调用同文件下的_find_and_load函数,其源码如下图4所示。通过源码可知,python将程序运行所需的所有模块都保存在了sys.modules中,它是一个字典。

 图4 _find_and_load函数源码

python程序中一切皆为对象

修改first.py文件的内容如图5所示,python解释器执行first.py后,输出内容如注释所示,这里,我们先断言它们都是类的实例化对象。如何验证呢?

 图5 first.py

python提供了__class__属性来获取对象的类。修改first.py文件内容如图6所示, 访问它们的__class__后,输出的类如注释所示,由此验证了它们都是对象。而且,输出的这些类与MyClass一样,它们仍是类的实例化对象,可以继续访问__class__属性,输出的类均为<class 'type'>。此时,我们发现module,function和int的类都是type,而且type的类仍是type。由此可知,python的对象体系如图7所示,其中type被称作metaclass。

 图6 first.py的类

结合python解释器逐行解释的工作原理可知,def myFunc经过编译、解释后会被创建为名为myFunc的function对象,class MyClass经过编译、解释后会被创建为名为MyClass的class对象。查阅python官方文档可知,类function实例化的function对象myFunc只允许被调用以及作为参数传递,MyClass则可以实例化object[2]。而且,Class MyClass等同于MyClass = type('MyClass', (), {}),其中type(className, bases, dict, **kwds)为type的一种构造函数[3]。

 图7 python的对象体系

理解了python在宏观层面的对象体系后,我们将目光聚焦到对象本身。在python中,object是数据和方法的聚合体, 其中数据与方法的可调用对象, 统称为object的属性(attribute),内置函数dir(object)可以查看object的属性,object调用访问符"."可以访问它的属性。

例如, 在first.py的同级目录下添加一个third.py文件, 内容如图8所示。python解释器执行third.py后,third.py中的输出内容如图8中注释所示。由此可知,module对象second,class对象MyClass及function对象myFunc,都是module对象first的属性。在third.py中使用first.XXX即可访问它们。通过属性__module__,对象可以获得它从属的module的名称。

 图8 third.py

除了符号"."以外,python还提供了内置函数getattr(object, name),能够通过字符串映射到object的属性。与之相对地,内置函数setattr(object, name, value)则用于更新属性的值,这就是python的反射机制。因为python程序中几乎一切皆为对象,所以熟练掌握这些内置函数后,软件开发者能够轻松实现基于字符串的事件驱动。

至此,我们梳理一下上述内容:

  1. 解释器逐行解释python程序。

  2. python文件被import后,会被python解释器转换成module对象。

  3. 所有module对象被归纳在sys.modules字典内,key是moudle对象的名称。

  4. python文件内的class对象,function对象,基本数值对象,module对象,都是该python文件被转换后的module对象的属性。

  5. __class__属性可以获取对象的class对象,__module__属性可以获取对象从属模块的名称。

  6. 内置函数getattr和setattr实现了以字符串映射到object的属性。

理论上,基于以上6点内容,我们可以从任意python对象出发,获取到python程序中的其他已被解释器实例化的任意对象。比如,从任意python对象推得其从属的module对象,然后获取属性module,最后访问属性module内的对象。或者借助sys.modules,绕过import语句,直接访问其他module内的python对象。

python解释器的解释顺序和python程序中一切皆为对象的抽象方式,赋予了软件开发者自由编排、更改python程序行为的能力。在此基础上,软件开发者再结合设计模式,开发经验以及大量练习,就可以实现高效、简洁、表达能力强的python程序。

参考文献

[1] importlib — The implementation of import — Python 3.10.5 documentation

[2] https://docs.python.org/3/library/stdtypes.html?highlight=class%20object#functions

[3] https://docs.python.org/3/library/functions.html?highlight=type#type

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值