日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

python之路——模块和包

發(fā)布時間:2023/11/27 生活经验 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python之路——模块和包 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、模塊

1、什么是模塊?

常見的場景:一個模塊就是一個包含了Python定義和聲明的文件,文件名就是模塊名字加上.py的后綴。

但其實import加載的模塊分為四個通用類別:

  1、使用Python編寫的代碼(.py文件)

  2、已被編譯為共享庫或DLL的C++擴展

  3、包好一組模塊的包

  4、使用C編寫并鏈接到Python解釋器的內(nèi)置模塊

2、為何要使用模塊?

? ? 如果你退出python解釋器然后重新進入,那么你之前定義的函數(shù)或者變量都將丟失,因此我們通常將程序?qū)懙轿募幸员阌谰帽4嫦聛?#xff0c;需要時就通過python test.py方式去執(zhí)行,此時test.py被稱為腳本script。

? ? 隨著程序的發(fā)展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結(jié)構(gòu)更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執(zhí)行,還可以把他們當做模塊來導入到其他的模塊中,實現(xiàn)了功能的重復利用。

3、如何使用模塊?

3.1、import:

示例文件:自定義模塊my_module.py,文件名my_module.py,模塊名my_module

#my_module.py
print('from the my_module.py')money=1000def read1():print('my_module->read1->money',money)def read2():print('my_module->read2 calling read1')read1()def change():global moneymoney=0
My_module模塊

3.1.1

模塊可以包含可執(zhí)行的語句和函數(shù)的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執(zhí)行(import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優(yōu)化手段是:第一次導入后就將模塊名加載到內(nèi)存了,后續(xù)的import語句僅是對已經(jīng)加載大內(nèi)存中的模塊對象增加了一次引用,不會重新執(zhí)行模塊內(nèi)的語句),如下?

#demo.py
import my_module #只在第一次導入時才執(zhí)行my_module.py內(nèi)代碼,此處的顯式效果是只打印一次'from the my_module.py',當然其他的頂級代碼也都被執(zhí)行了,只不過沒有顯示效果.
import my_module
import my_module
import my_module'''
執(zhí)行結(jié)果:
from the my_module.py
'''
demo.py

我們可以從sys.modules中找到當前已經(jīng)加載的模塊,sys.modules是一個字典,內(nèi)部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否需要重新導入。

3.1.2

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

#測試一:money與my_module.money不沖突
#demo.py
import my_module
money=10
print(my_module.money)'''
執(zhí)行結(jié)果:
from the my_module.py
1000
'''
測試一:money與my_module.money不沖突
#測試二:read1與my_module.read1不沖突
#demo.py
import my_module
def read1():print('========')
my_module.read1()'''
執(zhí)行結(jié)果:
from the my_module.py
my_module->read1->money 1000
'''
測試二:read1與my_module.read1不沖突
#測試三:執(zhí)行my_module.change()操作的全局變量money仍然是my_module中的
#demo.py
import my_module
money=1
my_module.change()
print(money)'''
執(zhí)行結(jié)果:
from the my_module.py
1
'''
測試三:執(zhí)行my_module.change()操作的全局變量money仍然是my_module中的

3.1.3

總結(jié):首次導入模塊my_module時會做三件事:

1.為源文件(my_module模塊)創(chuàng)建新的名稱空間,在my_module中定義的函數(shù)和方法若是使用到了global時訪問的就是這個名稱空間。

2.在新創(chuàng)建的命名空間中執(zhí)行模塊中包含的代碼,見初始導入import?my_module

1 提示:導入模塊時到底執(zhí)行了什么?
2 
3 In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table.
4 事實上函數(shù)定義也是“被執(zhí)行”的語句,模塊級別函數(shù)定義的執(zhí)行將函數(shù)名放入模塊全局名稱空間表,用globals()可以查看

3.創(chuàng)建名字my_module來引用該命名空間

1 這個名字和變量名沒什么區(qū)別,都是‘第一類的’,且使用my_module.名字的方式可以訪問my_module.py文件中定義的名字,my_module.名字與test.py中的名字來自兩個完全不同的地方。

3.1.4

為模塊名起別名,相當于m1=1;m2=m1?

1 import my_module as sm
2 print(sm.money)

示范用法一:

有兩中sql模塊mysql和oracle,根據(jù)用戶的輸入,選擇不同的sql功能

#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 dbdb.sqlparse() 
示例用法1

示范用法二:?

為已經(jīng)導入的模塊起別名的方式對編寫可擴展的代碼很有用,假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數(shù)read_data(filename):用來從文件中讀取一些數(shù)據(jù),但采用不同的輸入格式。可以編寫代碼來選擇性地挑選讀取模塊,例如

if file_format == 'xml':import xmlreader as reader
elif file_format == 'csv':import csvreader as reader
data=reader.read_date(filename)
示例用法2

3.1.5

在一行導入多個模塊

1 import sys,os,re

3.2 ?from...import...:

3.2.1

對比import my_module,會將源文件的名稱空間'my_module'帶到當前名稱空間中,使用時必須是my_module.名字的方式

而from 語句相當于import,也會創(chuàng)建新的名稱空間,但是將my_module中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了、

 1 from my_module import read1,read2

這樣在當前位置直接使用read1和read2就好了,執(zhí)行時,仍然以my_module.py文件全局名稱空間

#測試一:導入的函數(shù)read1,執(zhí)行時仍然回到my_module.py中尋找全局變量money
#demo.py
from my_module import read1
money=1000
read1()
'''
執(zhí)行結(jié)果:
from the my_module.py
spam->read1->money 1000
'''#測試二:導入的函數(shù)read2,執(zhí)行時需要調(diào)用read1(),仍然回到my_module.py中找read1()
#demo.py
from my_module import read2
def read1():print('==========')
read2()'''
執(zhí)行結(jié)果:
from the my_module.py
my_module->read2 calling read1
my_module->read1->money 1000
'''
View Code

需要特別強調(diào)的一點是:python中的變量賦值不是一種存儲操作,而只是一種綁定關系,如下:

from my_module import money,read1
money=100 #將當前位置的名字money綁定到了100
print(money) #打印當前的名字
read1() #讀取my_module.py中的名字money,仍然為1000'''
from the my_module.py
100
my_module->read1->money 1000
'''
View Code

3.2.2

也支持as

1 from my_module import read1 as read

3.2.3

也支持導入多行

1 from my_module import (read1,
2                   read2,
3                   money)

3.2.4

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

from my_module import * #將模塊my_module中所有的名字都導入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)'''
執(zhí)行結(jié)果:
from the my_module.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''
View Code

在my_module.py中新增一行

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

*如果my_module.py中的名字前加_,即_money,則from my_module?import *,則_money不能被導入

3.2.5?

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

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

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

如果只是你想交互測試的一個模塊,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這只能用于測試環(huán)境。

def func1():print('func1')
aa.py
import time,importlib
import aatime.sleep(20)
# importlib.reload(aa)
aa.func1()
測試代碼

在20秒的等待時間里,修改aa.py中func1的內(nèi)容,等待test.py的結(jié)果。

打開importlib注釋,重新測試

?

3.3把模塊當做腳本執(zhí)行:

我們可以通過模塊的全局變量__name__來查看模塊名:
當做腳本運行:
__name__ 等于'__main__'

當做模塊導入:
__name__=?模塊名

作用:用來控制.py文件在不同的應用場景下執(zhí)行不同的邏輯
if __name__ == '__main__':

def fib(n):   a, b = 0, 1while b < n:print(b, end=' ')a, b = b, a+bprint()if __name__ == "__main__":print(__name__)num = input('num :')fib(int(num))
View Code

?

3.4 模塊搜索路徑

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

在第一次導入某個模塊時(比如my_module),會先檢查該模塊是否已經(jīng)被加載到內(nèi)存中(當前執(zhí)行文件的名稱空間對應的內(nèi)存),如果有則直接引用

如果沒有,解釋器則會查找同名的內(nèi)建模塊,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。

所以總結(jié)模塊的查找順序是:內(nèi)存中已經(jīng)加載的模塊->內(nèi)置模塊->sys.path路徑中包含的模塊

sys.path的初始化的值來自于:

The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.

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

在初始化后,python程序可以修改sys.path,路徑放到前面的優(yōu)先于標準庫被加載。

1 >>> import sys
2 >>> sys.path.append('/a/b/c/d')
3 >>> sys.path.insert(0,'/x/y/z') #排在前的目錄,優(yōu)先被搜索

注意:搜索時按照sys.path中從左到右的順序查找,位于前的優(yōu)先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。

#首先制作歸檔文件:zip module.zip foo.py bar.pyimport sys
sys.path.append('module.zip')
import foo,bar#也可以使用zip中目錄結(jié)構(gòu)的具體位置
sys.path.append('module.zip/lib/python')#windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
View Code

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

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

官網(wǎng)解釋

#官網(wǎng)鏈接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜索路徑:
當一個命名為my_module的模塊被導入時解釋器首先會從內(nèi)建模塊中尋找該名字找不到,則去sys.path中找該名字sys.path從以下位置初始化
執(zhí)行文件所在的當前目錄
PTYHONPATH(包含一系列目錄名,與shell變量PATH語法一樣)
依賴安裝時默認指定的注意:在支持軟連接的文件系統(tǒng)中,執(zhí)行腳本所在的目錄是在軟連接之后被計算的,換句話說,包含軟連接的目錄不會被添加到模塊的搜索路徑中在初始化后,我們也可以在python程序中修改sys.path,執(zhí)行文件所在的路徑默認是sys.path的第一個目錄,在所有標準庫路徑的前面。這意味著,當前目錄是優(yōu)先于標準庫目錄的,需要強調(diào)的是:我們自定義的模塊名不要跟python標準庫的模塊名重復,除非你是故意的,傻叉。
View Code

?

3.5?編譯python文件:

為了提高加載模塊的速度,強調(diào)強調(diào)強調(diào):提高的是加載速度而絕非運行速度。python解釋器會在__pycache__目錄中下緩存每個模塊編譯后的版本,格式為:module.version.pyc。通常會包含python的版本號。例如,在CPython3.3版本下,my_module.py模塊會被緩存成__pycache__/my_module.cpython-33.pyc。這種命名規(guī)范保證了編譯后的結(jié)果多版本共存。

?

Python檢查源文件的修改時間與編譯的版本進行對比,如果過期就需要重新編譯。這是完全自動的過程。并且編譯的模塊是平臺獨立的,所以相同的庫可以在不同的架構(gòu)的系統(tǒng)之間共享,即pyc使一種跨平臺的字節(jié)碼,類似于JAVA火.NET,是由python虛擬機來執(zhí)行的,但是pyc的內(nèi)容跟python的版本相關,不同的版本編譯后的pyc文件不同,2.5編譯的pyc文件不能到3.5上執(zhí)行,并且pyc文件是可以反編譯的,因而它的出現(xiàn)僅僅是用來提升模塊的加載速度的。

?

python解釋器在以下兩種情況下不檢測緩存
  1 如果是在命令行中被直接導入模塊,則按照這種方式,每次導入都會重新編譯,并且不會存儲編譯后的結(jié)果(python3.3以前的版本應該是這樣)

python -m my_module.py

  2 如果源文件不存在,那么緩存的結(jié)果也不會被使用,如果想在沒有源文件的情況下來使用編譯后的結(jié)果,則編譯后的結(jié)果必須在源目錄下?

提示:

1.模塊名區(qū)分大小寫,foo.py與FOO.py代表的是兩個模塊

2.你可以使用-O或者-OO轉(zhuǎn)換python命令來減少編譯模塊的大小

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

?

補充:dir()函數(shù)

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

import my_module
dir(my_module)

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

dir()不會列舉出內(nèi)建函數(shù)或者變量的名字,它們都被定義到了標準模塊builtin中,可以列舉出它們,

import builtins
dir(builtins)

?

二、包

包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。

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

2. 包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質(zhì)就是一個包含__init__.py文件的目錄)

3. import導入文件時,產(chǎn)生名稱空間中的名字來源于文件,import 包,產(chǎn)生的名稱空間的名字同樣來源于文件,即包下的__init__.py,導入包本質(zhì)就是在導入該文件

強調(diào):

  1. 在python3中,即使包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下一定要有該文件,否則import 包報錯

  2. 創(chuàng)建包的目的不是為了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包即模塊


包A和包B下有同名模塊也不會沖突,如A.a與B.a來自倆個命名空間

import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open('glance/api/policy.py','w'))
l.append(open('glance/api/versions.py','w'))
l.append(open('glance/cmd/__init__.py','w'))
l.append(open('glance/cmd/manage.py','w'))
l.append(open('glance/db/models.py','w'))
map(lambda f:f.close() ,l)
創(chuàng)建目錄代碼
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
目錄結(jié)構(gòu)
#文件內(nèi)容#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)
文件內(nèi)容

2.1注意事項

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

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

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

2.2 import?

我們在與包glance同級別的文件中測試

1 import glance.db.models
2 glance.db.models.register_models('mysql') 

2.3 from ... import ...

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

我們在與包glance同級別的文件中測試?

1 from glance.db import models
2 models.register_models('mysql')
3 
4 from glance.db.models import register_models
5 register_models('mysql')

2.4 __init__.py文件

隨筆- 29? 文章- 57? 評論- 61?

python之路——模塊和包

閱讀目錄

  • 一 模塊
  • 3.1 import
  • 3.2?from ... import...
  • 3.3?把模塊當做腳本執(zhí)行?
  • 3.4 模塊搜索路徑
  • 3.5?編譯python文件
  • 二 包
  • 2.2 import?
  • 2.3 from ... import ...
  • 2.4 __init__.py文件
  • 2.5 ?from glance.api import *
  • 2.6 絕對導入和相對導入
  • 2.7 單獨導入包
回到頂部

一 模塊

1 什么是模塊?

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

? ?但其實import加載的模塊分為四個通用類別: 

  1 使用python編寫的代碼(.py文件)

  2 已被編譯為共享庫或DLL的C或C++擴展

  3 包好一組模塊的包

  4 使用C編寫并鏈接到python解釋器的內(nèi)置模塊

2 為何要使用模塊?

? ? 如果你退出python解釋器然后重新進入,那么你之前定義的函數(shù)或者變量都將丟失,因此我們通常將程序?qū)懙轿募幸员阌谰帽4嫦聛?#xff0c;需要時就通過python test.py方式去執(zhí)行,此時test.py被稱為腳本script。

? ? 隨著程序的發(fā)展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結(jié)構(gòu)更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執(zhí)行,還可以把他們當做模塊來導入到其他的模塊中,實現(xiàn)了功能的重復利用,

3.如何使用模塊?

回到頂部

3.1 import

示例文件:自定義模塊my_module.py,文件名my_module.py,模塊名my_module

?my_module模塊

?

3.1.1

模塊可以包含可執(zhí)行的語句和函數(shù)的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執(zhí)行(import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優(yōu)化手段是:第一次導入后就將模塊名加載到內(nèi)存了,后續(xù)的import語句僅是對已經(jīng)加載大內(nèi)存中的模塊對象增加了一次引用,不會重新執(zhí)行模塊內(nèi)的語句),如下?

?demo.py

?

我們可以從sys.modules中找到當前已經(jīng)加載的模塊,sys.modules是一個字典,內(nèi)部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否需要重新導入。

?

3.1.2?

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

?測試一:money與my_module.money不沖突 ?測試二:read1與my_module.read1不沖突 ?測試三:執(zhí)行my_module.change()操作的全局變量money仍然是my_module中的

?

3.1.3

總結(jié):首次導入模塊my_module時會做三件事:

1.為源文件(my_module模塊)創(chuàng)建新的名稱空間,在my_module中定義的函數(shù)和方法若是使用到了global時訪問的就是這個名稱空間。

2.在新創(chuàng)建的命名空間中執(zhí)行模塊中包含的代碼,見初始導入import?my_module

1 提示:導入模塊時到底執(zhí)行了什么?
2 
3 In fact function definitions are also ‘statements’ that are ‘executed’; the execution of a module-level function definition enters the function name in the module’s global symbol table.
4 事實上函數(shù)定義也是“被執(zhí)行”的語句,模塊級別函數(shù)定義的執(zhí)行將函數(shù)名放入模塊全局名稱空間表,用globals()可以查看

3.創(chuàng)建名字my_module來引用該命名空間

1 這個名字和變量名沒什么區(qū)別,都是‘第一類的’,且使用my_module.名字的方式可以訪問my_module.py文件中定義的名字,my_module.名字與test.py中的名字來自兩個完全不同的地方。

?

?3.1.4

為模塊名起別名,相當于m1=1;m2=m1?

1 import my_module as sm
2 print(sm.money)

?

示范用法一:

有兩中sql模塊mysql和oracle,根據(jù)用戶的輸入,選擇不同的sql功能

?示例用法1

?

示范用法二:?

為已經(jīng)導入的模塊起別名的方式對編寫可擴展的代碼很有用,假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數(shù)read_data(filename):用來從文件中讀取一些數(shù)據(jù),但采用不同的輸入格式。可以編寫代碼來選擇性地挑選讀取模塊,例如

?示例用法2

?

?

3.1.5

在一行導入多個模塊

1 import sys,os,re

?

回到頂部

3.2?from ... import...

3.2.1

對比import my_module,會將源文件的名稱空間'my_module'帶到當前名稱空間中,使用時必須是my_module.名字的方式

而from 語句相當于import,也會創(chuàng)建新的名稱空間,但是將my_module中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了、

 1 from my_module import read1,read2

這樣在當前位置直接使用read1和read2就好了,執(zhí)行時,仍然以my_module.py文件全局名稱空間

?View Code

?

如果當前有重名read1或者read2,那么會有覆蓋效果。

?View Code

?

需要特別強調(diào)的一點是:python中的變量賦值不是一種存儲操作,而只是一種綁定關系,如下:

?View Code

?

3.2.2

也支持as

1 from my_module import read1 as read

?

3.2.3

也支持導入多行

1 from my_module import (read1,
2                   read2,
3                   money)

?

3.2.4

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

?View Code

?

在my_module.py中新增一行

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

?

*如果my_module.py中的名字前加_,即_money,則from my_module?import *,則_money不能被導入

?

3.2.5?

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

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

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

如果只是你想交互測試的一個模塊,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這只能用于測試環(huán)境。

?aa.py ?測試代碼

在20秒的等待時間里,修改aa.py中func1的內(nèi)容,等待test.py的結(jié)果。

打開importlib注釋,重新測試

?

回到頂部

3.3?把模塊當做腳本執(zhí)行?

我們可以通過模塊的全局變量__name__來查看模塊名:
當做腳本運行:
__name__ 等于'__main__'

當做模塊導入:
__name__=?模塊名

作用:用來控制.py文件在不同的應用場景下執(zhí)行不同的邏輯
if __name__ == '__main__':

?View Code

?

回到頂部

3.4 模塊搜索路徑

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

在第一次導入某個模塊時(比如my_module),會先檢查該模塊是否已經(jīng)被加載到內(nèi)存中(當前執(zhí)行文件的名稱空間對應的內(nèi)存),如果有則直接引用

如果沒有,解釋器則會查找同名的內(nèi)建模塊,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。

所以總結(jié)模塊的查找順序是:內(nèi)存中已經(jīng)加載的模塊->內(nèi)置模塊->sys.path路徑中包含的模塊

sys.path的初始化的值來自于:

The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
The installation-dependent default.

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

在初始化后,python程序可以修改sys.path,路徑放到前面的優(yōu)先于標準庫被加載。

1 >>> import sys
2 >>> sys.path.append('/a/b/c/d')
3 >>> sys.path.insert(0,'/x/y/z') #排在前的目錄,優(yōu)先被搜索

注意:搜索時按照sys.path中從左到右的順序查找,位于前的優(yōu)先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。

#首先制作歸檔文件:zip module.zip foo.py bar.pyimport sys
sys.path.append('module.zip')
import foo,bar#也可以使用zip中目錄結(jié)構(gòu)的具體位置
sys.path.append('module.zip/lib/python')#windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')

?

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

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

?

官網(wǎng)解釋

?View Code

?

回到頂部

3.5?編譯python文件

為了提高加載模塊的速度,強調(diào)強調(diào)強調(diào):提高的是加載速度而絕非運行速度。python解釋器會在__pycache__目錄中下緩存每個模塊編譯后的版本,格式為:module.version.pyc。通常會包含python的版本號。例如,在CPython3.3版本下,my_module.py模塊會被緩存成__pycache__/my_module.cpython-33.pyc。這種命名規(guī)范保證了編譯后的結(jié)果多版本共存。

?

Python檢查源文件的修改時間與編譯的版本進行對比,如果過期就需要重新編譯。這是完全自動的過程。并且編譯的模塊是平臺獨立的,所以相同的庫可以在不同的架構(gòu)的系統(tǒng)之間共享,即pyc使一種跨平臺的字節(jié)碼,類似于JAVA火.NET,是由python虛擬機來執(zhí)行的,但是pyc的內(nèi)容跟python的版本相關,不同的版本編譯后的pyc文件不同,2.5編譯的pyc文件不能到3.5上執(zhí)行,并且pyc文件是可以反編譯的,因而它的出現(xiàn)僅僅是用來提升模塊的加載速度的。

?

python解釋器在以下兩種情況下不檢測緩存
  1 如果是在命令行中被直接導入模塊,則按照這種方式,每次導入都會重新編譯,并且不會存儲編譯后的結(jié)果(python3.3以前的版本應該是這樣)

python -m my_module.py

  2 如果源文件不存在,那么緩存的結(jié)果也不會被使用,如果想在沒有源文件的情況下來使用編譯后的結(jié)果,則編譯后的結(jié)果必須在源目錄下?

提示:

1.模塊名區(qū)分大小寫,foo.py與FOO.py代表的是兩個模塊

2.你可以使用-O或者-OO轉(zhuǎn)換python命令來減少編譯模塊的大小

?了解

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

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

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

?

補充:dir()函數(shù)

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

import my_module
dir(my_module)

?

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


dir()不會列舉出內(nèi)建函數(shù)或者變量的名字,它們都被定義到了標準模塊builtin中,可以列舉出它們,

import builtins
dir(builtins)

?

回到頂部

二 包

包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。

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

2. 包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質(zhì)就是一個包含__init__.py文件的目錄)

3. import導入文件時,產(chǎn)生名稱空間中的名字來源于文件,import 包,產(chǎn)生的名稱空間的名字同樣來源于文件,即包下的__init__.py,導入包本質(zhì)就是在導入該文件

強調(diào):

  1. 在python3中,即使包下沒有__init__.py文件,import 包仍然不會報錯,而在python2中,包下一定要有該文件,否則import 包報錯

  2. 創(chuàng)建包的目的不是為了運行,而是被導入使用,記住,包只是模塊的一種形式而已,包即模塊


包A和包B下有同名模塊也不會沖突,如A.a與B.a來自倆個命名空間

?創(chuàng)建目錄代碼

?

?目錄結(jié)構(gòu) ?文件內(nèi)容

?

2.1 注意事項


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

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

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

2.2 import?

我們在與包glance同級別的文件中測試

1 import glance.db.models
2 glance.db.models.register_models('mysql')?

2.3 from ... import ...

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

我們在與包glance同級別的文件中測試?

1 from glance.db import models
2 models.register_models('mysql')
3 
4 from glance.db.models import register_models
5 register_models('mysql')

2.4 __init__.py文件

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

2.5 ?from glance.api import *

在講模塊時,我們已經(jīng)討論過了從一個模塊內(nèi)導入所有*,此處我們研究從一個包導入所有*。

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

#在__init__.py中定義
x=10def func():print('from api.__init.py')__all__=['x','func','policy']

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

glance/                   ├── __init__.py      ├── api                  │   ├── __init__.py   __all__ = ['policy','versions'] │   ├── policy.py│   └── versions.py├── cmd               __all__ = ['manage']    │   ├── __init__.py│   └── manage.py    └── db                __all__ = ['models']              ├── __init__.py└── models.pyfrom glance.api import *
policy.get()
from glance.api import *

2.6 絕對導入和相對導入

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

絕對導入:以glance作為起始

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

例如:我們在glance/api/version.py中想要導入glance/cmd/manage.py

在glance/api/version.py#絕對導入
from glance.cmd import manage
manage.main()#相對導入
from ..cmd import manage
manage.main()

測試結(jié)果:注意一定要在于glance同級的文件中測試

1 from glance.api import versions 

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

?

特別需要注意的是:可以用import導入內(nèi)置或者第三方模塊(已經(jīng)在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。

比如我們想在glance/api/versions.py中導入glance/api/policy.py,有的同學一抽這倆模塊是在同一個目錄下,十分開心的就去做了,它直接這么做

1 #在version.py中
2 
3 import policy
4 policy.get()

沒錯,我們單獨運行version.py是一點問題沒有的,運行version.py的路徑搜索就是從當前路徑開始的,于是在導入policy時能在當前目錄下找到

但是你想啊,你子包中的模塊version.py極有可能是被一個glance包同一級別的其他文件導入,比如我們在于glance同級下的一個test.py文件中導入version.py,如下

from glance.api import versions'''
執(zhí)行結(jié)果:
ImportError: No module named 'policy'
''''''
分析:
此時我們導入versions在versions.py中執(zhí)行
import policy需要找從sys.path也就是從當前目錄找policy.py,
這必然是找不到的
'''
glance/                   ├── __init__.py      from glance import apifrom glance import cmdfrom glance import db├── api                  │   ├── __init__.py  from glance.api import policyfrom glance.api import versions│   ├── policy.py│   └── versions.py├── cmd                 from glance.cmd import manage│   ├── __init__.py│   └── manage.py└── db                   from glance.db import models├── __init__.py└── models.py
絕對導入
glance/                   ├── __init__.py      from . import api  #.表示當前目錄from . import cmdfrom . import db├── api                  │   ├── __init__.py  from . import policyfrom . import versions│   ├── policy.py│   └── versions.py├── cmd              from . import manage│   ├── __init__.py│   └── manage.py    from ..api import policy   #..表示上一級目錄,想再manage中使用policy中的方法就需要回到上一級glance目錄往下找api包,從api導入policy

└── db               from . import models├── __init__.py└── models.py
相對導入

2.7 單獨導入包

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

#在與glance同級的test.py中
import glance
glance.cmd.manage.main()'''
執(zhí)行結(jié)果:
AttributeError: module 'glance' has no attribute 'cmd''''

解決方法:

1 #glance/__init__.py
2 from . import cmd
3 
4 #glance/cmd/__init__.py
5 from . import manage

執(zhí)行:

1 #在于glance同級的test.py中
2 import glance
3 glance.cmd.manage.main()

千萬別問:__all__不能解決嗎,__all__是用于控制from...import *?

glance/                   ├── __init__.py     from .api import *from .cmd import *from .db import *    
├── api                  │   ├── __init__.py   __all__ = ['policy','versions'] │   ├── policy.py│   └── versions.py├── cmd               __all__ = ['manage']    │   ├── __init__.py│   └── manage.py    └── db                __all__ = ['models']              ├── __init__.py└── models.pyimport glance
policy.get()
View Code

#=============>bin目錄:存放執(zhí)行腳本
#start.py
import sys,osBASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)from core import core
from conf import my_log_settingsif __name__ == '__main__':my_log_settings.load_my_logging_cfg()core.run()#=============>conf目錄:存放配置文件
#config.ini
[DEFAULT]
user_timeout = 1000[egon]
password = 123
money = 10000000[alex]
password = alex3714
money=10000000000[yuanhao]
password = ysb123
money=10#settings.py
import os
config_path=r'%s\%s' %(os.path.dirname(os.path.abspath(__file__)),'config.ini')
user_timeout=10
user_db_path=r'%s\%s' %(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),\'db')#my_log_settings.py
"""
logging配置
"""import os
import logging.config# 定義三種日志輸出格式 開始

standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' \'[%(levelname)s][%(message)s]' #其中name為getlogger指定的名字

simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'# 定義日志輸出格式 結(jié)束

logfile_dir = r'%s\log' %os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # log文件的目錄

logfile_name = 'all2.log'  # log文件名# 如果不存在定義的日志目錄就創(chuàng)建一個
if not os.path.isdir(logfile_dir):os.mkdir(logfile_dir)# log文件的全路徑
logfile_path = os.path.join(logfile_dir, logfile_name)# log配置字典
LOGGING_DIC = {'version': 1,'disable_existing_loggers': False,'formatters': {'standard': {'format': standard_format},'simple': {'format': simple_format},},'filters': {},'handlers': {#打印到終端的日志'console': {'level': 'DEBUG','class': 'logging.StreamHandler',  # 打印到屏幕'formatter': 'simple'},#打印到文件的日志,收集info及以上的日志'default': {'level': 'DEBUG','class': 'logging.handlers.RotatingFileHandler',  # 保存到文件'formatter': 'standard','filename': logfile_path,  # 日志文件'maxBytes': 1024*1024*5,  # 日志大小 5M'backupCount': 5,'encoding': 'utf-8',  # 日志文件的編碼,再也不用擔心中文log亂碼了
        },},'loggers': {#logging.getLogger(__name__)拿到的logger配置'': {'handlers': ['default', 'console'],  # 這里把上面定義的兩個handler都加上,即log數(shù)據(jù)既寫入文件又打印到屏幕'level': 'DEBUG','propagate': True,  # 向上(更高level的logger)傳遞
        },},
}def load_my_logging_cfg():logging.config.dictConfig(LOGGING_DIC)  # 導入上面定義的logging配置logger = logging.getLogger(__name__)  # 生成一個log實例logger.info('It works!')  # 記錄該文件的運行狀態(tài)if __name__ == '__main__':load_my_logging_cfg()#=============>core目錄:存放核心邏輯
#core.py
import logging
import time
from conf import settings
from lib import read_iniconfig=read_ini.read(settings.config_path)
logger=logging.getLogger(__name__)current_user={'user':None,'login_time':None,'timeout':int(settings.user_timeout)}
def auth(func):def wrapper(*args,**kwargs):if current_user['user']:interval=time.time()-current_user['login_time']if interval < current_user['timeout']:return func(*args,**kwargs)name = input('name>>: ')password = input('password>>: ')if config.has_section(name):if password == config.get(name,'password'):logger.info('登錄成功')current_user['user']=namecurrent_user['login_time']=time.time()return func(*args,**kwargs)else:logger.error('用戶名不存在')return wrapper@auth
def buy():print('buy...')@auth
def run():print('''
購物
查看余額
轉(zhuǎn)賬''')while True:choice = input('>>: ').strip()if not choice:continueif choice == '1':buy()if __name__ == '__main__':run()#=============>db目錄:存放數(shù)據(jù)庫文件
#alex_json
#egon_json#=============>lib目錄:存放自定義的模塊與包
#read_ini.py
import configparser
def read(config_file):config=configparser.ConfigParser()config.read(config_file)return config#=============>log目錄:存放日志
#all2.log
[2017-07-29 00:31:40,272][MainThread:11692][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:31:41,789][MainThread:11692][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:31:46,394][MainThread:12348][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:31:47,629][MainThread:12348][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:31:57,912][MainThread:10528][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:03,340][MainThread:12744][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:05,065][MainThread:12916][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:08,181][MainThread:12916][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:32:13,638][MainThread:7220][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:32:23,005][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功]
[2017-07-29 00:32:40,941][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功]
[2017-07-29 00:32:47,222][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功]
[2017-07-29 00:32:51,949][MainThread:7220][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:33:00,213][MainThread:7220][task_id:core.core][core.py:20][INFO][登錄成功]
[2017-07-29 00:33:50,118][MainThread:8500][task_id:conf.my_log_settings][my_log_settings.py:75][INFO][It works!]
[2017-07-29 00:33:55,845][MainThread:8500][task_id:core.core][core.py:20][INFO][登錄成功]
[2017-07-29 00:34:06,837][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:34:09,405][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在]
[2017-07-29 00:34:10,645][MainThread:8500][task_id:core.core][core.py:25][ERROR][用戶名不存在]
View Code

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

?

轉(zhuǎn)載于:https://www.cnblogs.com/bsxq/p/7592484.html

總結(jié)

以上是生活随笔為你收集整理的python之路——模块和包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。