This article from another different perspective to analyze the import mechanism of python, the main purpose is to understand import absolute, relative import encountered several errors.
Here the different perspective is from the Python import hooks this aspect to expand, of course, itself about the Python import hooks have a lot of articles, I do not intend to start talking about this aspect of the content, the article will be the main combination of code and PEP 302–new import Hooks this Pep.
1. Several import-related module Properties
First we need to know a few of the module properties related to import, because we are frequently exposed to these properties when we parse the code, and refer to these properties in Detail: Import-related-module-attributes.
__name__: The full name of the module, used to uniquely identify a module.
__package__: Module's__package__
The property must be set and must be a string. When the module is a package__package__
==__name__
If the module is Non-package and is a top-level module then__package__
Set to an empty string, for sub-modules then__package__
is the upper parent Module.__name__
。 About__package__
More detailed information can be found in: PEP 366–main module Explicit relative Imports.
__path__: This property is used to distinguish whether a module is a package or a PY file, and if the module is a__path__
property must be set, but this property may not have much other meaning. In more detail__path__
Content Reference: module.__path__.
2. Getting Started with Python import hooks
Although the focus of this article is not on Python import hooks, because the article is from this perspective, it is a little bit of a bit of introductory knowledge about this Aspect.
In general we use the code in theimport foo
, then the call is actually__builtin__.__import__
。 Sometimes we want to load a module dynamically in code, so we can useimp
、importlib
These two modules to implement, but sometimes we want to more control the import of python, such as to achieve an automatic installation, load module dependent function, then import hooks can come in handy.
Python offers two ways to do an import hook:Meta hooks and Path hooks, we can basically do what we want with the hook (there are some rules to follow, of course). Python also provides a template for import hooks, calledihooks
(/usr/lib/python2.7/ihooks.py), which is a module that we will focus on later.
If you want to use Ihooks instead of the default import feature, execute the following code before you execute any import:
#before any importsimport ihooksihooks.install()
All import operations in the back will then go to ihooks. The Moduleimporter.import_module () Function.
3. Profiling ihooks,imports_module Parameters
After executing the above mentioned ihooks.install()
import entrance becomes the following import_module()
Function.
def import_module (self, name, globals= None, locals=none, fromlist=none, Level=-1): parent = self.determine_parent (globals, level) q, tail = self.find_head_package (parent, str (name)) m = self.load_tail (q, tail) if not fromlist: return q if Hasattr (m, "__path__"): self.ensure_fromlist (m, fromlist) return m
The specific meaning of each parameter of this function can refer to builtin.__import__, focusing on level
this parameter:
-used to indicate absolute or relative import;
-if 0 is the absolute import;
-greater than 0 indicates the number of relative imports, relative to the number of imported parent directories;
--1 means that the absolute or relative may be imported.
locals
The parameter is temporarily unused.
4. Anatomy of the ihooks,determine_parent () function
DefDetermine_parent(self, globals, level=-1):IfNot globalsOrNot level:#code 1ReturnNone pkgname = Globals.get (' __package__ ')If PkgnameIsNotNone:IfNot Pkgnameand level >0:#code 2Raise valueerror,' Attempted relative import in Non-package 'Else#code 3# __package__ not set, figure it out and set it modname = Globals.get (' __name__ ')If ModNameIsNone:ReturnNoneIf"__path__"In Globals:# __path__ is set so modname are already the package name Pkgname = ModNameElse# Normal module, work out package name if anyIf‘.‘NotIn Modname:If level >0:Raise valueerror, (' Attempted relative import in '' Non-package ') globals[' __package__ '] =NoneReturnNone pkgname = modname.rpartition (‘.‘) [0] globals[' __package__ '] = pkgnameIf level >0:#code 4 dot = Len (pkgname)For XIn range,1,-1):Try:dot = Pkgname.rindex (‘.‘,0, Dot)Except Valueerror:raise valueerror ( ' attempted relative import beyond ' ' top-level package ') pkgname = pkgname[:d ot] try: return sys.modules[pkgname] #code 5 except keyerror: if level < 1: Warn ( "Parent module '%s ' not found while handling" "absolute Import "% pkgname, runtimewarning, 1" return none else: raise systemerror, (" Parent module '%s ' not loaded, cannot "" perform relative import "% Pkgname)
determine_parent()
The function is primarily responsible for populating the Module's __packge__
properties and returning the modules corresponding to the Module's anchor point (relative import only).
In the code, I made a code n for some of the key branches, which makes it easy to reference later.
Code 1: First we meet the code 1 this branch, the globals
empty case I have not encountered, but the case of level
0 is the previous analysis of the level
parameters shown in the Case: this is a absolute import, such as you used before the import from __future__ import absolute_import
, then it level
is 0. That is, if it is a absolute import then it is no longer necessary to find the parent module, and no longer set the __package__
module properties, Why in this case do not need to set the __package__
module properties?
Let's have a good read of this passage (from: PEP 366 proposed change):
As with the current __name__ attribute, setting __package__ 'll be is the responsibility of the PEP 302 loader used to Impor T a Module.
This means that the loader of the __package__
hooks (moduleimporter contains the finder, loader) is the responsibility, which is determine_parent()
to be done.
When the import system encounters an explicit relative import in a module without __package__ set (or with it set to None ), it'll calculate and store the correct value (__name__.rpartition ('. ') [0] for normal modules and __name__ for package initialisation modules).
The meaning of this sentence is that if you encounter a definite relative import and__package__
Not set then the loader will calculate, store the correct__package__
Value.
From the above two, the loader is responsible for setting__package__
, but also under certain conditions to be responsible, for our Code 1 encountered this situation (not explicit relative import), loader can not take this Responsibility.
Code 2: Here'sValueError: Attempted relative import in non-package
The mistake should be pythoner almost all met, but don't worry, we will continue to meet Later. The reason for this error is that__package__
An empty string means that this is a top-level regular Python source code module (top-level module), Then if there is a relative import then it is impossible to locate the Module.
Code 3: This part is set__package__
, the entire process is basically the same as THE PEP 366 proposed change mentioned, first through__path__
attribute to determine whether this is a package or a common source code module, if the package is directly set__package__
For__name__
, otherwise through__name__.rpartition(‘.‘)[0]
Calculations are Obtained. Here we meet again in front of theValueError
, the cause of the error here is similar to the previous, no longer too much Explanation.
So far we have finisheddetermine_parent()
The first important function: set the Module's__package__
Property.
Code 4: If you are importing relative, you need to calculate which of the relative anchor points, for example, in the Spam.foo.test moduleimport ..sub
Then the final calculation is that the module that needs to be imported is spam.sub.
In this section we encounter another common Mistake.ValueError: attempted relative import beyond top-level package
, the reason for this error is that we exceeded the maximum module when we calculated the anchor, for example, in the Spam.foo.test module.import ...sub
。
Code 5: The last function is completed: return to the anchor point Module.
5. Anatomy of the ihooks,find_head_package () function
DefFind_head_package(self, parent, name):If‘.‘in name:i = name.find ( '. ') head = name[:i] tail = Name[i+ 1:] else:head = name tail = "if parent:qname = "%s.%s "% (parent.__name__, head) else:qname = head q = self.import_it (head, qname, parent) #code 1 if Q: return q, tail if parent: QName = Head parent = none q = self.import_it (head, qname, parent) #code 2 if Q: return q, tail raise importerror, "No module named '%s '"% qname
From the function name we can probably guess what this function does, which is to import the first module in the full module path name, similar to if we are going to import spam.foo.test
, then the function is to import the spam
module First.
The theory of this function we can see from the third paragraph of PEP-0302 specification part 1:the Importer protocol, The general meaning is that we do relative import first,
For example spam
, if we were executing in, we import foo
would first try to import spam.foo
(code 1labeled in our codes Above) and then execute the absolute import if it fails foo
(code 2labeled in the above codes).
6. Anatomy of the Ihooks,load_tail () function
We have already imported the first module in the front so the next step is to import the remaining (trailing) modules, which is what this function does. Code is not affixed, it is relatively simple, is the cycle of the full module name of each sub-module import, function theory can refer to PEP-0302 specification Part 1:the Importer protocol fourth Paragraph.
7. Anatomy of the ihooks,ensure_fromlist () function
This function is to from spam import foo.test
import similar foo.test
parts.
8. Anatomy of the ihooks,import_it () function
DefImport_it(self, partname, fqname, parent, force_load=0):IfNot partname:# completely empty module name should only happen in# ' From. Import ' or __import__ ("")return parentIfNot force_load:try: return self.modules[fqname] #code 1 except keyerror: pass Span class= "hljs-keyword" >try:path = parent and parent.__path__ except attributeerror: return None partname = str (partname) stuff = Self.loader.find_module (partname, Path) #code 2 if not stuff: return none fqname = str (fqname) m = self.loader.load_module (fqname, stuff) #code 3 if parent:setattr (parent, partname, m) return m
This function is the core function to perform the import, and the various functions we described earlier are ultimately passed import_it()
to perform the final import.
The function code is actually quite simple, especially if you can combine PEP-0302 specification part 1:the Importer protocol to see the Code.
Code 1: If the module already exists in the cache, return the module directly.
Code 2: find the corresponding module, return a ternary group, indirectly called imp.find_module
. More about this function can refer to Protocol in addition to the above "PEP-0302 specification part 1:the Importer imp.find_module".
code 3: load the corresponding module, is called imp
within the various functions, no longer repeat.
The whole import_module()
function is introduced, before reading ihooks.py
or the Python/import.c
source code suggested that you first several pep and Python Language reference a few articles first read through, if some you do not know for a while, then you can stay in the source to find Out.
-the Import System
-PEP 302–new Import Hooks
-imp-access the Import Internals
-PEP 366–main Module Explicit relative imports
A brief analysis of Python relative and absolute import