目錄
1 模塊
一個模塊是包含了Python定義和聲明的文件,文件名,就是模塊名字加上py 后綴
把定義的函數、變量保存到文件中,通過Python test.py的方式執行,test.py就是腳本文件。程序功能越來越多,這些監本文件還可以當做模塊導入其他的模塊中,實現了重利用。
import
使用import的時候,想要使用spam下面的方法,必須使用spam.的方式
import spam
print(spam.money)
spam.read1()
spam.read2()
spam.change()
from… import …
對比import spam,會將源文件的名稱空間’spam’帶到當前名稱空間中,使用時必須是spam.名字的方式
而from 語句相當於import,也會創建新的名稱空間,但是將spam中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了、
1.1 使用模塊
#spam.py
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 # 經過測試這個在執行這個腳本自身的時候,global是把money的值引過來了,然后再下面進行了修改,之后再打印就是0
money=0
print(money) # 這里是有打印了下 主要 是在模塊中的時候進行測試
if __name__ == '__main__':
# main()
print("測試") # 初始化執行的
print(money)
change()
print(money)
結果是:
1000
0
0
1.2 Python模塊的導入
python模塊在導入的時候,防止重復導入,在第一次導入后,會加載到內存,在之后的調用都是指向內存
#test.py
import spam #只在第一次導入時才執行spam.py內代碼,此處的顯式效果是只打印一次'from the spam.py',當然其他的頂級代碼也都被執行了,只不過沒有顯示效果.
import spam
import spam
import spam
''' 執行結果: from the spam.py '''
從sys.module中找到當前已經加載的模塊
1.3 模塊的名稱空間
每一個模塊都是一個獨立的名稱空間,定義在這個模塊總的函數會把模塊的名稱空間當做全局名稱空間,這樣我們在編寫自己的模塊時,就不用擔心我們定義在自己模塊中全局變量會在被導入時,與使用者的全局變量沖突
from spam import money, read1, read2,change # 可以導入多個
print(money) # 引用的是change 中的money
change() # 這個是修改了的 只是在
print(money)
''' 結果是: 1000 0 1000 '''
1.4 導入模塊的做的事情
- 為源文件(spam)創建新的名稱空間,在spam中定義的函數和方法使用了global時,訪問的就是這個名稱空間。
- 在新創建的名稱空間中執行模塊中包含的代碼
- 創建名字spam來引用該命名空間
尋找的優先級:
''' 先從內存中尋找,sys.modules 然后從內置的尋找(內建) 從自己的路徑中尋找 從sys.path中尋找 '''
2 from import
2.1
python中的變量賦值不是一種存儲操作,而只是一種綁定關系
from spam import money, read1, read2, change
money = 100000000 # money現在是綁定到新的上
print(money) # 現在就有沖突了
read1()
read1 = 1111111
read2() # 這個不影響,從哪里調用,用哪里的
2.2 from spam import *
from spam import 把spam中所有的不是以下划線(_)開頭的名字都導入到當前位置,大部分情況下我們的python程序不應該使用這種導入方式,因為你不知道你導入什么名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。
通常使用all=[‘money’,’read1’],這樣引用spam的就只能用money和read1兩種
3 把模塊當做腳本執行
文件有兩種應用場景,一種是當做腳本執行,一種是當做模塊
3.1 腳本執行
spam文件執行的的時候
print(__name__)
結果是:
''' __main__ '''
3.2 模塊執行
在test文件中導入import spam,打印的結果是spam
為了能夠控制在不同場景下面的轉換,使用了if name == ‘main‘:,當做腳本執行的時候,邏輯寫到if name == ‘main‘:下面。
當做模塊導入的時候,不會執行if name == ‘main‘:下面的內容。
4 模塊搜索路徑
總結模塊查找的順序:
內存—>內建—>sys.path
sys.path 的路徑是以執行文件為基准的
在不同的路徑中的調用,在dir1中調用dir2中的內容
import sys # 先導如sys模塊
sys.path.append(r"D:\Python_fullstack_s4\day35\模塊\dir1") # 在sys.path的路徑中添加dir1的路徑 r是在win平台的轉義
import spam # 添加路徑后再調用spam
""" 結果: from dir1 # 這是spam中打印的內容 """
5 編譯Python文件
pyc文件是在導入模塊的時候進行編譯的,提高模塊的導入速度,只有import/from import才能產生
提前編譯
python -m compileall /module_directory 遞歸着編譯
6 包
package是有init.py 文件的包
包的本質就是一個包含init.py文件的目錄。
6.1
創建一個包的目錄結構,可以把包想象成一個大的模塊
glance/ #Top-level package
├── __init__.py #Initialize the glance package
├── api #Subpackage for api
│ ├── __init__.py
│ ├── policy.py
│ └── versions.py
├── cmd #Subpackage for cmd
│ ├── __init__.py
│ └── manage.py
└── db #Subpackage for db
├── __init__.py
└── models.py
文件的內容:
#文件內容
#policy.py
def get():
print('from policy.py')
#versions.py
def create_resource(conf):
print('from version.py: ',conf)
#manage.py
def main():
print('from manage.py')
#models.py
def register_models(engine):
print('from models.py: ',engine)
想要在外部是用glance-api-policy中的文件
import方法
import glance.api.plicy # 用點的方式
glance.api.plicy.get() # 通過import導入的還是要用名字的方式進行使用
''' 結果: from policy.py # policy中的get的內容 '''
from import方法
from glance.api.plicy import get # 下面使用的時候就直接使用了
get()
6.2 小結
在導入時都必須遵循一個原則:凡是在導入時帶點的,點的左邊都必須是一個包,
對於導入后,在使用時就沒有這種限制了,點的左邊可以是包,模塊,函數,類(它們都可以用點的方式調用自己的屬性)。
需要注意的是from后import導入的模塊,必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法
6.3 init.py文件
包都有init.py文件,這個文件是包初始化就會執行的文件,可以為空,也可以是初始化的代碼
導入包的時候,僅僅做的就是執行init.py
from glance.api import plicy # 導入的是glance api
''' 結果: glance 的包 init 的包 '''
下面用import *來測試,也是一樣的,但是api下面的內容找不到
from glance.api import * # 這里則是導入包,執行inti,*對應的是__all__中的
''' 結果: glance 的包 init 的包 '''
from glance.api import *
print(x)
print(y)
''' 結果: glance 的包 init 的包 1 2 '''
''' 這是api中__init__,所以*是對應的__all__中的內容 __all__ = ["x", "y"] x = 1 y = 2 '''
7 絕對導入和相對導入
7.1 絕對導入是從包的最開始的位置開始
從test的sys.path的列表中尋找
絕對導入的缺點是包的名字改變的話有問題
在api的init.py下面寫
from glance.api import plicy
from glance.api import versions
test的使用
import glance.api # 僅僅是導入了glance.api 實際是找不到plicy的,因為導入模塊僅僅是執行了__init__
print(glance.api.plicy)
''' 結果: glance 的包 init 的包 <module 'glance.api.plicy' from 'D:\\Python_fullstack_s4\\day35\\包\\glance\\api\\plicy.py'> '''
7.2 相對導入
.—當前目錄
..—上一級的目錄
from . import plicy,versions # 通過當前目錄的方式導入,當前的目錄是api
注意這種方式的init自己執行的時候會報錯
導入包的執行效果
import glance.api
print(glance.api.plicy.get())
print(glance.api.versions.create_resource('aaa'))
''' 結果: from . import plicy,versionsglance 的包 init 的包 from policy.py None from version.py: aaa None '''
8 通過包調用內部的所有的
這是glance的包中的init
from .api.plicy import get # 都是用的相對導入
from .api.versions import create_resource
from .cmd.manage import main
from .db.modules import register_models
包的定義者容易管理,對於使用者來說,使用的不知道是包還是模塊
test文件調用的方式
import glance
glance.get() # 直接就能夠使用
''' glance 的包 init 的包 cmd 的包 from policy.py '''
8 包遵循的原則
特別需要注意的是:可以用import導入內置或者第三方模塊,但是要絕對避免使用import來導入自定義包的子模塊,應該使用from… import …的絕對或者相對導入,且包的相對導入只能用from的形式。
包是給別人用的,是不能自己運行的。自己運行的時候會出錯
9 在任意位置都能調用包
關鍵是在運行的的文件查找到的是當前目錄的sys.path,如果在別的目錄中使用的話就需要找到包的父目錄
首先找到文件的絕對路徑
import sys
import os
p=os.path.abspath(__file__) # 打印的是文件的絕對路徑
print(p)
''' 結果: D:\Python_fullstack_s4\day35\包\test.py '''
p = os.path.dirname(os.path.abspath(__file__)) # 返回的是文件的上一級目錄
print(p)
''' 結果: D:\Python_fullstack_s4\day35\包 '''
下面的目錄結構是
D:.day35
├─包
│ └─glance
│ ├─api
│ │ └─__pycache__
│ ├─cmd
│ │ └─__pycache__
│ ├─db
│ │ └─__pycache__
│ └─__pycache__
├─模塊
│ ├─dir1
│ │ └─__pycache__
│ ├─dir2
│ └─__pycache__
└─練習
import sys
import os
base_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# sys.path.append(r"%s"%(base_path)) # 把父目錄添加到sys.path中
sys.path.append(r'%s\包' %(base_path)) # 拼接路徑,這里拼接的是有glance的包
print(base_path)
import glance
glance.get()
''' 結果: D:\Python_fullstack_s4\day35 glance 的包 init 的包 cmd 的包 '''