1. 程式人生 > >Python自定義模塊

Python自定義模塊

3.3 nbsp 技術 自動編譯 地方 中新 win 用戶輸入 執行

自定義模塊

我們今天來學習一下自定義模塊(也就是私人訂制),我們要自定義模塊,首先就要知道什麽是模塊啊

一個函數封裝一個功能,比如現在有一個軟件,不可能將所有程序都寫入一個文件,所以咱們應該分文件,組織結構要好,代碼不冗余,所以要分文件,但是分文件,分了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,rC:\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自定義模塊