python之模塊導入和包


一、何為模塊

1.一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的后綴。

2.模塊目的和函數,對象等一樣,是為了代碼的復用性而存在的。即一個py程序寫了很多功能,也可以被其他py程序調用。被調用后被作為模塊,此時會產生一個pyc的文件即為這個模塊的緩存文件,相當於編譯了模塊文件使該py模塊被調用的速度變快。一個大的項目一個人肯定是不行的,所以模塊也為程序員相互間的合作提供了便利。

二、模塊妙用

1.import

只需要在py程序開頭加上import 文件名(不能有.py后綴)。例如,模塊的文件名是span.py,那么只需在自己的py程序加import span。

2. import可以在程序的任意位置調用,但一次調用后會駐留內存,下次再調用會直接調用內存中的模塊(python的優化)。

3.個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當做全局名稱空間,這樣我們在編寫自己的模塊時,就不用擔心我們定義在自己模塊中全局變量會在被導入時,與使用者的全局變量沖突。

print('from the spam.py')

money=1000

def read1():
    print('spam->read1->money',1000)

def read2():
    print('spam->read2 calling read')
    read1()

def change():
    global money
    money=0
#test.py
import spam #只在第一次導入時才執行spam.py內代碼,此處的顯式效果是只打印一次'from the spam.py',當然其他的頂級代碼也都被執行了,只不過沒有顯示效果.
import spam
import spam
import spam

'''
執行結果:
from the spam.py
'''
test.py
#測試一:money與spam.money不沖突
#test.py
import spam 
money=10
print(spam.money)

'''
執行結果:
from the spam.py
1000
'''
test1.py
#測試二:read1與spam.read1不沖突
#test.py
import spam
def read1():
    print('========')
spam.read1()

'''
執行結果:
from the spam.py
spam->read1->money 1000
'''
test2.py
#測試三:執行spam.change()操作的全局變量money仍然是spam中的
#test.py
import spam
money=1
spam.change()
print(money)

'''
執行結果:
from the spam.py
1
'''
test3.py

so,在首次導入模塊后,解釋器做了三件事:①為源文件(spam模塊)創建新的名稱空間,在spam中定義的函數和方法若是使用到了global時訪問的就是這個名稱空間。②在新創建的命名空間中執行模塊中包含的代碼③創建名字spam來引用該命名空間

4.模塊可以起別名

if file_format == 'xml':
    import xmlreader as reader
elif file_format == 'csv':
    import csvreader as reader
data=reader.read_date(filename)
#這也體現的程序的歸一化思想

5.一行導入多個模塊(不建議,為了增加可讀性一般一行只加一個)

import sys,os,re

三、import高級使用(form ... import ...)

1. from 語句相當於import,也會創建新的名稱空間,但是將spam中的名字直接導入到當前的名稱空間中,在當前名稱空間中,不需要使用“模塊.”的方式,直接使用名字就可以了。

from spam import read1,read2

2.上述方法也可以使用as起別名

from spam import read1 as read

3.from spam import *

把spam中所有的不是以下划線(_)開頭的名字都導入到當前位置,大部分情況下我們的python程序不應該使用這種導入方式,因為*你不知道你導入什么名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。

from spam import * #將模塊spam中所有的名字都導入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)

'''
執行結果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''

可以在模塊文件使用__all__=[]來控制*導入什么,通常用來發布新版本

__all__=['money','read1']
#在模塊文件加入這個可以選擇導入什么變量

4.考慮到性能的原因,每個模塊只被導入一次,放入字典sys.module中,如果你改變了模塊的內容,你必須重啟程序,python不支持重新加載或卸載之前導入的模塊,

有的同學可能會想到直接從sys.module中刪除一個模塊不就可以卸載了嗎,注意了,你刪了sys.module中的模塊對象仍然可能被其他程序的組件所引用,因而不會被清楚。

特別的對於我們引用了這個模塊中的一個類,用這個類產生了很多對象,因而這些對象都有關於這個模塊的引用。

5.把模塊當作腳本使用

使用"__name__"等於"__main__"

#fib.py

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))
#可以自己測試使用

6.模塊搜索路徑

python解釋器在啟動時會自動加載一些模塊,可以使用sys.modules查看。

模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊。

在初始化后,python程序可以修改sys.path,路徑放到前面的優先於標准庫被加載。

import sys
sys.path.append('/a/b/c/d')
sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索
#首先制作歸檔文件:zip module.zip foo.py bar.py

import sys
sys.path.append('module.zip')
import foo,bar

#也可以使用zip中目錄結構的具體位置
sys.path.append('module.zip/lib/python')

至於.egg文件是由setuptools創建的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。

需要強調的一點是:只能從.zip文件中導入.py,.pyc等文件。使用C編寫的共享庫和擴展塊無法直接從.zip文件中加載(此時setuptools等打包系統有時能提供一種規避方法),且從.zip中加載文件不會創建.pyc或者.pyo文件,因此一定要事先創建他們,來避免加載模塊是性能下降。

 7.小提示

①模塊名字區分大小寫

②可以使用-O或者-OO轉換python命令來減少編譯模塊的大小

1 -O轉換會幫你去掉assert語句
2 -OO轉換會幫你去掉assert語句和__doc__文檔字符串
3 由於一些程序可能依賴於assert語句或文檔字符串,你應該在在確認需要的情況下使用這些選項。

③在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,只有在模塊被加載時,.pyc文件才是更快的

④只有使用import語句是才將文件自動編譯為.pyc文件,在命令行或標准輸入中指定運行腳本則不會生成這類文件,因而我們可以使用compieall模塊為一個目錄中的所有模塊創建.pyc文件

模塊可以作為一個腳本(使用python -m compileall)編譯Python源
 
python -m compileall /module_directory 遞歸着編譯
如果使用python -O -m compileall /module_directory -l則只一層
 
命令行里使用compile()函數時,自動使用python -O -m compileall
 
詳見:https://docs.python.org/3/library/compileall.html#module-compileall

8.標准模塊

ython提供了一個標准模塊庫,一些模塊被內置到解釋器中,這些提供了不屬於語言核心部分的操作的訪問,但它們是內置的,無論是為了效率還是提供對操作系統原語的訪問。這些模塊集合是依賴於底層平台的配置項,如winreg模塊只能用於windows系統。特別需要注意的是,sys模塊內建在每一個python解釋器。

9.dir

內建函數dir是用來查找模塊中定義的名字,返回一個有序字符串列表
import spam
dir(spam)

如果沒有參數,dir()列舉出當前定義的名字

dir()不會列舉出內建函數或者變量的名字,它們都被定義到了標准模塊builtin中,可以列舉出它們,
import builtins
dir(builtins)

四、包

一、概念

無論是import形式還是from...import形式,凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的導入語法。

包的本質就是一個包含__init__.py文件的目錄。

注意:

1.關於包相關的導入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什么位置,在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。

2.對於導入后,在使用時就沒有這種限制了,點的左邊可以是包,模塊,函數,類(它們都可以用點的方式調用自己的屬性)。

3.對比import item 和from item import name的應用場景:
如果我們想直接使用name那必須使用后者

二、from ... import ...

需要注意的是from后import導入的模塊,必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法

三、__init__.py文件

不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,都會依次執行包下的__init__.py文件(我們可以在每個包的文件內都打印一行內容來驗證一下),這個文件可以為空,但是也可以存放一些初始化包的代碼。

四、from glance.api import *

此處是想從包api中導入所有,實際上該語句只會導入包api下__init__.py文件中定義的名字,我們可以在這個文件中定義__all___:

#在__init__.py中定義
x=10

def func():
    print('from api.__init.py')

__all__=['x','func','policy']

此時我們在於glance同級的文件中執行from glance.api import *就導入__all__中的內容(versions仍然不能導入)。

五、絕對導入和相對導入

們的最頂級包glance是寫給別人用的,然后在glance包內部也會有彼此之間互相導入的需求,這時候就有絕對導入和相對導入兩種方式:

絕對導入:以glance作為起始

相對導入:用.或者..的方式最為起始(只能在一個包中使用,不能用於不同目錄內)

在glance/api/version.py

#絕對導入
from glance.cmd import manage
manage.main()

#相對導入
from ..cmd import manage
manage.main()

注意:在使用pycharm時,有的情況會為你多做一些事情,這是軟件相關的東西,會影響你對模塊導入的理解,因而在測試時,一定要回到命令行去執行,模擬我們生產環境,你總不能拿着pycharm去上線代碼吧!!!(因為本人一直使用pycharm)

特別需要注意的是:可以用import導入內置或者第三方模塊,但是要絕對避免使用import來導入自定義包的子模塊,應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。

六、單獨導入包

單獨導入包名稱時不會導入包中所有包含的所有子模塊。

#在與glance同級的test.py中
import glance
glance.cmd.manage.main()

'''
執行結果:
AttributeError: module 'glance' has no attribute 'cmd'

'''

解決方法:

#glance/__init__.py
from . import cmd
#glance/cmd/__init__.py
from . import manage

執行:

#在於glance同級的test.py中
import glance
glance.cmd.manage.main()

 

常用的模塊

#traceback模塊被用來跟蹤異常返回信息

try:  
    raise SyntaxError, "traceback test"  
except:  
    traceback.print_exc() 


#用於打印完整的錯誤信息。
View Code
#subprocess 我們通過標准庫中的subprocess包來fork一個子進程,並運行一個外部的程序。
import subprocess
retcode = subprocess.call(["ls", "-l"])
#和shell中命令ls -a顯示結果一樣
print retcode

"""
shell默認為False,在Linux下,shell=False時, Popen調用os.execvp()執行args指定的程序;shell=True時,如果args是字符串,Popen直接調用系統的Shell來執行args指定的程序,如果args是一個序列,則args的第一項是定義程序命令字符串,其它項是調用系統Shell時的附加參數。
"""
retcode = subprocess.call("ls -l",shell=True)
subprocess

subprocess 待續

 

paramiko模塊提供了ssh及sft進行遠程登錄服務器執行命令和上傳下載文件的功能

# 建立一個sshclient對象
ssh = paramiko.SSHClient()
# 允許將信任的主機自動加入到host_allow 列表,此方法必須放在connect方法的前面
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 調用connect方法連接服務器
ssh.connect(hostname='192.168.2.129', port=22, username='super', password='super')
# 執行命令
stdin, stdout, stderr = ssh.exec_command('df -hl')
# 結果放到stdout中,如果有錯誤將放到stderr中
print(stdout.read().decode())
# 關閉連接
ssh.close()
基於用戶名和密碼的 sshclient 方式登錄
'''

方法1是傳統的連接服務器、執行命令、關閉的一個操作,有時候需要登錄上服務器執行多個操作,比如執行命令、上傳/下載文件,方法1則無法實現,可以通過如下方式來操作
'''
# 實例化一個transport對象
trans = paramiko.Transport(('192.168.2.129', 22))
# 建立連接
trans.connect(username='super', password='super')

# 將sshclient的對象的transport指定為以上的trans
ssh = paramiko.SSHClient()
ssh._transport = trans
# 執行命令,和傳統方法一樣
stdin, stdout, stderr = ssh.exec_command('df -hl')
print(stdout.read().decode())

# 關閉連接
trans.close()
基於用戶名和密碼的 transport 方式登錄
# 指定本地的RSA私鑰文件,如果建立密鑰對時設置的有密碼,password為設定的密碼,如無不用指定password參數
pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
# 建立連接
ssh = paramiko.SSHClient()
ssh.connect(hostname='192.168.2.129',
            port=22,
            username='super',
            pkey=pkey)
# 執行命令
stdin, stdout, stderr = ssh.exec_command('df -hl')
# 結果放到stdout中,如果有錯誤將放到stderr中
print(stdout.read().decode())
# 關閉連接
ssh.close()
基於公鑰密鑰的 SSHClient 方式登錄
# 指定本地的RSA私鑰文件,如果建立密鑰對時設置的有密碼,password為設定的密碼,如無不用指定password參數
pkey = paramiko.RSAKey.from_private_key_file('/home/super/.ssh/id_rsa', password='12345')
# 建立連接
trans = paramiko.Transport(('192.168.2.129', 22))
trans.connect(username='super', pkey=pkey)

# 將sshclient的對象的transport指定為以上的trans
ssh = paramiko.SSHClient()
ssh._transport = trans

# 執行命令,和傳統方法一樣
stdin, stdout, stderr = ssh.exec_command('df -hl')
print(stdout.read().decode())

# 關閉連接
trans.close()
基於密鑰的 Transport 方式登錄

傳文件 SFTP

# 實例化一個trans對象# 實例化一個transport對象
trans = paramiko.Transport(('192.168.2.129', 22))
# 建立連接
trans.connect(username='super', password='super')

# 實例化一個 sftp對象,指定連接的通道
sftp = paramiko.SFTPClient.from_transport(trans)
# 發送文件
sftp.put(localpath='/tmp/11.txt', remotepath='/tmp/22.txt')
# 下載文件
# sftp.get(remotepath, localpath)
trans.close()
實例
'''
以上操作都是基本的連接,如果我們想實現一個類似xshell工具的功能,登錄以后可以輸入命令回車后就返回結果:
'''
import paramiko
import os
import select
import sys

# 建立一個socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 啟動一個客戶端
trans.start_client()

# 如果使用rsa密鑰登錄的話
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 如果使用用戶名和密碼登錄
trans.auth_password(username='super', password='super')
# 打開一個通道
channel = trans.open_session()
# 獲取終端
channel.get_pty()
# 激活終端,這樣就可以登錄到終端了,就和我們用類似於xshell登錄系統一樣
channel.invoke_shell()
# 下面就可以執行你所有的操作,用select實現
# 對輸入終端sys.stdin和 通道進行監控,
# 當用戶在終端輸入命令后,將命令交給channel通道,這個時候sys.stdin就發生變化,select就可以感知
# channel的發送命令、獲取結果過程其實就是一個socket的發送和接受信息的過程
while True:
    readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
    # 如果是用戶輸入命令了,sys.stdin發生變化
    if sys.stdin in readlist:
        # 獲取輸入的內容
        input_cmd = sys.stdin.read(1)
        # 將命令發送給服務器
        channel.sendall(input_cmd)

    # 服務器返回了結果,channel通道接受到結果,發生變化 select感知到
    if channel in readlist:
        # 獲取結果
        result = channel.recv(1024)
        # 斷開連接后退出
        if len(result) == 0:
            print("\r\n**** EOF **** \r\n")
            break
        # 輸出到屏幕
        sys.stdout.write(result.decode())
        sys.stdout.flush()

# 關閉通道
channel.close()
# 關閉鏈接
trans.close()
實現輸入命令立馬返回結果的功能
import paramiko
import os
import select
import sys
import tty
import termios

'''
實現一個xshell登錄系統的效果,登錄到系統就不斷輸入命令同時返回結果
支持自動補全,直接調用服務器終端

'''
# 建立一個socket
trans = paramiko.Transport(('192.168.2.129', 22))
# 啟動一個客戶端
trans.start_client()

# 如果使用rsa密鑰登錄的話
'''
default_key_file = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
prikey = paramiko.RSAKey.from_private_key_file(default_key_file)
trans.auth_publickey(username='super', key=prikey)
'''
# 如果使用用戶名和密碼登錄
trans.auth_password(username='super', password='super')
# 打開一個通道
channel = trans.open_session()
# 獲取終端
channel.get_pty()
# 激活終端,這樣就可以登錄到終端了,就和我們用類似於xshell登錄系統一樣
channel.invoke_shell()

# 獲取原操作終端屬性
oldtty = termios.tcgetattr(sys.stdin)
try:
    # 將現在的操作終端屬性設置為服務器上的原生終端屬性,可以支持tab了
    tty.setraw(sys.stdin)
    channel.settimeout(0)

    while True:
        readlist, writelist, errlist = select.select([channel, sys.stdin,], [], [])
        # 如果是用戶輸入命令了,sys.stdin發生變化
        if sys.stdin in readlist:
            # 獲取輸入的內容,輸入一個字符發送1個字符
            input_cmd = sys.stdin.read(1)
            # 將命令發送給服務器
            channel.sendall(input_cmd)

        # 服務器返回了結果,channel通道接受到結果,發生變化 select感知到
        if channel in readlist:
            # 獲取結果
            result = channel.recv(1024)
            # 斷開連接后退出
            if len(result) == 0:
                print("\r\n**** EOF **** \r\n")
                break
            # 輸出到屏幕
            sys.stdout.write(result.decode())
            sys.stdout.flush()
finally:
    # 執行完后將現在的終端屬性恢復為原操作終端屬性
    termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)

# 關閉通道
channel.close()
# 關閉鏈接
trans.close()
支持tab自動補全

 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple nympy  使用清華源,5分更新一次。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM