Python自定義模塊
自定義模塊
我們今天來學習一下自定義模塊(也就是私人訂制),我們要自定義模塊,首先就要知道什麽是模塊啊
一個函數封裝一個功能,比如現在有一個軟件,不可能將所有程序都寫入一個文件,所以咱們應該分文件,組織結構要好,代碼不冗余,所以要分文件,但是分文件,分了5個文件,每個文件裏面可能都有相同的功能(函數),怎麽辦?所以將這些相同的功能封裝到一個文件中.
模塊就是文件,存放一堆函數,誰用誰拿。怎麽拿?
比如:我要策馬奔騰共享人世繁華,應該怎麽樣?我應該騎馬,你也要去浪,你是不是也要騎馬。
模塊是一系列常用功能的集合體,一個py文件就是一個模塊
為什麽要使用模塊?
1、從文件級別組織程序,更方便管理
隨著程序的發展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結構更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執行,還可以把他們當做模塊來導入到其他的模塊中,實現了功能的重復利用
2、拿來主義,提升開發效率
同樣的原理,我們也可以下載別人寫好的模塊然後導入到自己的項目中使用,這種拿來主義,可以極大地提升我們的開發效率,避免重復造輪子。
ps:
如果你退出python解釋器然後重新進入,那麽你之前定義的函數或者變量都將丟失,因此我們通常將程序寫到文件中以便永久保存下來,需要時就通過python meet.py方式去執行,此時meet.py被稱為腳本script。
‘‘‘ -*- coding: utf-8 -*- @Author : Meet @Software: PyCharm @File : meet.py ‘‘‘ print(‘from the meet.py‘) name = ‘guoboayuan‘ def read1(): print(‘meet模塊:‘,name) def read2(): print(‘meet模塊‘) read1() def change(): global name name = ‘meet‘
導入
import 翻譯過來是一個導入的意思
模塊可以包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行
(import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優化手段是:
第一次導入後就將模塊名加載到內存了,後續的import語句僅是對已經加載到內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句),
如下
import spam #只在第一次導入時才執行meet.py內代碼,此處的顯式效果是只打印一次‘from the meet.py‘,當然其他的頂級代碼也都被執行了,
只不過沒有顯示效果.
代碼示例:
import meet import meet import meet import meet import meet 執行結果: 只打印一次 from the meet.py
每個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當做全局名稱空間,這樣我們在編寫自己的模塊時,
就不用擔心我們定義在自己模塊中全局變量會在被導入時,與使用者的全局變量沖突
示例:
當前是meet.py
import meet name = ‘alex‘ print(name) print(meet.name) ‘‘‘ 結果: from the meet.py alex guoboayuan ‘‘‘ import meet def read1(): print(666) meet.read1() ‘‘‘ from the meet.py meet模塊: guoboayuan ‘‘‘ import meet name = ‘日天‘ meet.change() print(name) print(meet.name) ‘‘‘ from the meet.py 日天 寶元 ‘‘‘
為模塊起別名
別名其實就是一個綽號,好處可以將很長的模塊名改成很短,方便使用.
import meet.py as t t.read1()
有利於代碼的擴展和優化
#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()
導入多個模塊
import os,sys,json 這樣寫可以但是不推薦
推薦寫法
import os
import sys
import json
多行導入:易於閱讀 易於編輯 易於搜索 易於維護
from ... import ...
from...import...使用
from meet import name, read1 print(name) read1() ‘‘‘ 執行結果: from the meet.py guoboayuan meet模塊: guoboayuan ‘‘‘
from...import... 與import對比
唯一的區別就是:使用from...import...則是將spam中的名字直接導入到當前的名稱空間中,所以在當前名稱空間中,
直接使用名字就可以了、無需加前綴:meet.
from...import...的方式有好處也有壞處
? 好處:使用起來方便了
? 壞處:容易與當前執行文件中的名字沖突
示例演示:
1.執行文件有與模塊同名的變量或者函數名,會有覆蓋效果。
name = ‘oldboy‘ from meet import name, read1, read2 print(name) ‘‘‘ 執行結果: from the meet.py guoboayuan ‘‘‘ from meet import name, read1, read2 name = ‘oldboy‘ print(name) ‘‘‘ 執行結果: oldboy ‘‘‘ def read1(): print(666) from meet import name, read1, read2 read1() ‘‘‘ 執行結果: from the meet.py meet模塊: guoboayuan ‘‘‘ from meet import name, read1, read2 def read1(): print(666) read1() ‘‘‘ 執行結果: from the meet.py 666 ‘‘‘
2.當前位置直接使用read1和read2就好了,執行時,仍然以spam.py文件全局名稱空間
#測試一:導入的函數read1,執行時仍然回到meet.py中尋找全局變量name #test.py from meet import read1 name = ‘alex‘ read1() ‘‘‘ 執行結果: from the meet.py meet--> read1 --> guobaoyuan ‘‘‘ #測試二:導入的函數read2,執行時需要調用read1(),仍然回到meet.py中找read1() #test.py from meet import read2 def read1(): print(‘==========‘) read2() ‘‘‘ 執行結果: from the meet.py meet --> read2 --> ‘meet模塊‘ --> read1 -->‘guobaoyuan‘ ‘‘‘
from導入的模式也支持as
from meet import read1 as read read()
from導入的時候,一行導入多個內容
from meet import read1,read2,name
全部導入
from meet import * #from spam import * 把spam中所有的不是以下劃線(_)開頭的名字都導入到當前位置 #大部分情況下我們的python程序不應該使用這種導入方式,因為*你不知道你導入什麽名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。 可以使用__all__來控制*(用來發布新版本),在meet.py中新增一行 __all__=[‘money‘,‘read1‘] #這樣在另外一個文件中用from spam import *就這能導入列表中規定的兩個名字
模塊循環導入問題
模塊循環/嵌套導入拋出異常的根本原因是由於在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()
模塊的重載(了解)
考慮到性能的原因,每個模塊只被導入一次,放入字典sys.module中,如果你改變了模塊的內容,你必須重啟程序,python不支持重新加載或卸載之前導入的模塊,
有的同學可能會想到直接從sys.module中刪除一個模塊不就可以卸載了嗎,註意了,你刪了sys.module中的模塊對象仍然可能被其他程序的組件所引用,因而不會被清楚。
特別的對於我們引用了這個模塊中的一個類,用這個類產生了很多對象,因而這些對象都有關於這個模塊的引用。
py文件的兩種功能
#編寫好的一個python文件可以有兩種用途: 一:腳本,一個文件就是整個程序,用來被執行 二:模塊,文件中存放著一堆功能,用來被導入使用 #python為我們內置了全局變量__name__, 當文件被當做腳本執行時:__name__ 等於‘__main__‘ 當文件被當做模塊導入時:__name__等於模塊名 #作用:用來控制.py文件在不同的應用場景下執行不同的邏輯(或者是在模塊文件中測試代碼) if __name__ == ‘__main__‘: print(‘from the meet.py‘) __all__ = [‘name‘, ‘read1‘, ] name = ‘guobaoyuan‘ def read1(): print(‘meet模塊:‘, name) def read2(): print(‘meet模塊‘) read1() def change(): global name name = ‘寶元‘ if __name__ == ‘__main__‘: # 在模塊文件中測試read1()函數 # 此模塊被導入時 __name__ 就變成了文件名,if條件不成立 # 所以read1不執行 read1()
模塊的搜索路徑
模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊
#模塊的查找順序 1、在第一次導入某個模塊時(比如spam),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),如果有則直接引用 ps:python解釋器在啟動時會自動加載一些模塊到內存中,可以使用sys.modules查看 2、如果沒有,解釋器則會查找同名的內建模塊 3、如果還沒有找到就從sys.path給出的目錄列表中依次尋找spam.py文件。 #需要特別註意的是:我們自定義的模塊名不應該與系統內置模塊重名。雖然每次都說,但是仍然會有人不停的犯錯。 #在初始化後,python程序可以修改sys.path,路徑放到前面的優先於標準庫被加載。 >>> import sys >>> sys.path.append(‘/a/b/c/d‘) >>> sys.path.insert(0,‘/x/y/z‘) #排在前的目錄,優先被搜索 註意:搜索時按照sys.path中從左到右的順序查找,位於前的優先被查找. #windows下的路徑不加r開頭,會語法錯誤 sys.path.insert(0,r‘C:\Users\Administrator\PycharmProjects\a‘)
編譯Python文件(了解)
為了提高加載模塊的速度,強調強調強調:提高的是加載速度而絕非運行速度。python解釋器會在__pycache__目錄中下緩存每個模塊編譯後的版本,格式為:module.version.pyc。通常會包含python的版本號。例如,在CPython3.3版本下,spam.py模塊會被緩存成__pycache__/spam.cpython-33.pyc。這種命名規範保證了編譯後的結果多版本共存。
Python檢查源文件的修改時間與編譯的版本進行對比,如果過期就需要重新編譯。這是完全自動的過程。並且編譯的模塊是平臺獨立的,所以相同的庫可以在不同的架構的系統之間共享,即pyc使一種跨平臺的字節碼,類似於JAVA火.NET,是由python虛擬機來執行的,但是pyc的內容跟python的版本相關,不同的版本編譯後的pyc文件不同,2.5編譯的pyc文件不能到3.5上執行,並且pyc文件是可以反編譯的,因而它的出現僅僅是用來提升模塊的加載速度的,不是用來加密的。
#提示: 1.模塊名區分大小寫,foo.py與FOO.py代表的是兩個模塊 2.在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,只有在模塊被加載時,.pyc文件才是更快的 3.只有使用import語句是才將文件自動編譯為.pyc文件,在命令行或標準輸入中指定運行腳本則不會生成這類文件.
time模塊
time翻譯過來就是時間,有我們其實在之前編程的時候有用到過.
#常用方法 1.time.sleep(secs) (線程)推遲指定的時間運行。單位為秒。 2.time.time() 獲取當前時間戳
在計算中時間共有三種方式:
1.時間戳: 通常來說,時間戳表示的是從1970年1月1日00:00:00開始按秒計算的偏移量。我們運行“type(time.time())”,返回的是float類型
2.格式化字符串時間: 格式化的時間字符串(Format String): ‘1999-12-06’
python中時間日期格式化符號: %y 兩位數的年份表示(00-99) %Y 四位數的年份表示(000-9999) %m 月份(01-12) %d 月內中的一天(0-31) %H 24小時制小時數(0-23) %I 12小時制小時數(01-12) %M 分鐘數(00=59) %S 秒(00-59) %a 本地簡化星期名稱 %A 本地完整星期名稱 %b 本地簡化的月份名稱 %B 本地完整的月份名稱 %c 本地相應的日期表示和時間表示 %j 年內的一天(001-366) %p 本地A.M.或P.M.的等價符 %U 一年中的星期數(00-53)星期天為星期的開始 %w 星期(0-6),星期天為星期的開始 %W 一年中的星期數(00-53)星期一為星期的開始 %x 本地相應的日期表示 %X 本地相應的時間表示 %Z 當前時區的名稱 %% %號本身
3.結構化時間:元組(struct_time) struct_time元組共有9個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天等)
首先,我們先導入time模塊,來認識一下python中表示時間的幾種格式:
#導入時間模塊 >>>import time #時間戳 >>>time.time() 1500875844.800804 #時間字符串 >>>time.strftime("%Y-%m-%d %X") ‘2017-07-24 13:54:37‘ >>>time.strftime("%Y-%m-%d %H-%M-%S") ‘2017-07-24 13-55-04‘ #時間元組:localtime將一個時間戳轉換為當前時區的struct_time time.localtime() time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=13, tm_min=59, tm_sec=37, tm_wday=0, tm_yday=205, tm_isdst=0)
小結:時間戳是計算機能夠識別的時間;時間字符串是人能夠看懂的時間;元組則是用來操作時間的
時間格式轉換:
#時間戳-->結構化時間 #time.gmtime(時間戳) #UTC時間,與英國倫敦當地時間一致 #time.localtime(時間戳) #當地時間。例如我們現在在北京執行這個方法:與UTC時間相差8小時,UTC時間+8小時 = 北京時間 >>>time.gmtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=2, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) >>>time.localtime(1500000000) time.struct_time(tm_year=2017, tm_mon=7, tm_mday=14, tm_hour=10, tm_min=40, tm_sec=0, tm_wday=4, tm_yday=195, tm_isdst=0) #結構化時間-->時間戳 #time.mktime(結構化時間) >>>time_tuple = time.localtime(1500000000) >>>time.mktime(time_tuple) 1500000000.0 #結構化時間-->字符串時間 #time.strftime("格式定義","結構化時間") 結構化時間參數若不傳,則顯示當前時間 >>>time.strftime("%Y-%m-%d %X") ‘2017-07-24 14:55:36‘ >>>time.strftime("%Y-%m-%d",time.localtime(1500000000)) ‘2017-07-14‘ #字符串時間-->結構化時間 #time.strptime(時間字符串,字符串對應格式) >>>time.strptime("2017-03-16","%Y-%m-%d") time.struct_time(tm_year=2017, tm_mon=3, tm_mday=16, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=3, tm_yday=75, tm_isdst=-1) >>>time.strptime("07/24/2017","%m/%d/%Y") time.struct_time(tm_year=2017, tm_mon=7, tm_mday=24, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=205, tm_isdst=-1)
#結構化時間 --> %a %b %d %H:%M:%S %Y串 #time.asctime(結構化時間) 如果不傳參數,直接返回當前時間的格式化串 >>>time.asctime(time.localtime(1500000000)) ‘Fri Jul 14 10:40:00 2017‘ >>>time.asctime() ‘Mon Jul 24 15:18:33 2017‘ #時間戳 --> %a %b %d %H:%M:%S %Y串 #time.ctime(時間戳) 如果不傳參數,直接返回當前時間的格式化串 >>>time.ctime() ‘Mon Jul 24 15:19:07 2017‘ >>>time.ctime(1500000000) ‘Fri Jul 14 10:40:00 2017‘
我們看完了time在來看一個Python處理日期和時間的標準庫
datetime
獲取當前日期和時間
from datetime import datetime print(datetime.now()) ‘‘‘ 結果:2018-12-04 21:07:48.734886 ‘‘‘
註意:datetime
是模塊,datetime
模塊還包含一個datetime的
類,通過from datetime import datetime
導入的才是datetime
這個類。
如果僅導入import datetime
,則必須引用全名datetime.datetime
。
datetime.now()
返回當前日期和時間,其類型是datetime
。
獲取指定日期和時間
要指定某個日期和時間,我們直接用參數構造一個datetime
:
from datetime import datetime dt = datetime(2018,5,20,13,14) print(dt) ‘‘‘ 結果:2018-05-20 13:14:00 ‘‘‘
datetime轉換為timestamp(時間戳)
from datetime import datetime dt = datetime.now() new_timestamp = dt.timestamp() print(new_timestamp) ‘‘‘ 結果:1543931750.415896 ‘‘‘
timestamp轉換為datetime
import time from datetime import datetime new_timestamp = time.time() print(datetime.fromtimestamp(new_timestamp))
str轉換為datetime
很多時候,用戶輸入的日期和時間是字符串,要處理日期和時間,首先必須把str轉換為datetime。轉換方法是通過datetime.strptime()
實現,需要一個日期和時間的格式化字符串:
from datetime import datetime t = datetime.strptime(‘2018-4-1 00:00‘,‘%Y-%m-%d %H:%M‘) print(t) ‘‘‘ 結果: 2018-04-01 00:00:00 ‘‘‘
datetime轉換為str
如果已經有了datetime對象,要把它格式化為字符串顯示給用戶,就需要轉換為str,轉換方法是通過strftime()
實現的,同樣需要一個日期和時間的格式化字符串:
from datetime import datetime now = datetime.now() print(now.strftime(‘%a, %b %d %H:%M‘)) Mon, May 05 16:28
datetime加減
對日期和時間進行加減實際上就是把datetime往後或往前計算,得到新的datetime。加減可以直接用+
和-
運算符,不過需要導入timedelta
這個類:
from datetime import datetime, timedelta now = datetime.now() print(now) now1 = now + timedelta(hours=10) print(now1) now2 = now - timedelta(days=1) print(now2) now3 = now + timedelta(days=2, hours=12) print(now3)
可見,使用timedelta
你可以很容易地算出前幾天和後幾天的時刻。
小結註意
datetime
表示的時間需要時區信息才能確定一個特定的時間,否則只能視為本地時間。
Python自定義模塊