1. 程式人生 > >萬惡之源 - Python 自定義模組

萬惡之源 - 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語句是可以在程式中的任意位置使用的,且針對同一個模組很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

第一次匯入模組執行三件事

1.為原始檔(meet模組)建立新的名稱空間,在meet檔案中定義的函式和方法若是使用到了global時訪問的就是這個名稱空間。

2.在新建立的名稱空間中執行模組中包含的程式碼,見初始匯入import meet
​ 提示:匯入模組時到底執行了什麼?
​ 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.
​ 事實上函式定義也是“被執行”的語句,模組級別函式定義的執行將函式名放
​ 入模組全域性名稱空間表,用globals()可以檢視

3.建立名字meet來引用該名稱空間
​ 這個名字和變數名沒什麼區別,都是‘第一類的’,且使用meet.名字的方式
​ 可以訪問meet.py檔案中定義的名字,meet.名字與test.py中的名字來自
​ 兩個完全不同的地方。

ps:重複匯入會直接引用記憶體中已經載入好的結果

被匯入模組有獨立的名稱空間。

  每個模組都是一個獨立的名稱空間,定義在這個模組中的函式,把這個模組的名稱空間當做全域性名稱空間,這樣我們在編寫自己的模組時,
就不用擔心我們定義在自己模組中全域性變數會在被匯入時,與使用者的全域性變數衝突

示例:

當前是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'
'''

也支援as

from meet import read1 as read
read()

一行匯入多個

from meet import read1,read2,name

from ... 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中的模組物件仍然可能被其他程式的元件所引用,因而不會被清楚。

特別的對於我們引用了這個模組中的一個類,用這個類產生了很多物件,因而這些物件都有關於這個模組的引用。

如果只是你想互動測試的一個模組,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這隻能用於測試環境。

aa.py初始內容
def func1():
    print('func1')
執行test檔案
import time,importlib
import aa

time.sleep(20)
# importlib.reload(aa)
aa.func1()

在20秒的等待時間裡,修改aa.py中func1的內容,等待test.py的結果。

開啟importlib註釋,重新測試

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檔案。


#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.

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

#在初始化後,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直譯器會在__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檔案是可以反編譯的,因而它的出現僅僅是用來提升模組的載入速度的,不是用來加密的。

詳細說明
#python直譯器在以下兩種情況下不檢測快取
#1 如果是在命令列中被直接匯入模組,則按照這種方式,每次匯入都會重新編譯,並且不會儲存編譯後的結果(python3.3以前的版本應該是這樣)
    python -m spam.py

#2 如果原始檔不存在,那麼快取的結果也不會被使用,如果想在沒有原始檔的情況下來使用編譯後的結果,則編譯後的結果必須在源目錄下
sh-3.2# ls
__pycache__ spam.py
sh-3.2# rm -rf spam.py 
sh-3.2# mv __pycache__/spam.cpython-36.pyc ./spam.pyc
sh-3.2# python3 spam.pyc 
spam
 

#提示:
1.模組名區分大小寫,foo.py與FOO.py代表的是兩個模組
2.你可以使用-O或者-OO轉換python命令來減少編譯模組的大小
    -O轉換會幫你去掉assert語句
    -OO轉換會幫你去掉assert語句和__doc__文件字串
    由於一些程式可能依賴於assert語句或文件字串,你應該在在確認需要
    的情況下使用這些選項。
3.在速度上從.pyc檔案中讀指令來執行不會比從.py檔案中讀指令執行更快,只有在模組被載入時,.pyc檔案才是更快的

4.只有使用import語句是才將檔案自動編譯為.pyc檔案,在命令列或標準輸入中指定執行指令碼則不會生成這類檔案,因而我們可以使用compieall模組為一個目錄中的所有模組建立.pyc檔案

模組可以作為一個指令碼(使用python -m compileall)編譯Python源  
python -m compileall /module_directory 遞迴著編譯
如果使用python -O -m compileall /module_directory -l則只一層
  
命令列裡使用compile()函式時,自動使用python -O -m compileall
  
詳見:https://docs.python.org/3/library/compileall.html

#module-compileall詳細的

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個元素共九個元素:(年,月,日,時,分,秒,一年中第幾周,一年中第幾天等)img

首先,我們先匯入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)

小結:時間戳是計算機能夠識別的時間;時間字串是人能夠看懂的時間;元組則是用來操作時間的

時間格式轉換:

img

#時間戳-->結構化時間
#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)

img

#結構化時間 --> %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'

計算時間差:

import time
true_time=time.mktime(time.strptime('2017-09-11 08:30:00','%Y-%m-%d %H:%M:%S'))
time_now=time.mktime(time.strptime('2017-09-12 11:00:00','%Y-%m-%d %H:%M:%S'))
dif_time=time_now-true_time
struct_time=time.gmtime(dif_time)
print('過去了%d年%d月%d天%d小時%d分鐘%d秒'%(struct_time.tm_year-1970,struct_time.tm_mon-1,
                                       struct_time.tm_mday-1,struct_time.tm_hour,
                                       struct_time.tm_min,struct_time.tm_sec))

我們看完了time在來看一個Python處理日期和時間的標準庫

獲取當前日期和時間

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()
now
datetime.datetime(2015, 5, 18, 16, 57, 3, 540997)
now + timedelta(hours=10)
datetime.datetime(2015, 5, 19, 2, 57, 3, 540997)
now - timedelta(days=1)
datetime.datetime(2015, 5, 17, 16, 57, 3, 540997)
now + timedelta(days=2, hours=12)
datetime.datetime(2015, 5, 21, 4, 57, 3, 540997)

可見,使用timedelta你可以很容易地算出前幾天和後幾天的時刻。

小結

datetime表示的時間需要時區資訊才能確定一個特定的時間,否則只能視為本地時間。

如果要儲存datetime,最佳方法是將其轉換為timestamp再儲存,因為timestamp的值與時區完全無關。