Python 模块

概念:

对于一个复杂的功能,为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式。

在Python中,一个.py文件就称之为一个模块(Module)。即一个文件被看作一个独立的模块,一个模块也可以被看做是一个文件

使用模块可以避免函数名和变量名冲突。相同名字的函数和变量完全可以分别存在不同的模块中,因此,我们自己在编写模块时,不必考虑名字会与其他模块冲突。但是也要注意,尽量不要与内置函数名字冲突。 

 

模块分为三种:

  • 内置模块:如sys, os, subprocess, time, json 等等
  • 自定义模块:自定义模块时要注意命名,不能和Python自带的模块名称冲突。例如,系统自带了sys模块,自己的模块就不可命名为sys.py,否则将无法导入系统自带的sys模块。
  • 开源模块:公开的第三方模块, 如 https://pypi.python.org/pypi 。可以使用pip install 安装,类似于yum 安装软件

>>> help('modules')

查看python所有的modules

 

一个Python程序通常包括一个顶层程序文件和其它的模块文件
顶层文件:包含了程序的主要控制流程
模块文件:为顶层文件或其他模块提供各种功能性组件
模块首次导入import(或重载reload)时,Python会立即执行模块文件的顶层程序代码(不在函数内的代码),而位于函数主体内的代码直到函数被调用后才会执行

 

导入模块

Python允许“导入”其它模块以实现代码重用,从而也实现了将独立的代码文件组织成更大的程序系统。模块是被导入的,但模块也可以导入和使用其他模块,这些模块可以用Python或其他编程语言写成

在导入模块时只能使用模块名,而不是使用带.py后缀的模块文件名

1. import 语句, 用于导入整个模块

import module1, module2....  # 建议一个import语句只导入一个模块
import module as module_alias  # 别名(也就是自定义的模块名称空间)

>>> import sys,os

>>> import random as rd

 

2. from-import 语句 , 常用于只导入指定模块的部分属性或模糊导入
from module import name1,name2....

from pack1.pack2 import *
>>> from random import choice,random,seed

 

import和from import是赋值语句
import和from可执行语句,类似于def,因此,它们可以嵌套在if测试中,出现于def中等等
Python执行到这些语句时才会对其进行解析,这意味着,所有来自模块的属性仅在import语句执行后才能使用

 

import的工作机制
import语句导入指定的模块时会执行3个步骤
1. 找到模块文件:在模块搜索路径下搜索模块文件
  程序的主目录
  PYTHONPATH目录
  标准链接库目录

2.编译成字节码:文件导入时会编译,因此,顶层文件的.pyc字节码文件在内部使用后会被丢弃,只有被导入的文件才会留下.pyc文件
3.执行模块的代码来创建其所定义的对象:模块文件中的所有语句从头至尾依次执行,而此步骤中任何对变量名的赋值运算,都会产生所得到的模块文件的属性
注意:模块只在第一次导入时才会执行如上步骤,后续的导入操作只不过是提取内存中已加载的模块对象,reload()可用于重新加载模块

 

包: 用于将一组模块归并到一个目录中,此目录即为包,目录名即为包名

包是一个有层次的文件目录结构,它定义了一个由模块和子包组成的Python应用执行环境

基于包,Python在执行模块导入时可以指定模块的导入路径 import pack1.pack2.mod1

每个包内都必须有__init__.py文件, __init__.py 可包含python代码,但通常为空,仅用于扮演包初始化、替目录产生模块命名空间以及使用目录导入时实现from*行为的角色

 

假设有程序结构及依赖关系如下:

web/

├── __init__.py
├── backend

│   ├── __init__.py
│   ├── config

│   │   ├── __init__.py
│   │   └── settings.py  
│   ├── db

│   │   ├── __init__.py
│   │   ├── authentic.py
│   │   └── sqlapi.py  # 需要导入settings和authentic模块
│   └── logic

│   │   ├── __init__.py
│   │   └── handle.py  # 需要导入 sqlapi 模块中的select 函数
├── frontend
└── main.py   #需要导入handle模块

#!/usr/bin/env python
''' main module '''
import sys
from backend.logic import handle
print(sys.path) # 交互模式下可以直接sys.path,在交互模式下打印有所不同

#!/usr/bin/env python
''' handle module '''
from backend.db.sqlapi import select

#!/usr/bin/env python
''' sqlapi module '''
from backend.config import settings
from backend.db import authentic

main.py 执行结果:
['/root/scripts/python/web', '/root/.pyenv/versions/3.6.1/lib/python36.zip', '/root/.pyenv/versions/3.6.1/lib/python3.6', '/root/.pyenv/versions/3.6.1/lib/python3.6/lib-dynload', '/root/.pyenv/versions/3.6.1/lib/python3.6/site-packages']
/root/scripts/python/web 程序主目录
#/root/.pyenv/versions/3.6.1/
* PYTHONPATH目录 和 标准链接库目录,site-packages 存放第三方开源模块
注意:如果直接执行sqlapi.py 会抛出异常 ModuleNotFoundError: No module named 'backend' 。因为sqlapi模块所在路径下并没有backend目录,所以找不到路径,而我们执行main.py之所以可以正常导入,因为backend目录处于main所在的路径下。
但我们在编写一个模块时往往需要单独调试,此时我们可以把需要导入模块的正确路径添加到sys.path

修改sqlapi.py 如下:
#!/usr/bin/env python
''' sqlapi module '''
import
os,sys basedir=os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(basedir) print(sys.path)
from backend.config import settings
from backend.db import authentic
执行结果: [
'/root/scripts/python/web/backend/db', '/root/.pyenv/versions/3.6.1/lib/python36.zip', '/root/.pyenv/versions/3.6.1/lib/python3.6', '/root/.pyenv/versions/3.6.1/lib/python3.6/lib-dynload', '/root/.pyenv/versions/3.6.1/lib/python3.6/site-packages', '/root/scripts/python/web']

也可以使用相对路径:

#!/usr/bin/env python
''' sqlapi module '''

 import os,sys

  sys.path.append('..')  # .. 表示上一级目录

  print(sys.path)
  from config import settings
  from db import authentic


执行结果:
['/root/scripts/python/web/backend/db', '/root/.pyenv/versions/3.6.1/lib/python36.zip', '/root/.pyenv/versions/3.6.1/lib/python3.6', '/root/.pyenv/versions/3.6.1/lib/python3.6/lib-dynload', '/root/.pyenv/versions/3.6.1/lib/python3.6/site-packages', '..']

内置变量 __file__:表示当前的文件名

os.path.abspath(path) :返回path的绝对路径  

os.path.dirname(path) :返回path的目录(去掉最后一级目录或文件)

 

一个模块文件可以直接执行或被导入

每个模块都有一个名为__name__的内置变量,此变量值会根据调用此模块的方式发生变化:
1. 如果此文件被作为模块导入,则__name__的值为模块名称
2. 如果此文件被直接执行,则__name__的值为“__main__”

我们在创建模块时,在尾部添加如下代码进行模块的自我测试
if __name__ == '__main__':

  ...

如果模块被导入,则 if 后的语句就不会执行

 

第三方模块

官方文档:https://docs.python.org/3/installing/#

安装第三方模块有两种方式:

1. 通过包管理工具pip完成的

如果你正在使用Mac或Linux,安装pip本身这个步骤就可以跳过

如果使用Windows,确保安装时勾选了pipAdd python.exe to Path

注意:Mac或Linux上有可能并存Python 3.x和Python 2.x,因此对应的pip命令是pip3

2. 下载源码=> 解压源码=> 进入目录=> 编译源码:python setup.py build => 安装源码: python setup.py install  

在使用源码安装时,需要使用到gcc编译 #yum install gcc

 

以 paramiko模块为例

paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,fabric和ansible内部的远程管理就是使用的paramiko来现实。

# python -m pip install paramiko

No package 'libffi' found

# yum install -y libffi-devel

 

导入和使用模块

#!/usr/bin/env python
''' 通过用户名和密码连接服务器 '''
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect('10.104.229.168', 22, 'root', 'password') stdin,stdout,stderr = ssh.exec_command('ifconfig') print(stdout.read()) ssh.close();

执行结果:
b'eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500\n        inet 10.104.229.168  netmask 255.255.192.0  broadcast 10.104.255.255\n        ether 52:54:00:e3:90:ac  txqueuelen 1000  (Ethernet)\n        RX packets 12475918  bytes 2316344006 (2.1 GiB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 12179205  bytes 1357889151 (1.2 GiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\nlo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536\n        inet 127.0.0.1  netmask 255.0.0.0\n        loop  txqueuelen 0  (Local Loopback)\n        RX packets 68  bytes 5552 (5.4 KiB)\n        RX errors 0  dropped 0  overruns 0  frame 0\n        TX packets 68  bytes 5552 (5.4 KiB)\n        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0\n\n'

 

posted @ 2017-06-11 17:46  bobo0609  Views(3208)  Comments(0Edit  收藏  举报