概念:
對於一個復雜的功能,為了編寫可維護的代碼,我們把很多函數分組,分別放到不同的文件里,這樣,每個文件包含的代碼就相對較少,很多編程語言都采用這種組織代碼的方式。
在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,確保安裝時勾選了pip
和Add 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'