自定義模塊


1. 模塊的定義與分類

    模塊是什么?

    這幾天,我們進入模塊的學習。在學習模塊之前,我們首先要知道,什么是模塊?

    一個函數封裝一個功能,你使用的軟件可能就是由n多個函數組成的(先備考慮面向對象)。比如抖音這個軟件,不可能將所有程序都寫入一個文件,所以咱們應該將文件划分,這樣其組織結構要好並且代碼不冗余。加入分了10個文件,每個文件里面可能都有相同的功能(函數),怎么辦?所以將這些相同的功能封裝到一個文件中,那么這個存儲着很多常用的功能的py文件,就是模塊。 模塊就是文件,存放一堆常用的函數,誰用誰拿。怎么拿?比如:我要策馬奔騰共享人世繁華,應該怎么樣?我應該騎馬,你也要去浪,你是不是也要騎馬。 我們說一個函數就是一個功能,那么把一些常用的函數放在一個py文件中,這個文件就稱之為模塊,模塊,就是一些列常用功能的集合體。

    為什么要使用模塊?

  1. 從文件級別組織程序,更方便管理 隨着程序的發展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結構更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執行,還可以把他們當做模塊來導入到其他的模塊中,實現了功能的重復利用

  1. 拿來主義,提升開發效率 同樣的原理,我們也可以下載別人寫好的模塊然后導入到自己的項目中使用,這種拿來主義,可以極大地提升我們的開發效率,避免重復造輪子。

    ps:人們常說的腳本是什么?

如果你退出python解釋器然后重新進入,那么你之前定義的函數或者變量都將丟失,因此我們通常將程序寫到文件中以便永久保存下來,需要時就通過python test.py方式去執行,此時test.py被稱為腳本script。

所以,腳本就是一個python文件,比如你之前寫的購物車,模擬博客園登錄系統的文件等等。

    模塊的分類

Python語言中,模塊分為三類。

    第一類:內置模塊,也叫做標准庫。此類模塊就是python解釋器給你提供的,比如我們之前見過的time模塊,os模塊。標准庫的模塊非常多(200多個,每個模塊又有很多功能),我們這幾天就講常用的十幾種,后面課程中還會陸續的講到。

    第二類:第三方模塊,第三方庫。一些python大神寫的非常好用的模塊,必須通過pip install 指令安裝的模塊,比如BeautfulSoup, Django,等等。大概有6000多個。

    第三類:自定義模塊。我們自己在項目中定義的一些模塊。

這幾天,我們先學第一類和第三類模塊,第二類模塊會在我們並發編程開始逐漸的接觸學習。

今天,我們先講第三類,自定義模塊。

我們先定義一個模塊,定義一個模塊其實很簡單就是寫一個文件,里面寫一些代碼(變量,函數)即可。此文件的名字為tbjx.py,文件內容如下:

print('from the tbjx.py')
name = '太白金星'def read1():
    print('tbjx模塊:',name)
​
def read2():
    print('tbjx模塊')
    read1()
​
def change():
    global name
    name = 'barry'
View Code

2. import

2.1 import 使用

import 翻譯過來是一個導入的意思。

這里一定要給同學強調那個文件執行文件,和哪個文件是被執行模塊。   

     模塊可以包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行(import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優化手段是:第一次導入后就將模塊名加載到內存了,后續的import語句僅是對已經加載到內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句),如下 ​import tbjx #只在第一次導入時才執行tbjx.py內代碼,此處的顯式效果是只打印一次'from the tbjx.py',當然其他的頂級代碼也都被執行了,只不過沒有顯示效果.

代碼示例:

import tbjx
import tbjx
import tbjx
import tbjx
import tbjx
​
​
執行結果:只是打印一次:
from the tbjx.py
View Code


2.2 第一次導入模塊執行三件事

        1.創建一個以模塊名命名的名稱空間。

        2.執行這個名稱空間(即導入的模塊)里面的代碼。

        3.通過此模塊名. 的方式引用該模塊里面的內容(變量,函數名,類名等)。 這個名字和變量名沒什么區別,都是‘第一類的’,且使用tbjx.名字的方式可以訪問tbjx.py文件中定義的名字,tbjx.名字與test.py中的名字來自兩個完全不同的地方。

    ps:重復導入會直接引用內存中已經加載好的結果

2.3 被導入模塊有獨立的名稱空間

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

示例:

當前是meet.py

import tbjx.py
name = 'alex'
print(name)
print(tbjx.name)
'''
from the tbjx.py
alex
太白金星
'''def read1():
    print(666)
tbjx.read1()
'''
from the tbjx.py
tbjx模塊: 太白金星
'''
​
name = '日天'
tbjx.change()
print(name)
print(tbjx.name)
'''
from the tbjx.py
日天
barry
'''
View Code

讓同學們將上面的代碼練習一下。

2.4 為模塊起別名

別名其實就是一個外號,我們小的時候,都喜歡給學生們起外號對吧。

        1. 好處可以將很長的模塊名改成很短,方便使用.

import tbjx as t
t.read1()
View Code

        2. 有利於代碼的擴展和優化。

#mysql.py
def sqlparse():
    print('from mysql sqlparse')
#oracle.py
def sqlparse():
    print('from oracle sqlparse')
​
#test.py
db_type=input('>>: ')
if db_type == 'mysql':
    import mysql as db
elif db_type == 'oracle':
    import oracle as db
​
db.sqlparse()
View Code

2.5 導入多個模塊

    我們以后再開發過程中,免不了會在一個文件中,導入多個模塊,推薦寫法是一個一個導入。

import os,sys,json   # 這樣寫可以但是不推薦
​
推薦寫法
​
import os
import sys
import json
View Code

  多行導入:易於閱讀 易於編輯 易於搜索 易於維護。

3 from ... import ...

3.1 from ... import ... 使用

from ... import ... 的使用示例。

from tbjx import name, read1
print(name)
read1()
'''
執行結果:
from the tbjx.py
太白金星
tbjx模塊: 太白金星
​
'''
View Code

3.2 from...import... 與import對比

    唯一的區別就是:使用from...import...則是將spam中的名字直接導入到當前的名稱空間中,所以在當前名稱空間中,直接使用名字就可以了、無需加前綴:tbjx.

from...import...的方式有好處也有壞處

    好處:使用起來方便了

    壞處:容易與當前執行文件中的名字沖突

示例演示:

  1. 執行文件有與模塊同名的變量或者函數名,會有覆蓋效果。

name = 'oldboy'
from tbjx import name, read1, read2
print(name)  
'''
執行結果:
太白金星
'''
----------------------------------------
from tbjx import name, read1, read2
name = 'oldboy'
print(name)  
​
'''
執行結果:
oldboy
​
'''
----------------------------------------
def read1():
    print(666)
from tbjx import name, read1, read2
read1()
​
'''
執行結果:
tbjx模塊: 太白金星
'''
----------------------------------------from tbjx import name, read1, read2
def read1():
    print(666)
read1()
​
'''
執行結果:
tbjx模塊: 666
'''
View Code

     2. 當前位置直接使用read1和read2就好了,執行時,仍然以tbjx.py文件全局名稱空間

#測試一:導入的函數read1,執行時仍然回到tbjx.py中尋找全局變量 'alex'
#test.py
from tbjx import read1
name = 'alex'
read1()
'''
執行結果:
from the spam.py
spam->read1->name = '太白金星'
'''#測試二:導入的函數read2,執行時需要調用read1(),仍然回到tbjx.py中找read1()
#test.py
from tbjx import read2
def read1():
    print('==========')
read2()
​
'''
執行結果:
from the tbjx.py
tbjx->read2 calling read
tbjx->read1->tbjx 'barry'
'''
    4.3.3 也支持as

通過這種方式引用模塊也可以對模塊進行改名。

from tbjx import read1 as read
read()
View Code

3.4 一行導入多個

from tbjx import read1,read2,name

3.5 from ... import *

    from spam import *  把tbjx中所有的不是以下划線(_)開頭的名字都導入到當前位置

    大部分情況下我們的python程序不應該使用這種導入方式,因為*你不知道你導入什么名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。

可以使用all來控制*(用來發布新版本),在tbjx.py中新增一行

__all__=['money','read1'] #這樣在另外一個文件中用from spam import *就這能導入列表中規定的兩個名字

3.6 模塊循環導入問題

    模塊循環/嵌套導入拋出異常的根本原因是由於在python中模塊被導入一次之后,就不會重新導入,只會在第一次導入時執行模塊內代碼

    在我們的項目中應該盡量避免出現循環/嵌套導入,如果出現多個模塊都需要共享的數據,可以將共享的數據集中存放到某一個地方在程序出現了循環/嵌套導入后的異常分析、解決方法如下(了解,以后盡量避免

示范文件內容如下

#創建一個m1.py
print('正在導入m1')
from m2 import y
​
x='m1'#創建一個m2.py
print('正在導入m2')
from m1 import x
​
y='m2'#創建一個run.py
import m1
​
#測試一
執行run.py會拋出異常
正在導入m1
正在導入m2
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/aa.py", line 1, in <module>
    import m1
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/m2.py", line 2, in <module>
    from m1 import x
ImportError: cannot import name 'x'
​
​
#測試一結果分析
先執行run.py--->執行import m1,開始導入m1並運行其內部代碼--->打印內容"正在導入m1"
--->執行from m2 import y 開始導入m2並運行其內部代碼--->打印內容“正在導入m2”--->執行from m1 import x,由於m1已經被導入過了,所以不會重新導入,所以直接去m1中拿x,然而x此時並沒有存在於m1中,所以報錯
​
​
#測試二:執行文件不等於導入文件,比如執行m1.py不等於導入了m1
直接執行m1.py拋出異常
正在導入m1
正在導入m2
正在導入m1
Traceback (most recent call last):
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/m1.py", line 2, in <module>
    from m2 import y
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/m2.py", line 2, in <module>
    from m1 import x
  File "/Users/linhaifeng/PycharmProjects/pro01/1 aaaa練習目錄/m1.py", line 2, in <module>
    from m2 import y
ImportError: cannot import name 'y'
​
​
#測試二分析
執行m1.py,打印“正在導入m1”,執行from m2 import y ,導入m2進而執行m2.py內部代碼--->打印"正在導入m2",執行from m1 import x,此時m1是第一次被導入,執行m1.py並不等於導入了m1,於是開始導入m1並執行其內部代碼--->打印"正在導入m1",執行from m1 import y,由於m1已經被導入過了,所以無需繼續導入而直接問m2要y,然而y此時並沒有存在於m2中所以報錯
​
​
​
# 解決方法:
方法一:導入語句放到最后
#m1.py
print('正在導入m1')
​
x='m1'from m2 import y
​
#m2.py
print('正在導入m2')
y='m2'from m1 import x
​
方法二:導入語句放到函數中
#m1.py
print('正在導入m1')
​
def f1():
    from m2 import y
    print(x,y)
​
x = 'm1'# f1()
#m2.py
print('正在導入m2')
​
def f2():
    from m1 import x
    print(x,y)
​
y = 'm2'#run.py
import m1
​
m1.f1()
View Code

4. py文件的兩種功能

編寫好的一個python文件可以有兩種用途:
   一:腳本,一個文件就是整個程序,用來被執行(比如你之前寫的模擬博客園登錄那個作業等)
   二:模塊,文件中存放着一堆功能,用來被導入使用

python為我們內置了全局變量__name__,
   當文件被當做腳本執行時:__name__ 等於'__main__'
   當文件被當做模塊導入時:__name__等於模塊名

#作用:用來控制.py文件在不同的應用場景下執行不同的邏輯(或者是在模塊文件中測試代碼)

if __name__ == '__main__':
print('from the tbjx.py')
​
__all__ = ['name', 'read1',]
​
name = '太白金星'
​
​
def read1():
   print('tbjx模塊:',name)
​
​
def read2():
   print('tbjx模塊')
   read1()
​
​
def change():
   
   global name
   name = 'barry'
   
if __name__ == '__main__':  
   # 在模塊文件中測試read1()函數
   # 此模塊被導入時 __name__ == tbjx 所以不執行
   read1()
View Code

5. 模塊的搜索路徑

當你引用一個模塊時,不見得每次都可以import到:

上面的示例可以得知,引用模塊也是按照一定規則進行引用的。

    Python中引用模塊是按照一定的規則以及順序去尋找的,這個查詢順序為:先從內存中已經加載的模塊進行尋找找不到再從內置模塊中尋找,內置模塊如果也沒有,最后去sys.path中路徑包含的模塊中尋找。它只會按照這個順序從這些指定的地方去尋找,如果最終都沒有找到,那么就會報錯。

    內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊

模塊的查找順序

  1. 在第一次導入某個模塊時(比如tbjx),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),如果有則直接引用(ps:python解釋器在啟動時會自動加載一些模塊到內存中,可以使用sys.modules查看)

  2. 如果沒有,解釋器則會查找同名的內置模塊

  3. 如果還沒有找到就從sys.path給出的目錄列表中依次尋找tbjx.py文件。

需要特別注意的是:我們自定義的模塊名不應該與系統內置模塊重名。雖然每次都說,但是仍然會有人不停的犯錯

#在初始化后,python程序可以修改sys.path,路徑放到前面的優先於標准庫被加載。
> > > import sys
> > > sys.path.append('/a/b/c/d')
> > > sys.path.insert(0,'/x/y/z') #排在前的目錄,優先被搜索
> > > 注意:搜索時按照sys.path中從左到右的順序查找,位於前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理,
​
#首先制作歸檔文件: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')
​
#windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
​
#至於.egg文件是由setuptools創建的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。
#需要強調的一點是:只能從.zip文件中導入.py,.pyc等文件。使用C編寫的共享庫和擴展塊無法直接從.zip文件中加載(此時setuptools等打包系統有時能提供一種規避方法),且從.zip中加載文件不會創建.pyc或者.pyo文件,因此一定要事先創建他們,來避免加載模塊是性能下降。

接下來我們就開始講解python常用的內置模塊,由於Python常用的模塊非常多,我們不可能將所有的模塊都講完, 所以只針對於工作中經常用到模塊進行講解。剩下的模塊可以在課余時間自學。
View Code


免責聲明!

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



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