Python之import关键字探索

Java和Python玩家对import关键字一点都不陌生,本文以python为例,试着理解一下。

1. 环境变量

可以猜到,在import模块时,python会从环境变量中搜索需要加载的模块,这个列表就存放在sys.path变量中,可以进行修改。想要引入时,首先要将路径放到环境变量中。
对环境变量临时修改,将/home/admin/git添加进来,示例如下:

>>> import sys
>>> '/home/admin/git' in sys.path
False
>>> sys.path.append('/home/admin/git')
>>> '/home/admin/git' in sys.path
True
>>> print sys.path
['', '/usr/lib64/python26.zip', '/usr/lib64/python2.6', '/usr/lib64/python2.6/plat-linux2', '/usr/lib64/python2.6/lib-tk', '/usr/lib64/python2.6/lib-old', '/usr/lib64/python2.6/lib-dynload', '/usr/lib64/python2.6/site-packages', '/usr/lib64/python2.6/site-packages/gtk-2.0', '/usr/lib/python2.6/site-packages', '/home/admin/git']

类似windows系统变量,在查询时,也是按照列表的顺序进行遍历

2. 模块查询和加载

参考python PEP302,详细讲解了如何导入钩子机制,简单分为了两步模块导入和模块加载

The protocol involves two objects: a finder and a loader . A finder object has a single method:

finder.find_module(fullname, path=None)
loader.load_module(fullname)

2.1 finder.find_module实现

这个很容易理解,文件查询而已,有的是.py文件,有的是带目录package的。

import sys
import os
class ModuleFinder:
    def find_on_path(self, fullname):
        fls = ['%s/__init__.py', '%s.py']
        # import xx.oo.tt package导入,和java导入一样
        dirpath = '/'.join(fullname.split("."))

        for path in sys.path:
            path = os.path.abspath(path)
            for fp in fls:
                composed_path = fp % ("%s/%s" %(path, dirpath))
                if os.path.exists(composed_path):
                    # print composed_path
                    return composed_path

    def find_module(self, fullname, path=None):
        path = self.find_on_path(fullname)
        if path:
            do_something_else()

测试代码和结果如下:

# test case
f = ModuleFinder()
f.find_module('os')
f.find_module('urllib')
f.find_module('json')
/usr/lib64/python2.6/os.py
/usr/lib64/python2.6/urllib.py
/usr/lib64/python2.6/json/__init__.py

2.2 loader.load_module的实现

PEP302中,给出了一个简单的模块加载器:

# Consider using importlib.util.module_for_loader() to handle
# most of these details for you.
def load_module(self, fullname):
    code = self.get_code(fullname)
    ispkg = self.is_package(fullname)
    mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
    mod.__file__ = "<%s>" % self.__class__.__name__
    mod.__loader__ = self
    if ispkg:
        mod.__path__ = []
        mod.__package__ = fullname
    else:
        mod.__package__ = fullname.rpartition('.')[0]
    exec(code, mod.__dict__)
    return mod

将这个函数补充完善一下, 其中import_file_to_module是将源码弄成Python模块对象并返回,还有,要将模块的各类属性给塞进去。以json为例,他的属性列表(两个'_'开头结尾的)如下:

>>> import json
>>> dir(json)
['JSONDecoder', 'JSONEncoder', '__all__', '__author__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '__version__', '_default_decoder', '_default_encoder', 'decoder', 'dump', 'dumps', 'encoder', 'load', 'loads', 'scanner']
>>>

具体加载器实现如下:

class ModuleLoader(object):
    def __init__(self, path):
        self.path = path
    # package中包含文件__init__.py
    def is_package(self, fullname):
        dirpath = '/'.join(fullname.split("."))

        for path in sys.path:
            path = os.path.abspath(path)
            composed_path = '%s%s/__init__.py' % (path, dirpath)
            if os.path.exists(composed_path):
                return True
            return False

    def load_module(self, fullname):
        if fullname in sys.modules:
            return sys.modules(fullname)

        if not self.path:
            return
        sys.modules[fullname] = None
        # 读取源文件,编译成python代码,返回一个Python模块对象
        mod = import_file_to_module(fullname, self.path)

        ispkg = self.is_package(fullname)

        # 将模块的属性写上
        mod.__file__ = self.path
        mod.__loader__ = self
        mod=.__name__ = fullname

        # 如果引入的是一个package,还有其他属性
        if ispkg:
            mod.__path__ = []
            mod.__package__ = fullname
        else:
            mod.__package__ = fullname.rpartition('.')[0]

        sys.modules[fullname] = mod
        return mod

用的最多的东西反而最容易被忽视,比如C语言printf(),C++的cout,Ruby的Require等等。
拿来玩一玩,跟挖宝一样,蛮有意思的~~

参考内容

  1. PEP 302是什么?
  2. 书籍《Python高手之路》第二章 模块和库
  3. Experience of Python's “PEP-302 New Import Hooks”
时间: 2024-10-03 05:27:21

Python之import关键字探索的相关文章

Python的import初探[转]

日常使用python编程时,为了用某个代码模块,通常需要在代码中先import相应的module. 那么python的import是如何工作的呢? Table of Contents 1 如何使用import 2 import语句针对单个模块文件的工作方式 3 import语句针对模块包的工作方式 4 总结及深入阅读 5 参考 1 如何使用import 对于大型的软件项目,模块化的管理非常有必要. 于是在现如今的面向对象语言中,都有相应的机制来应对这一问题. 如C++中的namespace, J

详解 Kaggle 房价预测竞赛优胜方案:用 Python 进行全面数据探索

[导读]Kaggle 的房价预测竞赛从 2016 年 8 月开始,到 2017 年 2 月结束.这段时间内,超过 2000 多人参与比赛,选手采用高级回归技术,基于我们给出的 79 个特征,对房屋的售价进行了准确的预测.今天我们介绍的是目前得票数最高的优胜方案:<用 Python 进行全面数据探索>,该方案在数据探索,特征工程上都有十分出色的表现. 作者 Pedro Marcelino 在竞赛中使用的主要方法是关注数据科学处理方法,以及寻找能够指导工作的有力文献资料.作者主要参考<多元数

Python中super关键字用法实例分析

  本文实例讲述了Python中super关键字用法.分享给大家供大家参考.具体分析如下: 在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1: 代码段1: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class A: def __init__(self): print "enter A" print "leave A" class B(A): def __init__(s

python中import soundplayback失败

问题描述 python中import soundplayback失败 Traceback (most recent call last): File ""F:学习pytestNaoVideo1202.py"" line 7 in import soundplaybackImportError: DLL load failed: 找不到指定的模块.这个soundplayback 应该下载哪个模块?我用的python 2.7哪位大侠要是有模块直接发给我也行啊149197

python之import机制详解_python

本文详述了Python的import机制,对于理解Python的运行机制很有帮助! 1.标准import: Python中所有加载到内存的模块都放在 sys.modules .当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将模块的名字加入到正在调用 import 的模块的 Local 名字空间中.如果没有加载则从 sys.path 目录中按照模块名称查找模块文件,模块可以是py.pyc.pyd,找到后将模块载入内存,并加到 sys.modules 中,并

Python中的关键字参数与非关键字参数(可变参数)详解

学过php或者其他语言的同学都知道,php里面的参数不是个数不是可变的(只是很多时候是可以省略的,因为在函数定义的时候为参数设置了默认值).但是在python里却不是这样,python里面运行可变参数的出现,参数中出现(*arg,**arg2)的形式. 今天我们来详解一下这种用法: 例如: def foo1(arg1,arg2,key1=1,key2=2,*arg,**keywords): print "arg1 parameters is ",arg1 print "arg

一个Reentrant Error引发的对Python信号机制的探索和思考

写在前面 前几天工作时遇到了一个匪夷所思的问题.经过几次尝试后问题得以解决,但问题产生的原因却仍令人费解.查找 SO 无果,我决定翻看 Python 的源码.断断续续地研究了几天,终于恍然大悟.撰此文以记. 本文环境: Ubuntu 16.04 (64 bit) Python 3.6.2 使用的 C 源码可以从 Python 官网 获取. 起因 工作时用到了 celery 作为异步任务队列,为方便调试,我写了一个脚本用以启动/关闭 celery 主进程.代码简化后如下: import sys 

跟老齐学Python之Import 模块_python

认识模块 对于模块,在前面的一些举例中,已经涉及到了,比如曾经有过:import random (获取随机数模块).为了能够对模块有一个清晰的了解,首先要看看什么模块,这里选取官方文档中对它的定义: 复制代码 代码如下: A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended. Within a mo

obj-c在Xcode之外如何使用@import关键字

在Xcode中@import可以很方便的代替#import的功能,具体区别和便利请自行google之. 这里简单介绍下在Xcode之外如何使用@import.直接以 @import Foundation; 替换 #import <Foundation/Foundation.h> 在编译时会提示出错: ./foo.h:2:1: error: use of '@import' when modules are disabled 我们只要简单打开modules标志就可以了: clang -fobjc