如前所述,每个代码块(代码块)对应于PyCodeObject对象,Python会将对象存储在PYC文件中。不幸的是,并非总是如此。因此,我们猜测:一些Python程序仅暂时完成一些琐碎的任务。这样的程序只能运行一次,然后不会再次使用它,因此也不使用它,因此也不使用,因此也使用,因此也被使用,因此也被使用,因此也使用了它也被使用,因此也被使用,因此也被使用,因此也使用了,因此它也不需要保存到PYC文件。
如果我们在代码中添加了类似ABC的语句,然后执行您,您会发现Python生成了一个PYC文件,该文件表明导入将触发PYC的生成。
实际上,在运行过程中,如果您遇到诸如导入ABC之类的句子,则Python会在set路径中找到abc.pyc或abc.pyd文件。没有这些文件,但只有找到abc.py,然后Python会编译Pythonabc.py到pycodeObject中,然后创建pyc文件,然后将pycodeObject写入PYC文件。
接下来,使用abc.pyc上的导入操作。是的,将其编译到PyCodeObject对象之后,不直接使用它,请先在PYC文件中写入它,然后在存储器中的PYC文件中复制PyCodeObject对象。
关于Python的导入机制,我们稍后将分析这仅用于完成PYC文件的触发。
执行B.Py时,您会发现创建了A.Cpython-38.pyc。此外,PYC文件的创建位置将在当前文件的同一级别的__pycache__目录中创建。名称为:py file name.cpython-version number.pyc。
如上所述,当通过导入模块加载Python时,如果找不到相应的PYC或PYD文件,则PYC文件将根据PY文件自动创建。创建后,我将在其中编写三个内容。:
1.魔术数字
这是由Python定义的整数值。不同版本的Python将定义不同的魔术数字。该值是为了确保Python可以加载正确的PYC。
例如,Python3.7不会加载3.6版PYC,因为Python将在加载PYC文件时首先检测PYC的魔术数。如果它与自己的魔术数量不一致,则拒绝加载。
2. PYC的创建时间
这是充分理解的,并且源代码的最终修改时间和PYC文件的创建时间。如果PYC文件的创建时间比源代码修改时间早,则意味着在生成PYC后,源,源修改了代码,然后将重新编制并生成新的PYC,另一个将直接加载现有PYC。
3. PyCodeObject对象
不用说,必须存储它。
让我们看一下如何将PYC文件写入上述三个内容中。
由于您想编写它,因此必须有一个文件句柄。让我们来看看:
首先是写魔术数字和创建时间。他们将为写作的pymarshal_writelongtofile函数:
因此,此函数只是初始化的wfile对象,而真实的写作称为w_long。
w_long调用w_byte在文件中的a字节中编写x。
pycodeObject对象称为pymarshal_writeObjectTofile。让我们看看是什么样子。
可以看出,它基本上与pymarshal_writelongtofile基本相似,但是当它实际编写时,pymarshal_writelongtofile呼叫w_long和pymarshal_writeObjecttttttttttttttofile call w_object。
可以看出,w_byte基本上是称为w_byte,但这只是一些特殊对象。如果是列表,字典和其他数据,您将调用w_complex_object,即,如果代码中分支,则为最后一个。
w_complex_object功能的源代码很长。让我们看一下整体结构。特定的逻辑不会粘贴。我们将分别拦截部分分析。
尽管源代码很长,但逻辑非常简单,即为不同的对象和实现执行不同的写作动作。但是,它的最终目标是通过w_byte编写PYC文件。了解该功能的整体结构后,让我们查看特定的细节,看看将其写入对象时编写的内容是什么?
以列表和词典为示例,它们在编写时实际上编写内部元素,而其他对象相似。
但是问题是。如果您只编写元素,您如何知道加载Python时它是列表?上面的字典和w_type被称为,该词负责编写类型信息。
因此,无论哪个对象,在编写特定数据之前,我都会称呼w_type来编写类型信息。如果没有类型的信息,那么当Python加载PYC文件时,只能获得一堆字节,而无需分析该字节字节运行中包含的隐藏结构和信息。
因此,在将数据写入PYC文件之前,您必须编写一个徽标,例如type_list,type_tuple,type_dict等。这些徽标是相应的类型信息。
如果解释器在PYC文件中找到了这样的徽标,则表明上一个对象已经结束,新对象开始了,并且也知道新对象是什么,以便知道应该执行哪种施工操作。,也可以看到这些标志,并定义了底层。
这里可以看出,Python的PyCodeObject对象的导出实际上并不复杂。因为无论哪种对象,最终将其结论为两个简单的形式,一个是值的值,另一个是字符串编写。
以上是数值的写入,相对简单。它只需要根据字节写入PYC。但是,在编写字符串时,Python设计了一种更复杂的机制。如果您有兴趣,可以自己阅读源代码,也不会在此处介绍它。
下面有一个文件:
显然,编译后将创建三个PyCodeObject对象,但是另一个PyCodeObject对象中有两个PyCodeObject对象。
也就是说,在与模块相对应的pycodeObject对象中对应于foo和a的pycodobject对象在co_consts.指向的常数池中是准确的。
我们看到,与F2相对应的PyCodeObject确实在F1的常数池中。确切地说,F1常数池中有一个指针,以指向与F2相对应的PyCodeObject。
但这不是重点,关键是可以嵌套pycodeObject对象。当在示波器中找到新的示波器时,与新功能域相对应的pycodeObject对象将位于PyCodeObject对象的常数池中外部场,或恒定池中的指针。
在编写PYC时,它将开始从最外层的层(即模块的pycodeObject对象)写作。如果遇到另一个包含的pycodeObject对象,然后递归执行新的pycodeobject对象。
这样,所有pycodeObject对象都将写入PYC文件中。因此,PYCODEOBJECT对象也将PYCODEOBJECT对象链接到嵌套的关系,并且代码块之间的关系是一致的。
在这里,上面代码中创建了多少个PyCodeObject对象?
有6个答案。首先,该模块是一个,一个foo函数,一个条函数,A类A,A类中的FOO函数,A类中的条函数,因此总共有6个。
此外,这里的pycodeObject对象是分层的。首先,它为整个全局模块创建了PyCodeObject对象,然后遇到一个函数foo。然后为函数foo创建一个pycodeobject对象,然后依次向下下去。
因此,如果它是一个恒定值,它等效于静态信息,可以直接存储。但是如果它是函数和类,它将为其创建一个新的PyCodeObject对象,然后收集。
以上是与PYC文件有关的内容。源文件将在编译后获取PYC文件。因此,我们不仅可以手动导入PYC,还可以使用Python直接执行PYC文件。
以上是该共享的所有内容。如果您想了解更多信息,请转到公共帐户:Python编程学习圈,每日干货共享