Python 子類繼承父類構造函數說明
如果在子類中需要父類的構造方法就需要顯式地調用父類的構造方法,或者不重寫父類的構造方法。
子類不重寫 __init__,實例化子類時,會自動調用父類定義的 __init__。
class Father(object):
def __init__(self, name):
self.name=name
print ( "name: %s" %( self.name) )
def getName(self):
return 'Father ' + self.name
class Son(Father):
def getName(self):
return 'Son '+self.name
if __name__=='__main__':
son=Son('runoob')
print ( son.getName() )
輸出結果爲:
name: runoob
Son runoob
如果重寫了__init__ 時,實例化子類,就不會調用父類已經定義的 __init__,語法格式如下:
class Father(object):
def __init__(self, name):
self.name = name
print("name: %s" % self.name)
def getName(self):
return 'Father ' + self.name
class Son(Father):
def __init__(self, name):
print("hi")
self.name = name
def getName(self):
return 'Son ' + self.name
if __name__ == '__main__':
son = Son('runoob')
print(son.getName())
輸出結果爲:
hi
Son runoob
如果重寫了__init__ 時,要繼承父類的構造方法,可以使用 super 關鍵字:
super(子類,self).__init__(參數1,參數2,....)
還有一種經典寫法:
父類名稱.__init__(self,參數1,參數2,...)
class Father(object):
def __init__(self, name):
self.name = name
print("name: %s" % self.name)
def getName(self):
return 'Father ' + self.name
class Son(Father):
def __init__(self, name):
super(Son, self).__init__(name)
print("hi")
self.name = name
def getName(self):
return 'Son ' + self.name
if __name__ == '__main__':
son = Son('runoob')
print(son.getName())
輸出結果爲:
name: runoob
hi
Son runoob
總結:
情況一:子類需要自動調用父類的方法:子類不重寫__init__()方法,實例化子類後,會自動調用父類的__init__()的方法。
情況二:子類不需要自動調用父類的方法:子類重寫__init__()方法,實例化子類後,將不會自動調用父類的__init__()的方法。
情況三:子類重寫__init__()方法又需要調用父類的方法:使用super關鍵詞:
super(子類,self).__init__(參數1,參數2,....)
class Son(Father):
def __init__(self, name):
super(Son, self).__init__(name)
Python super() 函數
描述
super() 函數是用於調用父類(超類)的一個方法。
super 是用來解決多重繼承問題的,直接用類名調用父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查找順序(MRO)、重複調用(鑽石繼承)等種種問題。
MRO 就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。
語法
以下是 super() 方法的語法:
super(type[, object-or-type])
參數
- type -- 類。
- object-or-type -- 類,一般是 self
Python3.x 和 Python2.x 的一個區別是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
Python3.x 實例:
class A:
def add(self, x):
y = x+1
print(y)
class B(A):
def add(self, x):
super().add(x)
b = B()
b.add(2) # 3
Python2.x 實例:
class A(object): # Python2.x 記得繼承 object
def add(self, x):
y = x+1
print(y)
class B(A):
def add(self, x):
super(B, self).add(x)
b = B()
b.add(2) # 3
返回值
無。
實例
以下展示了使用 super 函數的實例:
class FooParent(object):
def __init__(self):
self.parent = 'I\'m the parent.'
print('Parent')
def bar(self, message):
print("%s from Parent" % message)
class FooChild(FooParent):
def __init__(self):
# super(FooChild,self) 首先找到 FooChild 的父類(就是類 FooParent),然後把類 FooChild 的對象轉換爲類 FooParent 的對象
super(FooChild, self).__init__()
print('Child')
def bar(self, message):
super(FooChild, self).bar(message)
print('Child bar fuction')
print(self.parent)
if __name__ == '__main__':
fooChild = FooChild()
fooChild.bar('HelloWorld')
執行結果:
Parent
Child
HelloWorld from Parent
Child bar fuction
I'm the parent.
直接用類名調用父類方法在使用單繼承的時候沒問題,但是如果使用多繼承,會涉及到查找順序(MRO)。一開始看到這句話,不太理解。
看了這個文章後,明白了。
我們在學習 Python 類的時候,總會碰見書上的類中有 __init__() 這樣一個函數,很多同學百思不得其解,其實它就是 Python 的構造方法。
構造方法類似於類似 init() 這種初始化方法,來初始化新創建對象的狀態,在一個對象被創建以後會立即調用,比如像實例化一個類:
f = FooBar()
f.init()
使用構造方法就能讓它簡化成如下形式:
f = FooBar()
你可能還沒理解到底什麼是構造方法,什麼是初始化,下面我們再來舉個例子:
class FooBar:
def __init__(self):
self.somevar = 42
>>>f = FooBar()
>>>f.somevar
我們會發現在初始化 FooBar 中的 somevar 的值爲 42 之後,實例化直接就能夠調用 somevar 的值;如果說你沒有用構造方法初始化值得話,就不能夠調用,明白了嗎?
在明白了構造方法之後,我們來點進階的問題,那就是構造方法中的初始值無法繼承的問題。
例子:
class Bird:
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print('Ahahahah')
else:
print('No thanks!')
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk'
def sing(self):
print(self.sound)
sb = SongBird()
sb.sing() # 能正常輸出
sb.eat() # 報錯,因爲 songbird 中沒有 hungry 特性
那解決這個問題的辦法有兩種:
1、調用未綁定的超類構造方法(多用於舊版 python 陣營)
class SongBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk'
def sing(self):
print self.sound
原理:在調用了一個實例的方法時,該方法的self參數會自動綁定到實例上(稱爲綁定方法);如果直接調用類的方法(比如Bird.__init__),那麼就沒有實例會被綁定,可以自由提供需要的self參數(未綁定方法)。
2、使用super函數(只在新式類中有用)
class SongBird(Bird):
def __init__(self):
super(SongBird, self).__init__()
self.sound = 'Squawk'
def sing(self):
print(self.sound)
原理:它會查找所有的超類,以及超類的超類,直到找到所需的特性爲止。
經典的菱形繼承案例,B C 繼承 A,然後 D 繼承 BC,創造一個 D 的對象。
---> B ---
A --| |--> D
---> C ---
使用 super() 可以很好地避免構造函數被調用兩次。
# 思考題正確答案如下:
class A:
def __init__(self):
print('enter A')
print('leave A')
class B(A):
def __init__(self):
print('enter B')
super().__init__()
print('leave B')
class C(A):
def __init__(self):
print('enter C')
super().__init__()
print('leave C')
class D(B, C):
def __init__(self):
print('enter D')
super().__init__()
print('leave D')
d = D()
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D
Python3 命名空間和作用域
命名空間
先看看官方文檔的一段話:
A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。
命名空間(Namespace)是從名稱到對象的映射,大部分的命名空間都是通過 Python 字典來實現的。
命名空間提供了在項目中避免名字衝突的一種方法。各個命名空間是獨立的,沒有任何關係的,所以一個命名空間中不能有重名,但不同的命名空間是可以重名而沒有任何影響。
我們舉一個計算機系統中的例子,一個文件夾(目錄)中可以包含多個文件夾,每個文件夾中不能有相同的文件名,但不同文件夾中的文件可以重名。
一般有三種命名空間:
- 內置名稱(built-in names), Python 語言內置的名稱,比如函數名 abs、char 和異常名稱 BaseException、Exception 等等。
- 全局名稱(global names),模塊中定義的名稱,記錄了模塊的變量,包括函數、類、其它導入的模塊、模塊級的變量和常量。
- 局部名稱(local names),函數中定義的名稱,記錄了函數的變量,包括函數的參數和局部定義的變量。(類中定義的也是)
命名空間查找順序:
假設我們要使用變量 runoob,則 Python 的查找順序爲:局部的命名空間去 -> 全局命名空間 -> 內置命名空間。
如果找不到變量 runoob,它將放棄查找並引發一個 NameError 異常:
NameError: name 'runoob' is not defined。
命名空間的生命週期:
命名空間的生命週期取決於對象的作用域,如果對象執行完成,則該命名空間的生命週期就結束。
因此,我們無法從外部命名空間訪問內部命名空間的對象。
# var1 是全局名稱
var1 = 5
def some_func():
# var2 是局部名稱
var2 = 6
def some_inner_func():
# var3 是內嵌的局部名稱
var3 = 7
如下圖所示,相同的對象名稱可以存在於多個命名空間中。
作用域
A scope is a textual region of a Python program where a namespace is directly accessible. "Directly accessible" here means that an unqualified reference to a name attempts to find the name in the namespace.
作用域就是一個 Python 程序可以直接訪問命名空間的正文區域。
在一個 python 程序中,直接訪問一個變量,會從內到外依次訪問所有的作用域直到找到,否則會報未定義的錯誤。
Python 中,程序的變量並不是在哪個位置都可以訪問的,訪問權限決定於這個變量是在哪裏賦值的。
變量的作用域決定了在哪一部分程序可以訪問哪個特定的變量名稱。Python的作用域一共有4種,分別是:
有四種作用域:
- L(Local):最內層,包含局部變量,比如一個函數/方法內部。
- E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。比如兩個嵌套函數,一個函數(或類) A 裏面又包含了一個函數 B ,那麼對於 B 中的名稱來說 A 中的作用域就爲 nonlocal。
- G(Global):當前腳本的最外層,比如當前模塊的全局變量。
- B(Built-in): 包含了內建的變量/關鍵字等。,最後被搜索
規則順序: L –> E –> G –>gt; B。
在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內置中找。
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 閉包函數外的函數中
def inner():
i_count = 2 # 局部作用域
內置作用域是通過一個名爲 builtin 的標準模塊來實現的,但是這個變量名自身並沒有放入內置作用域內,所以必須導入這個文件才能夠使用它。在Python3.0中,可以使用以下的代碼來查看到底預定義了哪些變量:
>>> import builtins
>>> dir(builtins)
import builtins
print(dir(builtins))
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
Python 中只有模塊(module),類(class)以及函數(def、lambda)纔會引入新的作用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)是不會引入新的作用域的,也就是說這些語句內定義的變量,外部也可以訪問,如下代碼:
>>> if True:
... msg = 'I am from Runoob'
...
>>> msg
'I am from Runoob'
>>>
實例中 msg 變量定義在 if 語句塊中,但外部還是可以訪問的。
如果將 msg 定義在函數中,則它就是局部變量,外部不能訪問:
>>> def test():
... msg_inner = 'I am from Runoob'
...
>>> msg_inner
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'msg_inner' is not defined
>>>
從報錯的信息上看,說明了 msg_inner 未定義,無法使用,因爲它是局部變量,只有在函數內可以使用。
全局變量和局部變量
定義在函數內部的變量擁有一個局部作用域,定義在函數外的擁有全局作用域。
局部變量只能在其被聲明的函數內部訪問,而全局變量可以在整個程序範圍內訪問。調用函數時,所有在函數內聲明的變量名稱都將被加入到作用域中。如下實例:
total = 0 # 這是一個全局變量
# 可寫函數說明
def sum(arg1, arg2):
# 返回2個參數的和."
total = arg1 + arg2 # total在這裏是局部變量.
print("函數內是局部變量 : ", total)
return total
# 調用sum函數
sum(10, 20)
print("函數外是全局變量 : ", total)
以上實例輸出結果:
函數內是局部變量 : 30
函數外是全局變量 : 0
global 和 nonlocal關鍵字
當內部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字了。
以下實例修改全局變量 num:
num = 1
def fun1():
global num # 需要使用 global 關鍵字聲明
print(num)
num = 123
print(num)
fun1()
print(num)
以上實例輸出結果:
1
123
123
如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關鍵字了,如下實例:
def outer():
num = 10
def inner():
nonlocal num # nonlocal關鍵字聲明
num = 100
print(num)
inner()
print(num)
outer()
以上實例輸出結果:
100
100
另外有一種特殊情況,假設下面這段代碼被運行:
a = 10
def test():
a = a + 1
print(a)
test()
以上程序執行,報錯信息如下:
Traceback (most recent call last):
File "test.py", line 7, in <module>
test()
File "test.py", line 5, in test
a = a + 1
UnboundLocalError: local variable 'a' referenced before assignment
錯誤信息爲局部作用域引用錯誤,因爲 test 函數中的 a 使用的是局部,未定義,無法修改。
修改 a 爲全局變量,通過函數參數傳遞,可以正常執行輸出結果爲:
a = 10
def test(a):
a = a + 1
print(a)
test(a)
執行輸出結果爲:
11
MEMO
eg1:
作爲一個從 Java 過來的人來說,Python 的作用域有點奇葩,作爲開發者,只需要關注全局作用域和局部作用域就好了,全局作用域就是說白了模塊(單個 .py 文件中)直接聲明的變量。
比如有個 demo.py 的文件,含有以下代碼:
var_global = 'global_var' # 這個var_global就是全局作用域
def globalFunc():
var_local = 'local_var' # 這個就是局部變量
class demo():
class_demo_local_var = 'class member' # 這裏也是局部變量
def localFunc(self):
var_locals = 'local_func_var' # 這裏也是局部變量
以上只是說明了全局變量僅僅是在 .py 文件中直接聲明的變量叫全局變量,還有在 .py 文件中直接寫的邏輯代碼塊中,也是全局變量。也就是說在 if/elif/else/、try/except、for/while 等邏輯代碼塊中的變量。
這個教程中在介紹三種命令空間的時候,說查找變量的順序爲局部的命名空間去 -> 全局命名空間 -> 內置命名空間,但是我理解的變量查找順序爲:當前域 -> 外部域(如果有) -> 全局域 -> 內置域。
光說沒有什麼概念,我們來一段代碼就清楚了。
我們以 demo1.py 爲例子:
global_var = 'this var on global space'
'''
申明global_var這個位置就是全局域,也就是教程中所說的全局作用域,
同時它也是直接聲明在文件中的,而不是聲明在函數中或者類中的變量
'''
class demo():
class_demo_local_var = 'class member'
'''
雖然class_demo_local_var在這裏是局部變量,這個局部變量的域相對於var_locals是外部域,
所以可以直接被var_locals所在的更小的局部域訪問
'''
def localFunc(self):
var_locals = 'local_func_var'
'''
這裏也是局部變量,但是相對於class_demo_local_var變量,卻是更小的域,
因此class_demo_local_var所在的哪個域無法訪問到當前域來
'''
print(self.class_demo_local_var) # 到這裏會查找當前域中有沒有class_demo_local_var這個變量,然後再到相對於當前域的外部域去查找變量
Python3 標準庫概覽
操作系統接口
os模塊提供了不少與操作系統相關聯的函數。
>>> import os
>>> os.getcwd() # 返回當前的工作目錄
'C:\\Python34'
>>> os.chdir('/server/accesslogs') # 修改當前的工作目錄
>>> os.system('mkdir today') # 執行系統命令 mkdir
0
import os
print(os.getcwd())
建議使用 "import os" 風格而非 "from os import *"。這樣可以保證隨操作系統不同而有所變化的 os.open() 不會覆蓋內置函數 open()。
在使用 os 這樣的大型模塊時內置的 dir() 和 help() 函數非常有用:
>>> import os
>>> dir(os)
<returns a list of all module functions>
>>> help(os)
<returns an extensive manual page created from the module's docstrings>
import os
dir(os)
help(os)
針對日常的文件和目錄管理任務,:mod:shutil 模塊提供了一個易於使用的高級接口:
import shutil
help(shutil)
Help on module shutil:
NAME
shutil - Utility functions for copying and archiving files and directory trees.
DESCRIPTION
XXX The functions here don't copy the resource fork or other metadata on Mac.
DATA
__all__ = ['copyfileobj', 'copyfile', 'copymode', 'copystat', 'copy', ...
FILE
d:\program files\python37\lib\shutil.py
[點擊並拖拽以移動]
import shutil
shutil.copyfile('data.db', 'archive.db')
shutil.move('/build/executables', 'installdir')
文件通配符
glob模塊提供了一個函數用於從目錄通配符搜索中生成文件列表:
>>> import glob
>>> glob.glob('*.py')
['primes.py', 'random.py', 'quote.py']
import glob
print(glob.glob('*.py'))
help(glob)
['test.py', 'test2.py', 'test3.py']
Help on module glob:
NAME
glob - Filename globbing utility.
FUNCTIONS
escape(pathname)
Escape all special characters.
glob(pathname, *, recursive=False)
Return a list of paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If recursive is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
iglob(pathname, *, recursive=False)
Return an iterator which yields the paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If recursive is true, the pattern '**' will match any files and
zero or more directories and subdirectories.
DATA
__all__ = ['glob', 'iglob', 'escape']
FILE
d:\program files\python37\lib\glob.py
命令行參數
通用工具腳本經常調用命令行參數。這些命令行參數以鏈表形式存儲於 sys 模塊的 argv 變量。例如在命令行中執行 "python demo.py one two three" 後可以得到以下輸出結果:
>>> import sys
>>> print(sys.argv)
['demo.py', 'one', 'two', 'three']
import sys
print(sys.argv)
錯誤輸出重定向和程序終止
sys 還有 stdin,stdout 和 stderr 屬性,即使在 stdout 被重定向時,後者也可以用於顯示警告和錯誤信息。
>>> sys.stderr.write('Warning, log file not found starting a new one\n')
Warning, log file not found starting a new one
import sys
sys.stderr.write('Warning, log file not found starting a new one\n')
Warning, log file not found starting a new one
大多腳本的定向終止都使用 "sys.exit()"。
字符串正則匹配
re模塊爲高級字符串處理提供了正則表達式工具。對於複雜的匹配和處理,正則表達式提供了簡潔、優化的解決方案:
>>> import re
>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat')
'cat in the hat'
如果只需要簡單的功能,應該首先考慮字符串方法,因爲它們非常簡單,易於閱讀和調試:
>>> 'tea for too'.replace('too', 'two')
'tea for two'
數學
math模塊爲浮點運算提供了對底層C函數庫的訪問:
>>> import math
>>> math.cos(math.pi / 4)
0.70710678118654757
>>> math.log(1024, 2)
10.0
random提供了生成隨機數的工具。
>>> import random
>>> random.choice(['apple', 'pear', 'banana'])
'apple'
>>> random.sample(range(100), 10) # sampling without replacement
[30, 83, 16, 4, 8, 81, 41, 50, 18, 33]
>>> random.random() # random float
0.17970987693706186
>>> random.randrange(6) # random integer chosen from range(6)
4
import re
import math
re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
print(re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest'))
print(re.sub(r'(\b[a-z]+) \1', r'\1', 'cat in the the hat'))
print("--------------------------------------------------------------")
print('tea for too'.replace('too', 'two'))
print("--------------------------------------------------------------")
# math模塊爲浮點運算提供了對底層C函數庫的訪問:
print(math.cos(math.pi / 4))
print(math.log(1024, 2))
print(math.pow(2, 10))
print(math.log10(100))
print("--------圓周率--------")
print(math.pi) # 圓周率
print(math.e) # 常數e
print("--------------------------------------------------------------")
print(math.inf)
print(math.nan)
print(math.tau)
print("--------------------------------------------------------------")
['foot', 'fell', 'fastest']
cat in the hat
--------------------------------------------------------------
tea for two
--------------------------------------------------------------
0.7071067811865476
10.0
1024.0
2.0
--------圓周率--------
3.141592653589793
2.718281828459045
--------------------------------------------------------------
inf
nan
6.283185307179586
--------------------------------------------------------------
訪問 互聯網
有幾個模塊用於訪問互聯網以及處理網絡通信協議。其中最簡單的兩個是用於處理從 urls 接收的數據的 urllib.request 以及用於發送電子郵件的 smtplib:
>>> from urllib.request import urlopen
>>> for line in urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl'):
... line = line.decode('utf-8') # Decoding the binary data to text.
... if 'EST' in line or 'EDT' in line: # look for Eastern Time
... print(line)
<BR>Nov. 25, 09:43:32 PM EST
>>> import smtplib
>>> server = smtplib.SMTP('localhost')
>>> server.sendmail('[email protected]', '[email protected]',
... """To: [email protected]
... From: [email protected]
...
... Beware the Ides of March.
... """)
>>> server.quit()
日期和時間
datetime模塊爲日期和時間處理同時提供了簡單和複雜的方法。
支持日期和時間算法的同時,實現的重點放在更有效的處理和格式化輸出。
該模塊還支持時區處理:
>>> # dates are easily constructed and formatted
>>> from datetime import date
>>> now = date.today()
>>> now
datetime.date(2003, 12, 2)
>>> now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
'12-02-03. 02 Dec 2003 is a Tuesday on the 02 day of December.'
>>> # dates support calendar arithmetic
>>> birthday = date(1964, 7, 31)
>>> age = now - birthday
>>> age.days
14368
# dates are easily constructed and formatted
from datetime import date, datetime
now = date.today()
print(now)
now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B.")
print(now.strftime("%m-%d-%y. %d %b %Y is a %A on the %d day of %B."))
# dates support calendar arithmetic
birthday = date(1964, 7, 31)
age = now - birthday
print(age.days)
2020-01-02
01-02-20. 02 Jan 2020 is a Thursday on the 02 day of January.
20243
數據壓縮
以下模塊直接支持通用的數據打包和壓縮格式:zlib,gzip,bz2,zipfile,以及 tarfile。
>>> import zlib
>>> s = b'witch which has which witches wrist watch'
>>> len(s)
41
>>> t = zlib.compress(s)
>>> len(t)
37
>>> zlib.decompress(t)
b'witch which has which witches wrist watch'
>>> zlib.crc32(s)
226805979
import zlib
s = b'witch which has which witches wrist watch'
print(len(s))
t = zlib.compress(s) # 壓縮
print(len(t))
# zlib.decompress(t)
print(zlib.decompress(t)) # 解壓
# zlib.crc32(s)
print(zlib.crc32(s))
41
37
b'witch which has which witches wrist watch'
226805979
性能度量
有些用戶對了解解決同一問題的不同方法之間的性能差異很感興趣。Python 提供了一個度量工具,爲這些問題提供了直接答案。
例如,使用元組封裝和拆封來交換元素看起來要比使用傳統的方法要誘人的多,timeit 證明了現代的方法更快一些。
>>> from timeit import Timer
>>> Timer('t=a; a=b; b=t', 'a=1; b=2').timeit()
0.57535828626024577
>>> Timer('a,b = b,a', 'a=1; b=2').timeit()
0.54962537085770791
相對於 timeit 的細粒度,:mod:profile 和 pstats 模塊提供了針對更大代碼塊的時間度量工具。
測試模塊
開發高質量軟件的方法之一是爲每一個函數開發測試代碼,並且在開發過程中經常進行測試
doctest模塊提供了一個工具,掃描模塊並根據程序中內嵌的文檔字符串執行測試。
測試構造如同簡單的將它的輸出結果剪切並粘貼到文檔字符串中。
通過用戶提供的例子,它強化了文檔,允許 doctest 模塊確認代碼的結果是否與文檔一致:
def average(values):
"""Computes the arithmetic mean of a list of numbers.
>>> print(average([20, 30, 70]))
40.0
"""
return sum(values) / len(values)
import doctest
doctest.testmod() # 自動驗證嵌入測試
import doctest
def average(values):
"""Computes the arithmetic mean of a list of numbers.
print(average([20, 30, 70]))
40.0
"""
return sum(values) / len(values)
doctest.testmod() # 自動驗證嵌入測試
unittest模塊不像 doctest模塊那麼容易使用,不過它可以在一個獨立的文件裏提供一個更全面的測試集:
import unittest
class TestStatisticalFunctions(unittest.TestCase):
def test_average(self):
self.assertEqual(average([20, 30, 70]), 40.0)
self.assertEqual(round(average([1, 5, 7]), 1), 4.3)
self.assertRaises(ZeroDivisionError, average, [])
self.assertRaises(TypeError, average, 20, 30, 70)
unittest.main() # Calling from the command line invokes all tests