1Z实验室 MicroPython ESP32

MicroPython-ESP32之import姿势-1Z实验室

2018-08-16  本文已影响704人  仍是少年呀

出品:1Z实验室 1zlab.com
1ZLAB: Make Things Easy

导引

熟悉了CPython语法的开发者, 对于CPython的模块和包一定不会陌生, import语法十分的简介干练, 但是在MicroPython里, CPython里的那一套import逻辑, 并不能完全照搬, 本篇文章就为大家介绍一下MicroPython中针对于包(Package)的import的那些坑以及解决方案.

sys.path--解释器都从哪里import

划重点:Python解释器一般都会在特定的环境变量下去找寻用户所要导入的包或模块, 这些特定的环境变量保存在sys模块下的path变量中
以下是CPython中的sys.path(笔者操作系统为linux):

>>> import sys
>>> sys.path
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/sindgein/.local/lib/python3.6/site-packages', '/usr/lib/python3.6/site-packages']

以下是MicroPython的sys.path:

MicroPython v1.9.4-429-ge755bd493 on 2018-08-03; ESP32 module with ESP32
Type "help()" for more information.
>>> import sys
>>> sys.path
['', '/lib']

值得一提的是,MicroPython默认从main.py 启动,而main.py处于/ 目录下

MicroPython v1.9.4-429-ge755bd493 on 2018-08-03; ESP32 module with ESP32
Type "help()" for more information.
>>> import os
>>> os.listdir('/')
['boot.py', 'main.py']

从模块(Module)引用

如上文所分析, 在ESP32 Micropython默认的固件下,当前目录,等同于/,我们称之为根目录
因此,任何在MicroPython 根目录 下的模块,即.py脚本文件,是可以直接被解释器寻找到的,用户可以直接import该模块.
接下来我们创建一个示例来说明:

>>> f = open('easylab.py','w')
>>> f.write('a=\'hello,1zlab\'')
11
>>> f.close()
>>> from easylab import a
>>> a
'hello,easylab'

由此可见,在模块的引用上CPython和MicroPython基本一致.

从包(Package)引用

如果你的项目体积较大,功能模块划分较多,很可能非常需要在MicroPython中创建包来使工程更有条例和层次,同时避免各模块名称之间的冲突.但是包的引用在micropython中,与CPython大不相同.
在CPython中,我们创建包的时候,只需在该目录下新建一个__init__.py以此向解释器说明这是一个包.大多数时候,我们的__init__.py都是空白的(至少我是这样).然后我们就可以import 到该包下的所有模块了.
以上是CPython的逻辑,我们以此逻辑,在MicroPython上进行如下测试:

>>> import os
>>> os.mkdir('mod1')
>>> os.listdir()
['boot.py', 'easylab.py', 'mod1', 'main.py']
>>> f = open('mod1/__init__.py','w')
>>> f.close()
>>> os.listdir('mod1')
['__init__.py']
>>> f = open('mod1/test.py','w')
>>> f.write('a=\'hello,1zlab\'')
15
>>> f.close()
>>> import mod1.test as test
>>> test.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'a'
>>> 
>>> from mod1.test import a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: cannot import name a
>>> 

就是import不了变量

看到这里是不是有一丝的绝望?那在MicroPython里究竟该如何从包里导入模块呢?这个问题在我刚接触MicroPython的时候,就深深的困扰了我半天的时间,查阅了很多大佬,组织等写的中文文档,几乎没有人解答这方面的问题,后来我查阅了MicroPython的官方文档--Importing-Modules,才渐渐明白了MicroPython与CPython在这方面的差别.

根据官方文档的描述,MicroPython的解释器在寻找环境变量时遵循了以下的原则:
以mod1为例,按如下顺序寻找:

所以,我们/mod1/test.py模块下的一切,MicroPython解释器表示视而不见,滑稽.

我们按照上述思路,把变量a写入__init__.py:

>>> with open('mod1/__init__.py','w') as f:
...     f.write('a = \'hello, 1zlab\'')
...     
...     
... 
18

尝试import:

>>> from mod1 import a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: no module named 'mod1.a'

再换个姿势

>>> import mod1
>>> mod1.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'a'

这又是怎么回事呢?更加绝望了吧,官方文档也忽悠人啊,滑稽.

就是import不了的绝望,到此真的对MicroPython下的包的引用感到了累觉不爱.

但是,经过笔者长期的对文档的检索,发现了这其中的终极奥义:

MicroPython正确的import姿势:添加环境变量到sys.path

>>> import sys
>>> sys.path.append('mod1')
>>> import test
>>> test.
__class__       __file__        __name__        a
>>> test.a
'hello, 1zlab'

以上是MicroPython官方文档给出的解决方案,以下是官方说辞:

MicroPython Docs
大致概括如下:

MicroPython的设备内存很小, 所以import系统是经过高度优化的.所以不要把同一个模块的东西放到两个文件里去(正常CPython的思维就是包+模块,所以MicroPython高度优化之后,是摒弃了包这一概念的).对于MicroPython而言,建议大家最好不要在sys.path中添加到3个以上的import路径 : 1.你的应用路径;2.允许用户自定义修改的路径;3,系统模块路径,不允许更改.

推广

1Z实验室官网

ad.png
上一篇下一篇

猜你喜欢

热点阅读