南天一柱 发表于 2018-8-13 09:25:34

CrazyWing:Python自动化运维开发实战 十五、Python模块

导语:
  模块让你能够有逻辑地组织Python代码段。把相关的代码分配到一个模块里能让你的代码更好用,更易懂。模块也是Python对象,具有随机的名字属性用来绑定或引用。简单地说,模块就是一个保存了Python代码的文件。模块能定义函数,类和变量。模块里也能包含可执行的代码。

相关站点:
  https://pypi.python.org/pypi/
  PyPI(Python Package Index,PyPI) python包索引
  为Internet上的第三方Python模块提供一个集中的存储库

制作模块
  模块结构和布局:
  用模块来合理组织你的 Python 代码是简单又自然的方法。你应该建立一种统一且容易阅读的结构,并将它应用到每一个文件中去。
  下面就是一种非常合理的布局,一个典型模块的内部结构:
  

    # (1) 起始行(Unix)  # (2) 模块文档
  # (3) 模块导入
  # (4) 变量定义
  # (5) 类定义
  # (6) 函数定义
  # (7) 主程序
  

  例:
  一个叫做aname的模块里的Python代码一般都能在一个叫aname.py的文件中找到。
  下例是个简单的模块mod.py:
  

# cat mod.py  
#!/usr/bin/env python
  
# coding=utf8
  
'''
  
这是一个模块实例
  
'''
  
import sys,os
  
name="wing"
  

  
def hello():
  print "hello world"
  

  
if __name__ =="__main__":
  hello()
  

  
使用模块:
  
In : import mod
  

  
In : mod.hello()
  
hello world
  

  
In : print mod.__doc__
  
这是一个模块实例
  

  
In : print mod.name
  
wing
  

import():
  查询模块是内建模块还是属于某一个模块文件
  

In : __import__("sys")  
Out: <module 'sys' (built-in)>
  

  
In : __import__("os")
  
Out: <module 'os' from '/usr/lib64/python2.7/os.pyc'>
  

  
In : __import__("os").__file__
  
Out: '/usr/lib64/python2.7/os.pyc'
  

内建变量name
  可以使用两种方法调用main函数,但是每次在python解释器里第一次import模块pysysinfo_func时都会执行main函数如果又想可以把模块当脚本一样在命令行整体执行,又想在其他脚本调用模块内单独的某一个函数,可以使用第二种方式
  

#vim pysysinfo_func.py  
#!/usr/bin/env python
  
import subprocess
  
def uname_func():
  uname = "uname"
  uname_arg = "-a"
  print "Gathering system information with %s command:\n" % uname
  subprocess.call()
  

  
def disk_func():
  diskspace = "df"
  diskspace_arg = "-Th"
  print "Gathering diskspace information with %s command:\n" % diskspace
  subprocess.call()
  

  
def main():
  uname_func()
  disk_func()
  

  
#第一种方式:
  
main()
  

  
#第二种方式:
  
if __name__ == "__main__":
  main()
  

  
第一种方式的结果如下:会在导入模块的时候执行main函数
  
In : import pysysinfo_func
  
Gathering system information with uname command:
  

  
Linux vm2.up.com 2.6.32-358.el6.x86_64 #1 SMP Tue Jan 29 11:47:41 EST 2013 x86_64 x86_64 x86_64 GNU/Linux
  
Gathering diskspace information with df command:
  


  
Filesystem    Type   >  
/dev/mapper/VolGroup-lv_root
  ext4   18G4.1G   13G25% /
  
tmpfs      tmpfs    565M224K565M   1% /dev/shm
  
/dev/sda1   ext4    485M   34M427M   8% /boot
  
/dev/sr0   iso9660    3.5G3.5G   0 100% /mnt
  

  
第二种方式的结果如下:
  
In : import pysysinfo_func    //不会在导入模块的时候执行main函数
  

  
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  

  
python中__name__的使用
  
1. 如果模块是被导入,__name__的值为模块名字
  
2. 如果模块是被直接执行,__name__的值为’__main__’
  

  
Py1.py
  
#!/usr/bin/env python
  
def test():
  print    '__name__ = ',__name__
  

  
if __name__ == '__main__':
  test()
  

  
Py2.py
  
#!/usr/bin/env python
  
import Py1.py
  
def test():
  print'__name__ = ',__name__
  

  
if __name__ == '__main__':
  test()
  print ‘Py1.py __name__ = ’,Py1.__name__
  

  
执行结果:
  __name__=__main__
  Py1.py __name__=Py1
  

  
通过结果可以知道,Py2.py直接执行,那么内建变量__name__的值为__main__,否则为
  
模块的名字,通过这个特性可以
  
在if语句里面添加测试代码,可以提高减少BUG,提高程序的健壮性。
  
if __name__ == '__main__':
  test()
  

查看模块:

查看模块存放位置
  通过查看模块存放路径就知道我们自己制作的模块应该放在哪个位置
  查看python默认的模块存放路径。
  

>>> import sys  
>>> sys.path
  
['', '/usr/local/python27/lib/python27.zip', '/usr/local/python27/lib/python2.7', '/usr/local/python27/lib/python2.7/plat-linux2', '/usr/local/python27/lib/python2.7/lib-tk', '/usr/local/python27/lib/python2.7/lib-old', '/usr/local/python27/lib/python2.7/lib-dynload', '/usr/local/python27/lib/python2.7/site-packages']
  
>>>
  

  列表内的第一个元素''表示当前工作目录,也就是说模块在当前目录下也可使用默认存放模块的目录是:/usr/local/python27/lib/python2.7/

查看模块内定义的各种标识符号
  dir()函数
  dir()函数返回一个排好序的字符串列表,内容是一个模块里定义过的名字。
  返回的列表容纳了在一个模块里定义的所有模块,变量和函数。
  实例:
  

#!/usr/bin/python  
import math
  
content = dir(math)
  
print content;
  

  
输出结果:
  
['__doc__', '__file__', '__name__', 'acos', 'asin', 'atan',
  
'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp',
  
'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log',
  
'log10', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh',
  
'sqrt', 'tan', 'tanh']
  

  在这里,特殊字符串变量name指向模块的名字,file指向该模块的导入文件名。

调用模块

import 语句
  想使用Python模块,只需在另一个源文件里执行import语句
  语法如下:
  import module1[, module2[,... moduleN]
  当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。
  搜索路径:
  是一个解释器会先进行搜索的所有目录的列表。如想要导入模块support.py,需要把命令放在脚本的顶端
  

    #!/usr/bin/python  # -*- coding: UTF-8 -*-
  # 导入模块
  import support
  # 现在可以调用模块里包含的函数了
  support.print_func("Zara")
  

  
以上实例输出结果:
  Hello : Zara
  

一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行。  

From…import 语句
  Python的from语句让你从模块中导入一个指定的部分到当前命名空间中。语法如下:
  from modname import name1[, name2[, ... nameN]]
  例如,要导入模块fib的fibonacci函数,使用如下语句:
  from fib import fibonacci
  这个声明不会把整个fib模块导入到当前的命名空间中,它只会将fib里的fibonacci单个引入到执行这个声明的模块的全局符号表。

From…import* 语句
  把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:
  from modname import *
  这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

定位模块
  当你导入一个模块,Python解析器对模块位置的搜索顺序是:
  

· 当前目录  
· 如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录。
  
· 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。
  

  模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。
  PYTHONPATH变量
  作为环境变量,PYTHONPATH由装在一个列表里的许多目录组成。PYTHONPATH的语法和shell
  变量PATH的一样。
  在Windows系统,典型的PYTHONPATH如下:
  set PYTHONPATH=c:\python20\lib
  在UNIX系统,典型的PYTHONPATH如下:
  set PYTHONPATH=/usr/local/lib/python

python解释器开启时调用模块
  正常情况下使用python解释器,使用模块的方法时需要导入模块,一些自己比较常用的模块比如os、process每次开始python解释器都得重新调用,下面的方法可以为你解除痛苦。
  非常简单,只需设置一个变量,变量的值为某个.py文件的路径,在.py文件内设置预先想要导入的模块
  

# export PYTHONSTARTUP=/a.py  

  
# cat /a.py
  
import os
  
import subprocess
  

  
# python
  
>>> os.system("ls")       //可以看到测试的OS模块的时候可以直接使用,无需事先导入
  
模块OS
  
anaconda-ks.cfgDocuments    install.log.syslogPublic
  
a.py         Downloads    Music       Templates
  
我们可以利用上面得方法把tab键功能写入模块内事先导入
  

重载模块
  reload()函数
  当一个模块被导入到一个脚本,模块顶层部分的代码只会被执行一次。因此,如果你想重新执行模块里顶层部分的代码,可以用reload()函数。该函数会重新导入之前导入过的模块。
  语法如下:
  reload(module_name)
  在这里,module_name要直接放模块的名字,而不是一个字符串形式。
  比如想重载hello模块,如下:
  reload(hello)
  

1.同一个模块导入,第一次代码全部运行,第二次很多的代码都不运行的,其实中间只是重复执行  

  
2.尝试在第一次导入后,修改源文件,然后第二次导入,结果跟第一次一样。
  

  原因:
  

导入操作的开销非常大,它把文件先编译成字节码,然后再导pvm(python virtual machine)上去执行,在编译的过程中,消耗资源非常多,所以,导入操作只编译执行一次,第二次只是重复执行,不再编译。  
如果想再次执行完整的代码,就需要reload()这个函数,他会把源代码重新载入,然后执行一遍,但是在执行reload前,必须保证已经import那个模块。

  
注意:在python3里,执行reload前,请先执行from imp import>  

  例:
  

1.编写模块a.py  
#!/usr/bin/env python
  
print "hello world"
  

  
2.进入Ipython倒入模块
  
In : import a
  
hello world    第一次导入结果有输出
  

  
In : import a
  第二次导入结果就没有输出了
  
In : import a
  修改源文件后再次导入仍然没有输出结果
  
In :
  
必须的用reload()重载模块才行
  

发布自己的模块
  以之前处理嵌套列表的代码为例,把他做成可以发给别人使用的模块
  1.创建父目录:
  #mkdir /nester
  2.准备源代码文件:
  #vim /nester/nester.py
  #!/usr/bin/env python
  #coding=utf-8
  &quot;这是模块文档字符串&quot;
  def print_list(name):
  &quot;这是函数文档字符串&quot;
  for each_item in name:
  if isinstance(each_item,list):
  print_list(each_item)
  else:
  print each_item
  3.准备setup.py文件:

vim /nester/setup.py
  

    #!/usr/bin/env python  from distutils.core import setup
  setup(
  name = 'nester',
  version = '1.0.0',
  py_modules = ['nester'],
  author = 'wing',
  author_email = '276267003@qq.com',
  url = 'www.fklinux.com',
  description = 'A simple printer of nested lists',
  )
  

  4.构建发布文件:

cd /nester
  

    # python setup.py sdist  

  5.构建成功之后/nester目录下会出现dist目录,dist目录下会出现构建好的模块打包文件,这个文件就可以发给别人使用了
  # ls dist/
  nester-1.0.0.tar.gz
  6.测试安装模块:
  #tar xvzf nester-1.0.0.tar.gz
  #cd nester-1.0.0
  #python setup.py install
  #python

  >> import nester            //其实到这里,如果你能导入模块成功的话,恭喜你,^_^说明你的模块儿没问题了,下面是这个模块的具体使用
  >> a=]]
  >> nester.print_list(a)
  1
  2
  3
  4
  5
  6
  7

Python中的包
  包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的Python的应用环境。

  例子:
  

1.创建目录Phone,作为包名  
#mkdir /Phone
  

  
2.分别创建模块文件pots.py、lsdn.py和G3.py
  
#cat /Phone/pots.py
  
#!/usr/bin/python
  
def Pots():
  print "I'm Pots Phone"
  

  
#cat /Phone/Isdn.py
  
#!/usr/bin/python
  
def Lsdn():
  print "I'm lsdn Phone"
  

  
#cat /Phone/G3.py
  
#!/usr/bin/python
  
def G3():
  print "I'm G3 Phone"
  

  
3.在/Phone目录下创建文件 __init__.py:
  
#cat /Phone/__init__.py
  
from Pots import Pots
  
from Isdn import Isdn
  
from G3 import G3
  

  
4.使用包:
  
#!/usr/bin/python
  
# 导入 Phone 包
  
import Phone
  

  
Phone.Pots()
  
Phone.Isdn()
  
Phone.G3()
  

  
输出结果:
  
I'm Pots Phone
  
I'm 3G Phone
  
I'm ISDN Phone
  
如上,为了举例,只在每个文件里放置了一个函数,但其实你可以放置许多函数。你也可以在这些文件里定义Python的类,然后为这些类建一个包。
  

制作tab补全模块:
  如果在python编辑器里tab键不能补全,可以自己定义一个tab.py程序,然后当作模块导入就可以使用tab键补全了
  

#cat tab.py  
#!/usr/bin/env python
  
import sys,readline,rlcompleter,os
  
readline.parse_and_bind('tab: complete')
  
histfile = os.path.join(os.environ['HOME'],'.pythonhistory')
  

  把自己编写的tab模块拷贝到默认模块目录下,如果不拷贝到默认位置也可以使用,不过需要在进入python shell的当前目录下存放tab.py
  #mv tab.py /usr/local/python27/lib/python2.7/
  tab模块使用:

  >> import tab

  上面的方式在使用tab模块的时候跟使用其他模块一样每次都需要手动导入,如果你像我一样比较懒^_^,可以使用下面的方法,在启动python shell的时候就自动导入我们想让他导入的模块

python shell自动导入模块:
  1.创建一个脚本文件:

cat /root/.startup.py
  

    #!/usr/bin/evn python  import tab
  print "module loaded"
  

  2.设置PYTHONSTARTUP环境变量:
  #export PYTHONSTARTUP=/root/.startup.py   //如何永久生效?你懂的
  3.测试:

python
  

    Python 2.7.10 (default, Dec 13 2016, 10:54:54)   on linux2
  Type "help", "copyright", "credits" or "license" for more information.
  module loaded
页: [1]
查看完整版本: CrazyWing:Python自动化运维开发实战 十五、Python模块