import,os
...
http://www.17jo.com/program/python/base/Import.html
Python:import模块导入
Python中import用于导入不同的模块,包括系统提供和自定义的模块。其基本形式为:import 模块名 [as 别名],如果只需要导入模块中的部分或全部内容可以用形式:from 模块名 import *来导入相应的模块。
若要导入自定义模块,则需两个步骤:第一步:先在要导入的模块下创建一空文件__init__.py;
第二步:然后在要编写程序的文件头加入 import 路径.子路径.文件名 as 别名,或from 路径.子路径.文件名 import * 将所有变量,函数和类导入。具体用法如以下实例:
# 例:import导入模块 # 文件 userdefine.py def say(): print 'hello' # 文件 tlib/userdefine.py def say(): print 'python' # 文件 test.py import os # 导入系统os模块 from userdefine import * # 导入当前目录userdefine模块中所有函数、类等 import tlib.userdefine as u # 导入子目录tlib下的userdefine模块,别名为t say() >> hello u.say() # 使用hello.py中定义的say函数 >> python
OS 模块
os模块提供了一个统一的操作系统接口函数,os模块能在不同操作系统平台如nt,posix中的特定函数间自动切换,从而实现跨平台操作。
- os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
- os.chdir("dirname") 改变当前脚本工作目录到dirname
- os.curdir 返回当前目录: ('.')
1 |
import os |
2 |
os.getcwd() #输出'/Volumes/Leopard/Users/Caroline' |
3 |
os.chdir( '/Volumes' ) |
4 |
os.getcwd() #输出'/Volumes' |
5 |
os.curdir #输出"." |
- os.makedirs() 可生成多层递归目录
- os.removedirs() 可删除多层递归空目录,若目录不为空则无法删除
- os.mkdir() 生成单级目录
- os.rmdir() 删除单级空目录,若目录不为空则无法删除,报错
- os.pardir() 获取当前目录的父目录字符串名
- os.listdir() 列出指定目录下的所有文件和子目录,包括隐藏文件
- os.tmpfile() 创建并打开‘w+b’一个新的临时文件
- os.remove() 删除一个文件
- os.rename("oldname","newname") 重命名文件
- os.sep 输出操作系统特定的路径分隔符。win下为"\\",macx下为"/"
- os.linesep 输出当前平台使用的行终止符
- os.pathsep 输出用于分割文件路径的字符串
- os.name 输出字符串指示当前使用平台。win->'nt'; mac->'posix'
- os.system(command) 运行shell命令
1 |
import os |
2 |
os.sep #输出"/" |
3 |
os.linesep #输出"\n" |
4 |
os.pathsep #输出":" |
- os.environ 获取系统环境变量
1 |
import os |
2 |
os.environ #输出{'LANG': 'en_US.UTF-8', 'TERM': 'xterm-color', 'Apple_PubSub_Socket_Render': '/tmp/launch-cQd0wl/Render', 'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'VERSIONER_PYTHON_VERSION': '2.6', 'SHLVL': '1', 'SSH_AUTH_SOCK': '/tmp/launch-Wd5ZJI/Listeners', 'TERM_PROGRAM_VERSION': '273', '__CF_USER_TEXT_ENCODING': '0x1F5:0:0', 'PWD': '/Volumes/Leopard/Users/Caroline', 'SHELL': '/bin/bash', 'LOGNAME': 'Caroline', 'USER': 'Caroline', 'HOME': '/Volumes/Leopard/Users/Caroline', 'PATH': '/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin', 'COMMAND_MODE': 'unix2003', '_': '/usr/bin/python', 'DISPLAY': '/tmp/launch-VIFSmk/org.x:0', 'TMPDIR': '/var/folders/Iy/IyQx9l7EHD0y+Z77k20XCU+++TI/-Tmp-/', 'TERM_PROGRAM': 'Apple_Terminal'} |
3 |
os.environ.get( 'LOGNAME' ) #输出'Caroline' |
-
os.access('pathfile',os.W_OK) 检验文件权限模式,输出True,False
- os.chmod('pathfile',os.W_OK) 改变文件权限模式
1 |
import os |
2 |
os.access( '/Volumes/Leopard/Users/Caroline/Desktop/1.mp4' ,os.W_OK) #输出True |
3 |
os.chmod( '/Volumes/Leopard/Users/Caroline/Desktop/1.mp4' ,os.X_OK) #修改权限为X |
4 |
os.access( '/Volumes/Leopard/Users/Caroline/Desktop/1.mp4' ,os.W_OK) #输出False |
5 |
os.access( '/Volumes/Leopard/Users/Caroline/Desktop/1.mp4' ,os.X_OK) #输出True |
python对操作系统的目录和文件操作
一、获取当前目录下的特定文件列表
>>>import glob,os
>>>curdir = os.getcwd() #获取当前目录
>>>os.chdir(workdir) #设置当前目录
>>>dir = glob.glob('*.dat') #获取当前目录的dat文件列表
>>>os.chdir(curdir) #还原当前工作目录
>>>print dir
['mudlog.dat','ddd.dat']
二、os模块的文件和目录操作函数
1.获得当前路径
os.getcwd()
>>> import os
>>> print 'current directory is ',os.getcwd()
current directory is D:/Python25/Lib/site-packages/pythonwin
#这里是PythonWin的安装目录
2.获得目录中的内容
os.listdir(path) path:要获得内容目录的路径。
>>> import os
>>> os.listdir(os.getcwd()) # 获得当前目录中的内容
['dde.pyd', 'license.txt', 'Pythonwin.exe', 'scintilla.dll', 'win32ui.pyd',
'win32uiole.pyd', 'pywin']
3.创建目录
os.mkdir(path) path:要创建目录的路径。
>>> import os
>>> os.mkdir('E://book//temp') # 使用os.mkdir创建目录
4.删除目录
os.rmdir(path) path:要删除的目录的路径。
>>> import os
>>> os.rmdir('E://book//temp') # 删除目录
需要说明的是,使用os.rmdir删除的目录必须为空目录,否则函数出错。
若想删除非空目录,先删除目录下的文件,然后再删除目录,递归过程。
5.判断是否是目录
os.path.isdir(path) path:要进行判断的路径。
>>> import os
>>> os.path.isdir('E://book//temp') # 判断E:/book/temp是否为目录
True # 表E:/book/temp是目录
6.判断是否为文件
os.path.isfile(path) path:要进行判断的路径。
>>> import os
>>> os.path.isfile('E://book//temp') # 判断是否为文件
False # 表示E:/book/temp不是文件
三、批量重命名
在日常工作中经常会遇到这样的情况,需要将某个文件夹下的文件按照一定的规律重新命名。如果手工完成的话,需要耗费大量的时间,而且
容易出错。在学习Python以后,完全可以写一个简单的脚本完成这样的工作。
import os
perfix = 'Python' # perfix 为重命名后的文件起始字符
length = 2 # length 为除去perfix后,文件名要达到的长度
base = 1 # 文件名的起始数
format = 'mdb' # 文件的后缀名
# 函数PadLeft将文件名补全到指定长度
# str 为要补全的字符
# num 为要达到的长度
# padstr 为达到长度所添加的字符
def PadLeft(str , num , padstr):
stringlength = len (str)
n = num - stringlength
if n >=0 :
str=padstr * n + str
return str
# 为了避免误操作,这里先提示用户
print 'the files in %s will be renamed' % os.getcwd()
input = raw_input('press y to continue/n') # 获取用户输入
if input != 'y': # 判断用户输入,以决定是否执行重命名操作
exit()
filenames = os.listdir(os.curdir) # 获得当前目录中的内容
# 从基数减1,为了使下边 i = i + 1在第一次执行时等于基数
i = base - 1
for filename in filenames: # 遍历目录中内容,进行重命名操作
i = i+1
# 判断当前路径是否为文件,并且不是“rename.py”
if filename != "rename.py" and os.path.isfile(filename):
name = str(i) # 将i转换成字符
name = PadLeft(name,length,'0') # 将name补全到指定长度
t = filename.split('.') # 分割文件名,以检查其是否是所要修改的类型
m = len(t)
if format == '': # 如果未指定文件类型,则更改当前目录中所有文件
os.rename(filename,perfix+name+'.'+t[m-1])
else: # 否则只修改指定类型
if t[m-1] == format:
os.rename(filename,perfix+name+'.'+t[m-1])
else:
i = i – 1 # 保证i连续
else:
i = i – 1 # 保证i连续
四、代码框架生成器
编写代码要养成良好的习惯,为了使脚本更具可读性,往往需要添加注释,而且还应该在脚本头添加基本的说明,如作者、文件名、日期、用途、版权说明,以及所 需要使用的模块等信息。这样,不仅便于保存脚本,而且也便于交流。但是,如果每次编写一个脚本就依次添加这样的信息,不免有些麻烦,以下代码实现了一个简 单的代码框架生成器。
# -*- coding:utf-8 -*-
# file: MakeCode.py
#
import os
import sys
import string
import datetime
# python脚本模板
py = '''#-----------------------------------------------------
# TO:
#-----------------------------------------------------
# BY:
#-----------------------------------------------------
'''
# c模板
c = ''' *-----------------------------------------------------
* TO:
*-----------------------------------------------------
* BY:
*-----------------------------------------------------
'''
if os.path.isfile(sys.argv[1]): # 判断要创建的文件是否存在,如果存在则退出脚本
print '%s already exist!' % sys.argv[1]
sys.exit()
file = open(sys.argv[1], 'w') # 创建文件
today = datetime.date.today() # 获得当前日期,并格式化为xxxx-xx-xx的形式
date = today.strftime('%Y')+'-'+today.strftime('%m')+'-'+today.strftime('%d')
filetypes = string.split(sys.argv[1],'.') # 判断将创建的文件是什么类型以便对其分别处理
length = len(filetypes)
filetype = filetypes[length - 1]
if filetype == 'py':
print 'use python mode'
file.writelines('# -*- coding:utf-8 -*-')
file.write('/n')
file.writelines('# File: ' + sys.argv[1])
file.write('/n')
file.write(py)
file.write('# Date: ' + date)
file.write('/n')
file.write('#-----------------------------------------------------')
elif filetype == 'c' or filetype == 'cpp':
print 'use c mode'
file.writelines(' /n')
else:
print 'just create %s' % sys.argv[1]
file.close() # 关闭文件
写好的脚本可以放到Windows的系统目录中,这样就可以随时运行,方便地在目录中产生Python脚本或者C/C++文件。
五、运行其他程序
在Python中可以方便地使用os模块运行其他的脚本或者程序,这样就可以在脚本中直接使用其他脚本,或者程序提供的功能,而不必再次编写实现该功能的代码。
使用os.system函数运行其他程序
os模块中的system()函数可以方便地运行其他程序或者脚本。其函数原型如下所示。
os.system(command) command :要执行的命令,相当于在Windows的cmd窗口中输入的命令。如果要向程序或者脚本传递参数,可以使用空格分隔程序及多个参数。
以下实例实现通过os.system()函数打开系统的记事本程序。
>>> import os
# 使用os.system()函数打开记事本程序
>>> os.system('notepad')
0 # 关闭记事本后的返回值
# 向记事本传递参数,打开python.txt文件
>>> os.system('notepad python.txt')
From:http://book.51cto.com/art/200710/58166.htm
From:http://blog.sina.com.cn/s/blog_4419b53f0100abzb.html
os 系统服务应用程序接口(API),系统管理员最应关注的模块之一。
一、os模块
(一)文件目录,权限相关
1、创建目录
mkdir(path [, mode=0777])
os.mkdir('/tmp/os_dir',0754)
2、删除目录
os.rmdir("/tmp/os_dir")
3、列出目录内容
os.listdir('/root')
结果为一个列表
4、更改文件目录
os.chdir('/tmp')
os.chroot('/tmp') #设置监牢目录
5、显示当前目录
os.getcwd()
'/tmp'
6、创建多层目录 类似系统makedir -p
os.makedirs('test/test1/test2')
7、删除多层目录【每层都是空的除了要删除目录外】
os.removedirs('/tmp/test/test1/test2')
8、创建一个文件
>>> f=open("/tmp/testfile", "w")
>>> f.write('test\n')
>>> f.close()
创建空文件呢?
>>> f=open("/tmp/testfile", "w")
>>> f.close()
9、删除文件
os.remove("/tmp/testfile")
10、查看文件状态
os.stat('/tmp/os_dir')
posix.stat_result(st_mode=16876, st_ino=17024L, st_dev=94L, st_nlink=2, st_uid=0, st_gid=0, st_size=512L, st_atime=1331646103, st_mtime=1331646103, st_ctime=1331646103)
依次是st_mode (权限模式), st_ino (inode number), st_dev (device), st_nlink (number of hard links), st_uid (所有者用户 ID), st_gid (所有者所在组 ID ), st_size (文件大小, 字节), st_atime (最近一次访问时间), st_mtime (最近修改时间), st_ctime (平台相关; Unix下的最近一次元数据/metadata修改时间, 或者 Windows 下的创建时间) - 以上项目也可作为属性访问
11、文件改名
文件夹改名
os.rename('/tmp/os_dir','/tmp/os_rename')
文件改名
os.rename('/tmp/testfile','/tmp/TSFILE')
12、文件权限修改
chmod(path, mode)
os.chmod('/tmp/os_rename',0777)
os.chmod('/tmp/TSFILE',0666)
os.lchmod(path, mode) 修改连接文件权限
13、文件属主修改
chown(path, uid, gid)
os.chown('/tmp/TSFILE',1003,1003)
os.chown('os_rename',0,1003)
14、umask
os.umask(022)
15、os.walk(topdirpath, topdown=True, onerror=None, followlinks=False)
os.walk()
函数声明:walk(top,topdown=True,onerror=None)
1>参数top表示需要遍历的目录树的路径
2>参数topdown的默认值是"True",表示首先返回目录树下的文件,然后在遍历目录树的子目录.Topdow
n的值为"False"时,则表示先遍历目录树的子目录,返回子目录下的文件,最后返回根目录下的文件
3>参数onerror的默认值是"None",表示忽略文件遍历时产生的错误.如果不为空,则提供一个自定义函
数提示错误信息后继续遍历或抛出异常中止遍历
4>该函数返回一个元组,该元组有3个元素,这3个元素分别表示每次遍历的路径名,目录列表和文件
列表
dirpath, dirnames, filenames
对于每个目录树中的目录根在顶部(包括顶部本身,但不包括.和..),产生一个3元组
组成的元组对象(dirpath,dirnames,文件名)
第一元素就是顶层目录,也就是walk方法的第一个参数
第二元素是顶层目录下子目录的列表
第三个元素为该目录的文件组成的列表
dirroot.next() 第一次是查找的目录为顶层目录
第二次迭代就是顶层目录下其他字目录,然后依次就可以获取所有目录下面的文件
为了得到一个完整的路径(顶部开始)到一个文件或目录dirpath,做os.path.join(dirpath,filename)。
默认情况下,os.walk不遵循符号链接到子目录支持它们的系统。为了得到这个功能,设置可选参数'followlinks为true
将根目录和文件名连接在一起【包括目录和子目录都join】
for root,dir,file in os.walk('/root'):
[os.path.join(root,name) for name in file]
这个结果是每个文件和每个文件夹都是一个列表
...
for root,dir,file in os.walk('/root'):
for filename in file:
os.path.join(root,filename)
这个结果显示每一个文件的路径(包括隐藏文件)
....
>>> path_collection=[]
>>> for root,dir,file in os.walk('/root'):
... for filename in file:
... fullpath=os.path.join(root,filename)
... path_collection.append(fullpath)
将目录全部文件的路径,保存到一个列表对象
..........
将根目录和子目录连接在一起
for root, dirs, files in os.walk('/root'):
[os.path.join(root,dirname) for dirname in dirs]
>>> for root, dirs, files in os.walk('/root'):
... print root, "consumes",
... print sum([getsize(join(root, name)) for name in files]),
... print "bytes in", len(files), "non-directory files"
每一个目录都是root
(二)用户相关
os.getuid()
os.getgid()
os.getgroups()
os.getlogin() 登陆用户
(二)进程相关
1、获取当前进程
os.getpid
2、执行操作系统命名
1)os.system(command)
这个方法直接返回命令的结果
command = "ls -l"
os.system(command)
os.system('ps -aux|grep python')
2)os.popen(command)
这个方法将命令返回值得保存到一个文件对象里,这个对象需要通过read()等方法读取内容。
Open a pipe to/from a command returning a file object. 这个文件对象保留在内存中。
p=os.popen('ssh 10.3.16.121 ps aux | grep mysql')
x=p.read()
print x
p.close()
print os.popen('ps aux | grep python').read()
如果不确定参数的安全性, 那么最好使用 exec 或 spawn
3、启动新的进程
>>> import os
>>> program = "python"
>>> arguments = ["hello.py"]
>>> os.execvp(program, (program,) + tuple(arguments))
hello again, and welcome to the show
使用的是execvp函数,它会从【标准路径搜索执行程序】,把第二个参数(元组)作为单独的参数传递给程
序,并使用当前的环境变量来运行程序. 其他七个同类型函数。
在 Unix 环境下, 你可以通过组合使用 exec , fork 以及 wait 函数来从当前程序调用另一个程序,
fork 函数复制当前进程, wait 函数会等待一个子进程执行结束.
3、os 模块调用其他程序 (Unix)
>>> def run(program, *args):
... pid = os.fork()
... if not pid:
... os.execvp(program, (program,) + args)
... return os.wait()[0]
>>> run("python", "hello.py")
hello again, and welcome to the show
1701
fork函数在子进程中返回0(这个进程首先从fork返回值),在父进程中返回一个非0的进程标识符(子进程的PID). 也就是说,
只有当我们处于父进程的时候 "not pid" 才为真.(即在子进程中pid为0,pid==0为真,在父进程中not pid(not 0),pid>0为真)
通过os模块中的fork方法,一个进程(Process)可以生成一个独立子进程。fork是一个程序拷贝(copyi
ng-program)的过程:当程序调用fork方法,操作系统生成一份该程序及其在内存中的进程的新的拷贝
,并以与原始程序并行的方式开始执行这份拷贝。原始程序称为父进程,新生成的拷贝叫做子进程。父
进程可以生成任意数目的子进程,子进程还可以生成它的子进程。这些子进程在操作系统的控制下相互独立的并行运行。子进程可以继续运行即便父进程已退出。
>>> def run(program, *args):
... pid = os.fork()
... if not pid:
... os.execvp(program, (program,) + args)
... return os.wait()
...
>>> run("python", "hello.py")
hello again, and welcome to the show
(1546, 0)
os.wait() 返回一个元组,这个两个数据代表什么? 第一元组已完成的子进程号pid,第二个元素为0表示子进程的退出状态
os.wait函数用于等待子进程结束(只适用于UNIX兼容系统)。该函数返回包含两个元素的元组,包括已完成的子进程号pid。以及子进程的退出状态,返回状态为0,表明子进程成功完成。返回状态为正整数表明子进程终止时出错.
如没有子进程,会引发OSError错误。os.wait
要求父进程等待它的任何一个子进程结束执行,然后唤醒父进程。要指示父进程等候一个指定的子进程终止,可在父进程中使用os.waitpid
函数(只适用于unix兼容系统).
它可等候一个指定进程结束,然后返回一个双元素元组,其中包括子进程的pid和子进程的退出状态。函数调用将pid
作为第一个参数传递。并将一个选项作为第二个选项,如果第一个参数大于0,则waitpid会等待该pid结束,如果第一个参数是-1
,则会等候所有子进程,也就和os.wait一样
参考http://developer.51cto.com/art/201003/185584.htm
fork和wait 函数在 Windows 上是不可用的, 但是你可以使用 spawn 函数, 不过, spawn
不会沿着路径搜索可执行文件, 你必须自己处理好这些
4、使用 os 模块调用其他程序 (Windows)
os.spawnv(os.P_WAIT, file, (file,) + args)
5、使用 spawn 或 fork/exec 调用其他程序
6、处理守护进程(Daemon Processes)
Unix系统中,你可以使用fork函数把当前进程转入后台(一个"守护者/daemon").一般来说,需要派生(fo
rk off)一个当前进程的副本, 然后终止原进程
import os
import time
pid = os.fork()
if pid:
os._exit(0) # kill original
print "daemon started"
time.sleep(10)
print "daemon terminated"
7、使用os模块终止当前进程
os._exit(0)
(三)执行操作系统命名
command = "ls -l"
os.system(command)
os.system('ps -aux|grep python')
(四)其他
1、获取环境变量
os.getenv('USER',default=None)
os.getenv('HOSTTYPE',default=None)
2、修改环境
putenv(key, value)
3、判断操作系统
os.name
if os.name in ("nt", "dos"):
exefile = ".exe"
else:
exefile = "
(五)os.path模块
os.path模块包含了各种处理长文件名(路径名)的函数.先导入os模块,然后就可以以os.path访问该模
块
1)简单的方法
拆分,目录名,文件名,连接
import os
filename = "my/little/pony"
print "using", os.name, "..."
print "split", "=>", os.path.split(filename)
print "splitext", "=>", os.path.splitext(filename)
print "dirname", "=>", os.path.dirname(filename)
print "basename", "=>", os.path.basename(filename)
print "join", "=>", os.path.join(os.path.dirname(filename),os.path.basename(filename))
结果
using posix ...
split => ('my/little', 'pony')
splitext => ('my/little/pony', '')
dirname => my/little
basename => pony
join => my/little/pony
注意这里的 split 只分割出最后一项(不带斜杠).
2、判断文件或文件夹是否存在
>>> os.path.exists(filename)
False
>>> os.path.exists('/root')
True
>>> os.path.exists('/root/wc.py')
True
3、判断是否是目录
>>> os.path.isdir('/root/wc.py')
False
>>> os.path.isdir('/root')
True
4、判断是否是文件
>>> os.path.isfile('/root')
False
>>> os.path.isfile('/root/wc.py')
True
5、判断是否是连接文件,是否是挂载文件
>>> os.path.islink('/root/wc.py')
>>> os.path.ismount('/root/wc.py')
6、判断两个目录是否有相同的文件
>>> os.path.samefile('/root/pathdir1','/root/pathdir2')
True
做了链接,判断就为真。
7、获取文件(文件夹)大小
>>> os.path.getsize('/root/pathdir1')
512L
8、获取文件时间戳
创建时间
>>> os.path.getctime('/root/pathdir1/person.py')
修改时间
>>> os.path.getmtime('/root/pathdir1/person.py')
访问时间
>>> os.path.getatime('/root/pathdir1/person.py')
9、walk(top, func, arg)
Directory tree walk with callback function.
os.path.walk()
函数声明:walk(top,func,arg)
1>参数top表示需要遍历的目录树的路径
2>参数func表示回调函数,对遍历路径进行处理.所谓回调函数,是作为某个函数的参数使用,当某个
时间触发时,程序将调用定义好的回调函数处理某个任务.回调函数必须提供3个参数:第1个参数为wa
lk()的参数arg,第2个参数表示目录列表,第3个参数表示文件列表
3>参数arg是传递给回调参数func的元组.回调函数的一个参数必须是arg,为回调函数提供处理参数.
参数arg可以为空
import os
def callback(arg, directory, files):
for file in files:
print os.path.join(directory, file), repr(arg)
os.path.walk("/root", callback, "secret message"
os.path.walk的第三个参数传给回调函数做第一参数,os.path.walk的第一个参数(目录)传递给回
调函数做第二个函数,回调函数的第三个函数就是目录下的file.
os.path.walk()与os.walk()产生的文件名列表并不相同.os.path.walk()产生目录树下的目录路径和\
文件路径,而os.walk()只产生文件路径.
# -*- coding: utf-8 -*-
import os
for root, dirs, files in os.walk('/root'):
open('/tmp/rootdir', 'a').write("%s %s %s" % (root,dirs,files))
本文出自 “diege” 博客,请务必保留此出处http://ipseek.blog.51cto.com/1041109/806036
Python的import初探
日常使用python编程时,为了用某个代码模块,通常需要在代码中先import相应的module。
那么python的import是如何工作的呢?
Table of Contents
1 如何使用import
对于大型的软件项目,模块化的管理非常有必要.
于是在现如今的面向对象语言中,都有相应的机制来应对这一问题.
如C++中的namespace, Java中的package,C#中的namespace和using.
import就是Python中用于程序模块化管理的关键字.
通过import语句,将模块中声明或定义的变量或者函数等名字在当前程序运行的时刻可见.
这样我们就可以直接通过名字的方式,如变量名或者函数名复用原有代码.
通过import语句,我们就能将python代码模块化,方便管理和维护
2 import语句针对单个模块文件的工作方式
先看一组示例:
>>> path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'path' is not defined >>> sys.path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'sys' is not defined >>> import sys >>> path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'path' is not defined >>> sys.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> from sys import path >>> path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
这段代码中,我们尝试使用sys包中的path变量得到python默认查找模块的路径信息,只有在import sys之后,python解释器才能正确的找到该变量.
我们通过一个python内部函数dir()来看看python解释器如何找到名字的. dir()函数是python内建函数,用于查看指定作用域下可用的名字.
若没有传参数,则打印当前作用域下的可用名字.
>>> help(dir) Help on built-in function dir in module __builtin__: dir(...) dir([object]) -> list of strings If called without an argument, return the names in the current scope. Else, return an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes reachable from it. If the object supplies a method named __dir__, it will be used; otherwise the default dir() logic is used and returns: for a module object: the module's attributes. for a class object: its attributes, and recursively the attributes of its bases. for any other object: its attributes, its class's attributes, and recursively the attributes of its class's base classes. >>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import sys >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> dir(sys) [ ..., 'modules', 'path', ... , 'version', 'version_info', 'warnoptions'] >>> from sys import path >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'path', 'sys']
执行import语句后,python解释器会将sys模块的名字添加到当前作用域中,这样就能直接通过sys.path得到python的搜索路径了.
注意到,我们还用了from sys import path语句.通过这条语句path就被直接提升到当前作用域中,这样path这个名字就能被直接使用了.
之所以有from,是为了更加精确的让某个模块中的某个名字在当前作用域可见.通过这种机制,程序员可以精确控制当前作用域的名字,防止作用域被不必要的名字污染.
另外,这种机制也避免了使用”.”来进行子成员的引用,减小程序员的输入.
这里需要提一句,虽然python提供了from XXX import *支持,能将XXX模块中的所有名字都提升到当前作用域中,但是要小心使用,因为程序员不能精确的知道到底import了哪些名字.
再看一组示例:
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import sys >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> import sys as SYS >>> dir() ['SYS', '__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> SYS.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> sys.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> del(sys) >>> SYS.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> sys.path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'sys' is not defined
上面的例子展示了两个功能:
- import XXX as YYY: 这个可以对模块实施重命名操作.
- del(): 用于删除当前空间中不再使用的名字.当空间中出现很多不再需要的名字时,可以利用该函数进行清理.
3 import语句针对模块包的工作方式
有时我们可能需要编写一个完整的模块库,比如python对XML的处理就需要一堆的函数.这时候可能划分成多个文件,更加方便管理.
逻辑上也更加清晰.
因此python引入了对多文件模块包的支持.说白了,就是import的不是一个文件的内容,而是一个文件夹的内容.
看下面的示例:
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import xml >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'xml'] >>> import xml.sax.xmlreader >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'xml'] >>> dir(xml) ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'sax'] >>> dir(xml.sax) ['ContentHandler', 'ErrorHandler', 'InputSource', 'SAXException', 'SAXNotRecognizedException', 'SAXNotSupportedException', 'SAXParseException', 'SAXReaderNotAvailable', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '_create_parser', '_exceptions', '_false', '_key', 'default_parser_list', 'handler', 'make_parser', 'parse', 'parseString', 'xmlreader'] >>> from xml import * >>> dir(xml) ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'dom', 'etree', 'parsers', 'sax']
表面上看起来,和内容在单个文件内的import机制差不多. 我们可以到xml对应的目录下看看:
erlv@erlv-debian:/usr/lib/python2.7/xml$ ls * __init__.py __init__.pyc __init__.pyo dom: domreg.py expatbuilder.py __init__.py minicompat.py minidom.py NodeFilter.py pulldom.py xmlbuilder.py etree: cElementTree.py ElementInclude.py ElementPath.py ElementTree.py __init__.py parsers: expat.py __init__.py sax: _exceptions.py expatreader.py handler.py __init__.py saxutils.py xmlreader.py
我们import的xmlreader,它的路径是xml/sax/xmlreader.py,和import xml.sax.xmlreader相同.
这实际上也正是python解释器实际的动作.
注意到,每个文件夹下都有一个_init__.py文件.这个是模块包中的必须文件,它帮助python解释器将该目录识别成包.
没有此文件的文件夹,python解释器不会把它当模块包文件夹的.
_init__.py中一般会指定包中所有的模块,以及import此包时,需要预先import哪些包等初始化信息.当然,你可以往里面添加其他代码.
该脚本会在import 包时执行. 默认可以为空.
另外,还注意到有.py,.pyc和.pyo三个文件.
- .py文件:Python源程序文件,文本文件
- .pyc文件:编译成字节码的python文件,可以使用python解释器,或者调用pycompile模块生成该文件.
- .pyo文件:进行一定编译优化的后的字节码文件.
- 另外,还可以控制python解释器,去掉”docstrings”,即代码中的无关文档字符串.
4 总结及深入阅读
从上面的观察中可以看到,其实python的import机制完成的是名字作用域的相关操作.包括作用域的分层,提升和删除等等.
Python中的作用域是一个树状的结构,通过”.”操作,程序员可以进入作用域分支中找到想要的名字.
同时,可以通过from XXX import YYY机制实现将某个树枝上的名字提升到当前作用域中.
所以,python解释器在实现这种作用域机制的时候,需要引入作用域层级的概念.
另外,为了实现这套机制的动态支持,包括提升新名字,名字重命名和名字删除操作.
Python解释器采取了全局模块池的方式.所有的模块在加载后都添加到这个池中.
在通过链表的形式维护树状的逻辑结构.
python中灵活的作用域管理,一方面可以让程序员更加方便的对代码进行模块化管理,另外一方面也增加了灵活性,最大可能的减小当前作用域的名字污染问题.
参考2中的<python源码剖析>中,详细介绍了python解释器中如何支持import动作的.
这部分的实现主要在cpython解释器的import.c文件中.import动作的入口函数是bltinmodule.c的builtin__import__函数.
5 参考
- http://stackoverflow.com/questions/1699108/local-import-statements-in-python
- python源码剖析:深度探索动态语言核心技术 陈儒 电子工业出版社 2008.6
- 可爱的Python 哲思社区著 电子工业出版社 2009.9
在python用import或者from...import来导入相应的模块。模块其实就一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中,我们就可以使用了。这 类似于C语言中的include头文件,Python中我们用import导入我们需要的模块。
eg:
import sys
print('================Python import mode==========================');
print ('The command line arguments are:')
for i in sys.argv:
print (i)
print ('\n The python path',sys.path)
from sys import argv,path#导入特定的成员
print('================python from import===================================')
print('path:',path)
如果你要使用所有sys模块使用的名字,你可以这样:
from sys import *
print('path:',path)
从以上我们可以简单看出:
############################
#导入mode,import与from...import的不同之处在于,简单说:
# 如果你想要直接输入argv变量到你的程序中而每次使用它时又不想打sys,
# 则可使用:from sys import argv
# 一般说来,应该避免使用from..import而使用import语句,
# 因为这样可以使你的程序更加易读,也可以避免名称的冲突
###########################
关于Import中的路径搜索问题
类似于头文件,模块也是需要系统的搜索路径的,下面的命令即是系统默认的搜索路径,当你导入一个模块时,系统就会在下面的路径列表中搜索相应的文件。
>>> print(sys.path)
['D:/xx/PythonSERVER/python31/Code', 'D:\\xx\\PythonSERVER\\python31\\Lib\\idlelib', 'C:\\WINDOWS\\system32\\python31.zip', 'D:\\xx\\PythonSERVER\\python31\\DLLs', 'D:\\xx\\PythonSERVER\\python31\\lib', 'D:\\xx\\PythonSERVER\\python31\\lib\\plat-win', 'D:\\xx\\PythonSERVER\\python31', 'D:\\xx\\PythonSERVER\\python31\\lib\\site-packages']
(从例表中,我们可以看到python会首先在当前工作目录里去找)
如果没有找到相应的内容,则报错:
>>> import syss
Traceback (most recent call last):
File "D:/xxx/xxx/xx/Code/test.py", line 19, in <module>
import syss
ImportError: No module named syss
当然,我们也可以自行添加要搜索路径。调用列表的append方法即可:
import sys
sys.path.append('D:/xx/PythonSERVER/python31/Code')
另注:以上本部分信息摘至:http://blog.sina.com.cn/s/blog_4b5039210100ennq.html
创建自己的模块
在创建之前,有一点需要说明一下:每个Python模块都有它的__name__(就每个对象都自己的__doc__一样)。通过__name__我们可以找出每一个模块的名称,一般__name__的值有种:1 一是主模块名称为:"__main__"(可以理解为直接运行的那个文件),2 那些被主模块导入的模块名称为:文件名字(不加后面的.py)。有__name__是很有用的,因为我们可以通过 if __name__ == 'xxx' 判断来执行那些模块,那些模块不被执行。另外:每个Python程序也是一个模块。它扩展名为:.py扩展名。
下面,我们通过例子来说明:
首先:我们创建模块:mymodel.py
#!/user/bin/python
#Filename:mymodel.py
version = '1.0'
def sayHello():
print ('Hello world')
def modelName():
return __name__#返回它自己的名称
#end of model
以上语句注意:
1 这个模块应该被放置在我们输入它的程序的同一个目录中,或者在sys.path所列目录之一。
2 你已经看到,它与我们普通的Python程序相比并没有什么特别之处
然后:我们在test.py中来调用此模块:test.py
import sys,mymodel
sys.path.append('D:/xx/PythonSERVER/python31/Code')#提供搜索路径
print(__name__) #此处打印主模块的名称:__main__
mymodel.sayHello()
print('Version',mymodel.version)
print('Model Name',mymodel.modelName())#打印被导入模块的名称: mymodel
我们使用from..import...
print('======================from.....import=====================================')
from mymodel import *
print(__name__) #此处打印主模块的名称:__main__
sayHello()
print('Version',version)
print('Model Name',modelName()) #打印被导入模块的名 称: mymodel
以上语句注意:
1 我们可以通过import来导入多个模块,用“,”(逗号)分隔。
2 注意import与from..import.....
更好的处理 Python 多工程 import 依赖
话说, 这段时间需要开发一个项目, 新项目对现有的几乎所有项目都有依赖。 豆瓣现存的几个大项目,基本都是围绕豆瓣主站shire的依赖, 也就是说, 其他项目对shire的单项依赖, 这些项目在需要主站shire模块的时候, 无一例外的将shire的工程路径加入到其他工程的sys.path中, 熟悉Python Import的人一定马上会意识到, 这个并不是什么好方法, 因为这样会造成Python Import的搜索路径产生一些不确定因素, 也就是Import 搜索空间污染, 比如说, 我在anduin(豆瓣说)项目中import 主站(shire)的工程,from luzong.user import User, 看起来好像是很magic的东西, 但是, 假如anduin中也有个luzong目录呢(当然这个没有), 这样就会造成搜索冲突, 严重点的可能隐士的错误的import了其他模块。 也就是说, 这个是很不确定的, 很有危险性的一个事情, 那豆瓣那些个大项目是怎么处理这个magic的危险的呢, 答: 每个新工程的顶级目录都与现存的各个工程的顶级目录不重名。
好吧, 一听就知道是个很蛋疼的想法, 我在skype上问了下anrs, 貌似他是遵守这个规定的。但是,随着内部工程量增多, 每个工程的命名都要考虑其他工程的命名是非常非常蛋疼的事情, 在我个人来看是非常要不得的, 那怎么办呢? 在这篇blog中我就循序渐进的介绍下我这两天探索到的方法, 真的非常的magic, 非常的好用:)
首先, 我们想到的是, 为了不污染import的导入空间, 我们何不把每个项目作为一个可以导入的顶层模块, 这样每个项目的内容都在自己独立的命名空间之内, 就不会出现那种很magic的隐式命名空间污染。 好吧, 这个好办, 在每个工程的顶层目录添加一个__init__.py 的空文件, 然后我们再开发一个类似import_project的东西:
def import_project(path):
sys.path.insert(0, os.path.dirname(path))
project = __import__(os.path.basename(path))
sys.path.remove(os.path.dirname(path))
globals()[os.path.basename(path)] = project
return project
然后,我们在我们项目的初始化文件中import依赖的project:
conf.__init__.py
shire = import_project(cfg.deps.shire)
使用的时候
from conf import shire
from shire.luzong.user import User
哇塞, 确实很magic, 是不是这样就完了呢, 答案往往不是那么简单, 不是那么如你所愿, 想一想anduin工程, 在豆瓣服务器上, 我们开发的时候部署了很多用来测试的工程, 比如shuo-test, anduin-test, auduin , 如果按照上面的方法, 线上的肯定没什么问题, 因为shire就是shire, 那如果我测试的那个工程顶级名目换成shire-test呢, 这下就不能处理了, 所以就有了后边的东西, 诸君且往下看:)
经过了半天的苦苦码字, 终于弄出来了一个还能用的东西, 思想就是, 对import部分进行类似勾子的hack, 让他不对这些shuo-test, anduin-test等类似的magic shring有任何的依赖。 于是开发一个函数, 这个函数实现类似普通import的功能, 但不会造成项目之间搜索空间污染的情况, 而且不依赖于这些类似shuo-test, anduin-test 的magic string, 下面我就来介绍一下我的东西, 完了之后再给出一个demo :)
码字了一天开发了下面这个东西 import_helper.py
# libs.import_helper
# -*- coding: utf-8 -*-
# luoweifeng@douban.comimport os
import sysdef import_deps(locals, path, imps=None):
from conf import cfg
pro_name, m_path = path[:path.find('.')], path[path.find('.')+1:]
re_pro_path = cfg.getByPath('deps.' + pro_name)
re_pro_dir = os.path.dirname(re_pro_path)
re_pro_name = os.path.basename(re_pro_path)# import project
sys.path.insert(0,re_pro_dir)
project = __import__(re_pro_name)
sys.path.remove(re_pro_dir)
locals[pro_name] = projectimp_module = re_pro_name + '.' + m_path
__import__(imp_module)
module = getattr(project, m_path)# add imps to locals
if imps:
for imp in imps:
if isinstance(imp, tuple):
locals[imp[1]] = getattr(module, imp[0])
else:
locals[imp] = getattr(module, imp)
主要的逻辑在这个文件中, 使用起来非常方便, 而且可以处理from XX import A as B等形式。 下面我就做个demo来演示一下:
1. 创建测试环境
$cd && mkdir -p test/test11 test22 && cd test
$touch test11/__init__.py && echo “age = 1″ > test11/app.py
$cd test22
我们这里创建了一个测试目录test, 两个工程test11 和test22, 这个test11 的工程名叫做test(尽管他的目录是test11, 但是就像shuo-test之于anduin一样:)
2. 创建配置文件(这里为了简单期间, 我用了个config的东西)
$mkdir conf && emacs config.cfg
path_prefix : `os.environ['HOME']`
deps :
{
test : $path_prefix + '/test/test11'
}
$emacs conf/__init__.py
import os
import posixpath
from config import Config
config_file = posixpath.abspath('conf/dimholt.cfg')
cfg = Config(file(config_file))
ok , 我们的配置文件就配置好了, 这里, 我配置了我这个工程依赖一个叫做test的工程, 这个工程的目录在我的主目录下test/test11.
3. 将import_helper.py(文章后边提供下载)放在test22目录下的libs目录下。
4. 使用演示
启动python解释器
>>> from libs.import_helper import import_deps
>>> import_deps(locals=locals(), 'test.app')
这样在你的工程中就可以访问test.app了, 如果想模拟from A import B
>>> import_deps(locals=locals(), 'test.app',['age'])
这样就会导入一个叫做age的变量, 如果还想模拟import A as B
>>> import_deps(locals=locals(), 'test.app',
[('age', 'g_age')])
再来个全面开花的
>>> import_deps(locals=locals(), 'test.app',
['age', ('name', 'screen_name')])
怎么样, 是不是很magic
总结一下, 使用这种方法之后解决了两个工程间import的问题, 一个就是import搜索路径污染, 我们通过把每个依赖的工程的import空间限制在每个工程名下面来实现, 第二个就是工程别名问题, 我们使用比较magic的这个开发的函数来解决。 个人感觉还是非常好用的,除了需要多输入一些东西。 但是好处是非常明显的, 还是值得的。
安, 北京:)
后记: (bugfixed)
话说, 本想今天好好看emacs的, 结果突然想到了一个问题, python locals和globals的处理方式是不同的, globals()是可以被修改的, 而locals()是不能被修改的, 所以上面的是不能用的, 尝试了各种试图修改locals的方法都不是很好, 所以更改了下工程的使用方式, 再加了一个对项目import的支持:
import_helper.py
# libs.import_helper
# -*- coding: utf-8 -*-
# luoweifeng@douban.com
import os
import sys
def import_deps(path, imps=None):
if '.' not in path:
pro_name, m_path = path, ''
else:
pro_name, m_path = path[:path.find('.')], path[path.find('.')+1:]
from conf import cfg
re_pro_path = cfg.getByPath('deps.' + pro_name)
re_pro_dir = os.path.dirname(re_pro_path)
re_pro_name = os.path.basename(re_pro_path)
sys.path.insert(0,re_pro_dir)
project = __import__(re_pro_name)
sys.path.remove(re_pro_dir)
if not m_path:
return project
imp_module = re_pro_name + '.' + m_path
__import__(imp_module)
module = getattr(project, m_path)
if imps:
return [getattr(module, imp) for imp in imps]
return module
使用的时候:
>>> from libs.import_helper import import_deps
可以直接import 顶层工程module
>>>shire = import_deps('shire')
也可以import任意工程外module
>>>user = import_deps('shire.luzong.user')
可以指定类似from import的方式
>>>User = import_deps('shire.luzong.user', ['User'])
当然如果你想模拟as的操作
>>>ShireUser = import_deps('shire.luzong.user', ['User'])
后边的部分可以是个list, 表示import多个参数
>>>get_user_rank, User = import_deps('shire.luzong.user',
['get_user_rank', 'User'])
总结, 因为locals空间不能修改, 所以使用这个方法来处理, 如果您能确定可以使用globals空间, 那加上globals的也行, 就跟以前那个方法一样, 不过传递globals参数, 在module层是没有locals的, 在module层传递locals其实是用的globals,切记。