簡明 Python3 教程 | 學習筆記

GitBook 原電子書地址:簡明 Python 教程

《A Byte of Python》是一本由 Swaroop C H 編寫,旨在於介紹如何使用 Python 語言進行編程的自由圖書。它以教材與指南的形式爲入門者介紹 Python 語言。如果你對電腦知識的瞭解僅限於如何保存文本文件的話,那這本書就是爲你準備的。
2005 年,沈潔元將本書的 1.20 版引進中國,並完成了全本翻譯,將本書的譯名定爲《簡明 Python 教程》。2017年,漠倫基於原書 4.0 版重新翻譯,製作了本版全新譯本,並沿用同樣的譯名。
本版譯本定義爲 4.08c 版。最後修訂於 2018 年 2 月 4 日。

本人僅在該教程上,提煉自身薄弱的知識點,以及需注意的重點。該筆記不具有參考意義,有需要的讀者可看原 GitBook 電子書。—-ZJ


格式化方法: format()

有時候我們會想要從其他信息中構建字符串。這正是 format() 方法大有用武之地的地方。

age = 20
name = 'Swaroop'

print('{0} was {1} years old when he wrote this book'.format(name, age))
print('Why is {0} playing with that python?'.format(name))
Swaroop was 20 years old when he wrote this book
Why is Swaroop playing with that python?
name  + ' is '+ str(age) + ' years old'
'Swaroop is 20 years old'
# 對於浮點數 '0.333' 保留小數點(.)後三位
print('{0:.3f}'.format(1.0/3))
# 使用下劃線填充文本,並保持文字處於中間位置
# 使用 (^) 定義 '___hello___'字符串長度爲 11
print('{0:_^11}'.format('hello'))
# 基於關鍵詞輸出 'Swaroop wrote A Byte of Python'
print('{name} wrote {book}'.format(name='Swaroop',book='A byte of Python'))
0.333
___hello___
Swaroop wrote A byte of Python

由於我們正在討論格式問題,就要注意 print 總是會以一個不可見的“新一行”字符( \n )
結尾,因此重複調用 print 將會在相互獨立的一行中分別打印。爲防止打印過程中出現這一
換行符,你可以通過 end 指定其應以空白結尾:

print('wow')
print('a',end='')
print('b',end='')
print()
print('c', end=' ')
print('d', end=' ')
wow
ab
c d 

轉義序列(Escape Sequence)

  • 通過 \ 來指定單引號:要注意它可是反斜槓。
  • 雙引號,必須使用轉義序列 \ 來指定反斜槓本身。
  • \n 來表示新一行的開始
print('what\'s your name?')
print("what's your name?")
print('This is the first line.\nThis is the second line.')
what's your name?
what's your name?
This is the first line.
This is the second line.

原始字符串

如果你需要指定一些未經過特殊處理的字符串,比如轉義序列,那麼你需要在字符串前增加 r 或 R 來指定一個 原始(Raw) 字符串 。

針對正則表達式用戶的提示
在處理正則表達式時應全程使用原始字符串。否則,將會有大量 Backwhacking 需要處理。舉例說明的話,反向引用可以通過 ‘\1’ 或 r’\1’ 來實現。

print(r"Newlines are indicated by \n")
Newlines are indicated by \n
i = 5
print(i)
i = i + 1
print(i)

s = '''This is a multi-line string.
This is the second line'''
print(s)
5
6
This is a multi-line string.
This is the second line
'''
強烈建議你對於每一行物理行最多隻寫入一行邏輯行。這個觀點就是說你不應該使
用分號。實際上,我從未在 Python 程序中使用、甚至是見過一個分號。

在一類情況下這一方法會頗爲有用:如果你有一行非常長的代碼,你可以通過使用反斜槓將
其拆分成多個物理行。這被稱作顯式行連接(Explicit Line Joining)

注意: \ 後面不要有空格

'''

s = 'This is a string. \
This continues the string.'
print(s)
This is a string. This continues the string.
i = \
5
print(i)
5

運算符

  • // (整除)
    • x 除以 y 並對結果向下取整至最接近的整數。
    • 13 // 3 輸出 4 。
    • -13 // 3 輸出 -5 。
  • % (取模)

    • 返回除法運算後的餘數。
    • 13 % 3 輸出 1 。 -25.5 % 2.25 輸出 1.5 。
    • 對於整型數a,b來說,取模運算或者求餘運算的方法都是:

      • 1.求 整數商: c = a/b;
      • 2.計算模或者餘數: r = a - c*b.
    • 歸納:

      • 當a和b符號一致時,求模運算和求餘運算所得的c的值一致,因此結果一致。
      • 當符號不一致時,結果不一樣。求模運算結果的符號和b一致,求餘運算結果的符號和a一致。
  • << (左移)

    • 將數字的位向左移動指定的位數。(每個數字在內存中以二進制數表示,即 0 和1)
    • 2 << 2 輸出 8 。 2 用二進制數表示爲 10 。
    • 向左移 2 位會得到 1000 這一結果,表示十進制中的 8 。
  • >> (右移)
    • 將數字的位向右移動指定的位數。
    • 11 >> 1 輸出 5 。
    • 11 在二進制中表示爲 1011 ,右移一位後輸出 101 這一結果,表示十進制中的 5 。
13//3
4
-13//3 # 向下取整 -4.多 向下取整是 -5
-5
13%3
1
'''
c = a/b; -25.5/2.25 = -12 (向下取整)
r = a - c*b. r = -25.5 - (-12 * 2.25) = 1.5
'''
-25.5%2.25
1.5
'''
2  ----- 10 
左移兩位

8  ----- 1000 

(2**3, 2**2, 2**1, 2**0)

'''
2<<2
8
'''
11 ---- 1011 (8+2+1)
右移 1 位
5  ----- 101 (4+1) 

'''

11>>1
5
  • & (按位與)
    • 對數字進行按位與操作。
    • 5 & 3 輸出 1 。
  • | (按位或)
    • 對數字進行按位或操作。運算規則:0 ^ 0 = 0;0 ^ 1 = 1;1 ^ 0 = 1;1 ^ 1 = 0;
    • 5 | 3 輸出 7 。
  • ^ (按位異或)
    • 對數字進行按位異或操作。
    • 5 ^ 3 輸出 6 。
  • ~ (按位取反)
  • < (小於)
    • 返回 x 是否小於 y。所有的比較運算符返回的結果均爲 True 或 False 。請注意這些名稱之中的大寫字母。
    • 5 < 3 輸出 False , 3 < 6 輸出 True 。
print(5 < 3)
print(~5)
a = 2
a *= 3
print(a)
False
-6
6
number = 23
guess = int(input('Enter an integer :'))

if guess == number:
    print('Congratulations, you guessed it.')
    print('(but you do not win any prizes!)')
# 新塊在這裏結束
elif guess < number:
# 另一代碼塊
    print('No, it is a little higher than that')
# 你可以在此做任何你希望在該代碼塊內進行的事情
else:
    print('No, it is a little lower than that')
# 你必須通過猜測一個大於(>)設置數的數字來到達這裏。
print('Done')
Enter an integer :89
No, it is a little lower than that
Done
number = 23
running = True

while running:
    guess = int(input('Enter an integer:'))

    if guess == number:
        print('Congratulations, you guessed it.')
        # 這將導致 while 循環中止
        running = False
    elif guess < number:
        print('No, it is a little higher than that.')
    else:
        print('No, it is a little lower than that.')
else:
    print('The while loop is over.')
# 在這裏你可以做你想做的任何事
print('Done')
Enter an integer:90
No, it is a little lower than that.
Enter an integer:11
No, it is a little higher than that.
Enter an integer:23
Congratulations, you guessed it.
The while loop is over.
Done
for i in range(1,5):
    print(i)
else:
    print('The for loop is over')
1
2
3
4
The for loop is over
while True:
    s = input('Enter something:')
    if s == 'quit':
        break
    print('Length of the string is', len(s))
print('Done')    
Enter something:what?
Length of the string is 5
Enter something:quit
Done
# 用 continue 語句跳過代碼塊中的其餘語句。
while True:
    s = input('Enter something:')
    if s == 'quit':
        break
    if len(s) < 3:
        print('Too small')
        continue
    print('Input is of sufficient length')    
Enter something:hello
Input is of sufficient length
Enter something:o
Too small
Enter something:quit
def say(message, times=1):
    print(message * times)

say('Hello ')    
say('World ', 5)
Hello 
World World World World World 
'''
關鍵字參數(Keyword Arguments)
'''
def func(a, b=5, c=10):
    print('a is',a, 'and b is', b, 'and c is', c)

func(3,7)    
func(35, c= 30)
func(c=80,b=9,a=888)
a is 3 and b is 7 and c is 10
a is 35 and b is 5 and c is 30
a is 888 and b is 9 and c is 80
'''
可變參數
'''
def total(a=5, *numbers, **phonebook):
    print('a', a)

    for single_item in numbers:
        print('single_item', single_item)

    for first_part,second_part in phonebook.items():
        print(first_part,second_part)

print(total(10,1,2,3,Jack=101010,John=22323,Inge=1889))
a 10
single_item 1
single_item 2
single_item 3
Jack 101010
John 22323
Inge 1889
None
'''
文檔字符串(Documentation Strings)

該文檔字符串所約定的是一串多行字符串,其中第一行以某一大寫字母開始,以句號結束。
第二行爲空行,後跟的第三行開始是任何詳細的解釋說明。 在此強烈建議你在你所有重要功
能的所有文檔字符串中都遵循這一約定。

'''


def print_max(x, y):
    '''打印兩個數值中的最大數。

    這兩個數都應該是整數'''
    # 如果可能,將其轉換至整數類型
    x = int(x)
    y = int(y)
    if x > y:
        print(x, 'is maximum')
    else:
        print(y, 'is maximum')

print_max(3, 5)
print(print_max.__doc__)
5 is maximum
打印兩個數值中的最大數。

    這兩個數都應該是整數
help(print_max)
Help on function print_max in module __main__:

print_max(x, y)
    打印兩個數值中的最大數。

    這兩個數都應該是整數
import sys

print('The command line arguments are:')
for i in sys.argv:
    print(i)

print('\n\n The PYTHONPATH is',sys.path,'\n') 
>python module_using_sys.py we are arguments
The command line arguments are:
module_using_sys.py
we
are
arguments


 The Python Path is ['D:\\github\\python\\Deep-Learning\\python', 'D:\\Program Files\\Python36\\python36.zip', 'D:\\Program Files\\Python36\\DLLs', 'D:\\Program Files\\Python36\\lib', 'D:\\Program Files\\Python36', 'C:\\Users\\qhtf\\AppData\\Roaming\\Python\\Python36\\site-packages', 'D:\\Program Files\\Python36\\lib\\site-packages']
import os

print(os.getcwd())
D:\github\python\Deep-Learning\python
from sys import argv
from math import sqrt

help(sqrt)
print('Square root of 16 is', sqrt(16))
Help on built-in function sqrt in module math:

sqrt(...)
    sqrt(x)

    Return the square root of x.

Square root of 16 is 4.0
import mymodule

mymodule.say_hi()
print('Version', mymodule.__version__)
Hi, this is mymodule speaking.
Version 0.1
from mymodule import say_hi,__version__

say_hi()
print('Version',__version__)
Hi, this is mymodule speaking.
Version 0.1
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
import sys

# 給出 sys 模塊中的屬性名稱
dir(sys)

# 給出當前模塊的屬性名稱
dir()

包(Packages)

包是指一個包含模塊與一個特殊的 init.py 文件的文件夾,後者向 Python 表明這一文件夾是特別的,因爲其包含了 Python 模塊。
讓我們這樣設想:你想創建一個名爲“world”的包,其中還包含着“asia”、“africa”等其它子包,同時這些子包都包含了諸如“india”、 “madagascar”等模塊。

數據結構

數據結構(Data Structures)基本上人如其名——它們只是一種結構,能夠將一些數據聚合在一起。換句話說,它們是用來存儲一系列相關數據的集合。

Python 中有四種內置的數據結構——列表(List)、元組(Tuple)、字典(Dictionary)和集合(Set)。我們將瞭解如何使用它們,並利用它們將我們的編程之路變得更加簡單。

列表是可變的(Mutable)而字符串是不可變的(Immutable)。

'''
list 列表
'''
shoplist = ['apple', 'mango', 'carrot','banana']

print('I have', len(shoplist), 'items to purchase')
print('I have %d items to purchase'% len(shoplist))

print('These items are:', end=' ')
for item in shoplist:
    print(item, end=' ')

print('\n I also have to buy rice.')    
shoplist.append('rice')
print('My shopping list is now', shoplist)

print('I will sort my list now')
shoplist.sort()
print('Sorted shopping list is now', shoplist)

print('The first item I will buy is', shoplist[0])
olditem = shoplist[0]

del shoplist[0]

print('I bought the',olditem)

print('My shopping list is now',shoplist)
I have 4 items to purchase
I have 4 items to purchase
These items are: apple mango carrot banana 
 I also have to buy rice.
My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice']
I will sort my list now
Sorted shopping list is now ['apple', 'banana', 'carrot', 'mango', 'rice']
The first item I will buy is apple
I bought the apple
My shopping list is now ['banana', 'carrot', 'mango', 'rice']

元組(Tuple)

元組(Tuple)用於將多個對象保存到一起。你可以將它們近似地看作列表,但是元組不能提供列表類能夠提供給你的廣泛的功能。元組的一大特徵類似於字符串,它們是不可變的,也就是說,你不能編輯或更改元組。

zoo = ('python','elephant','penguin')
print('Number od animals in the zoo is',len(zoo))

new_zoo = 'monkey','camel',zoo

print('Number of cages in the new zoo is', len(new_zoo))
print('All animals in the zoo are',new_zoo)
print('Animals brought from old zoo are', new_zoo[2])

print('Last animal brought from old zoo is',new_zoo[2][2])
print('Number of animals in the new zoo is',len(new_zoo)-1 + len(new_zoo[2]))
Number od animals in the zoo is 3
Number of cages in the new zoo is 3
All animals in the zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin'))
Animals brought from old zoo are ('python', 'elephant', 'penguin')
Last animal brought from old zoo is penguin
Number of animals in the new zoo is 5
'''
Dictionary 字典

'''
ab = {
    'Swaroop': '[email protected]',
    'Larry': '[email protected]',
    'Matsumoto': '[email protected]',
    'Spammer': '[email protected]'
}

print("Swaroop's address is", ab['Swaroop'])

del ab['Spammer']

print('\nThere are {} contacts in the address-book\n'.format(len(ab)))
print('There are %d contacts in the address-book\n'%len(ab))

for name, address in ab.items():
    print('Contact {} at {}'.format(name, address))

ab['Guido'] = '[email protected]'

if 'Guido' in ab:
    print("\nGuido's address is",ab['Guido'])
Swaroop's address is [email protected]

There are 3 contacts in the address-book

There are 3 contacts in the address-book

Contact Swaroop at [email protected]
Contact Larry at [email protected]
Contact Matsumoto at [email protected]

Guido's address is [email protected]
shoplist = ['apple', 'mango', 'carrot', 'banana']
name = 'swaroop'

# Indexing or 'Subscription' operation #
# 索引或“下標(Subscription)”操作符 #
print('Item 0 is', shoplist[0])
print('Item 1 is', shoplist[1])
print('Item 2 is', shoplist[2])
print('Item 3 is', shoplist[3])
print('Item -1 is', shoplist[-1])
print('Item -2 is', shoplist[-2])
print('Character 0 is', name[0])
Item 0 is apple
Item 1 is mango
Item 2 is carrot
Item 3 is banana
Item -1 is banana
Item -2 is carrot
Character 0 is s
# Slicing on a list shoplist = ['apple', 'mango', 'carrot', 'banana']#
print('Item 1 to 3 is', shoplist[1:3]) # 索引 1 開始 到 3 不包括3 
print('Item 2 to end is', shoplist[2:])
print('Item 1 to -1 is', shoplist[1:-1])
print('Item start to end is', shoplist[:])
Item 1 to 3 is ['mango', 'carrot']
Item 2 to end is ['carrot', 'banana']
Item 1 to -1 is ['mango', 'carrot']
Item start to end is ['apple', 'mango', 'carrot', 'banana']
# 從某一字符串中切片 name = 'swaroop'#
print('characters 1 to 3 is', name[1:3])
print('characters 2 to end is', name[2:])
print('characters 1 to -1 is', name[1:-1])
print('characters start to end is', name[:])
characters 1 to 3 is wa
characters 2 to end is aroop
characters 1 to -1 is waroo
characters start to end is swaroop

你同樣可以在切片操作中提供第三個參數,這一參數將被視爲切片的步長(Step)(在默認情況下,步長大小爲 1):

ablist = ['a','b','c','d','e','f']

print(ablist[::1])
print(ablist[::2])
print(ablist[::3])
print(ablist[::-1])
print(ablist[::-2])
['a', 'b', 'c', 'd', 'e', 'f']
['a', 'c', 'e']
['a', 'd']
['f', 'e', 'd', 'c', 'b', 'a']
['f', 'd', 'b']
bri = set(['brazil', 'russia', 'india'])
'india' in bri
True
bric = bri.copy()
bric.add('china')
bric.issuperset(bri)
True
bri.add('russia')
bri.remove('russia')
print(bri & bric)
print(bri.intersection(bric))
{'india', 'brazil'}
{'india', 'brazil'}

引用

當你創建了一個對象並將其分配給某個變量時,變量只會查閱(Refer)某個對象,並且它也不會代表對象本身。也就是說,變量名只是指向你計算機內存中存儲了相應對象的那一部分。這叫作將名稱綁定(Binding)給那一個對象。

print('Simple Assignment')
shoplist = ['apple', 'mango', 'carrot', 'banana']
# mylist 只是指向同一對象的另一種名稱
mylist = shoplist

del shoplist[0]

print('shoplist is', shoplist)
print('mylist is', mylist)

# 注意到 shoplist 和 mylist 二者都
# 打印出了其中都沒有 apple 的同樣的列表,以此我們確認
# 它們指向的是同一個對象
Simple Assignment
shoplist is ['mango', 'carrot', 'banana']
mylist is ['mango', 'carrot', 'banana']
print('Copy by making a full slice')

# 通過生成一份完整的切片製作一份列表的副本
shoplist = ['apple', 'mango', 'carrot', 'banana']

mylist = shoplist[:]
del mylist[0]

print('shoplist is', shoplist)
print('mylist is', mylist)

# 注意到現在兩份列表已出現不同
Copy by making a full slice
shoplist is ['apple', 'mango', 'carrot', 'banana']
mylist is ['mango', 'carrot', 'banana']

有關字符串的更多內容

  • startswith 方法用於查找字符串是否以給定的字符串內容開頭。
  • in 運算符用以檢查給定的字符串是否是查詢的字符串中的一部分。
  • find 方法用於定位字符串中給定的子字符串的位置。如果找不到相應的子字符串, find會返回 -1。
  • str 類同樣還擁有一個簡潔的方法用以 聯結(Join) 序列中的項目,其中字符串將會作爲每一項目之間的分隔符,並以此生成並返回一串更大的字符串。
'''
有關字符串的更多內容

'''
# 這是一個字符串對象
name = 'Swaroop'

if name.startswith('Swa'):
    print('Yes, the string starts with "Swa"')

if  'a' in name:
    print('Yes, it contains the string "a"')

if name.find('war') != -1:    
    print('Yes, it contains the string "war"')

delimiter = '_*_'    
mylist = ['Brazil', 'Russia', 'India', 'China']
print(delimiter.join(mylist))

Yes, the string starts with "Swa"
Yes, it contains the string "a"
Yes, it contains the string "war"
Brazil_*_Russia_*_India_*_China

問題

我們希望解決的問題如下:

我想要一款程序來備份我所有的重要文件。

分析(Analysis)— 例如,我們應該如何指定哪些文件是我們需要備份
的?它們應該如何進行備份?儲存到哪裏?

設計(Design)

  • 需要備份的文件與目錄應在一份列表中予以指定。
  • 備份必須存儲在一個主備份目錄中。
  • 備份文件將打包壓縮成 zip 文件。
  • zip 壓縮文件的文件名由當前日期與時間構成。
  • 我們使用在任何 GNU/Linux 或 Unix 發行版中都會默認提供的標準 zip 命令進行打包。
  • 在這裏你需要瞭解到只要有命令行界面,你就可以使用任何需要用到的壓縮或歸檔命令。
'''
第一版


'''

import os
import time

# 1. 需要備份的文件與目錄將被
# 指定在一個列表中。
# 例如在 Windows 下:
# source = ['"C:\\My Documents"', 'C:\\Code']
# 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes']

source = ["D:\\github\\python\\Deep-Learning\\python\\source"]

# 在這裏要注意到我們必須在字符串中使用雙引號
# 用以括起其中包含空格的名稱。

#2. 備份文件必須存儲在一個
#主備份目錄中
#例如在 Windows 下:
# target_dir = 'E:\\Backup'
# 又例如在 Mac OS X 和 Linux 下:target_dir = '/Users/swa/backup'
target_dir = 'E:\\qqfile\\backup'

# 要記得將這裏的目錄地址修改至你將使用的路徑

# 3. 備份文件將打包壓縮成 zip 文件。
# 4. zip 壓縮文件的文件名由當前日期與時間構成。
target = target_dir + os.sep + time.strftime('%Y%m%d%H%M%S') + 'zip'

# 如果目標目錄還不存在,則進行創建

if not os.path.exists(target_dir):
    os.mkdir(target_dir) # 創建目錄

# 5. 我們使用 zip 命令將文件打包成 zip 格式
zip_command = 'zip -r {0} {1}'.format(target,' '.join(source))

# 運行備份   
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup Failed')

Zip command is:
zip -r E:\qqfile\backup\20180313152803zip D:\github\python\Deep-Learning\python\source
Running:
Successful backup to E:\qqfile\backup\20180313152803zip

os.sep 變量的使用方式——它將根據你的操作系統給出相應的分隔符,在
GNU/Linux 與 Unix 中它會是 ‘/’ ,在 Windows 中它會是 ‘\’ ,在 Mac OS 中它會是’:’ 。

第二版

我們的第一版腳本已經能夠工作了。然而,我們還可以對它作出一些改進,從而使它能夠更好地在每一天都可以正常工作。我們將這一階段稱之爲軟件的維護(Maintenance)階段。

我認爲有一種頗爲有用的改進是起用一種更好的文件命名機制——使用時間作爲文件名,存儲在以當前日期爲名字的文件夾中,這一文件夾則照常存儲在主備份目錄下。
- 這種機制的第一個優點在於你的備份會以分層的形式予以存儲,從而使得它們能更易於管理。
- 第二個優點是文件名能夠更短。
- 第三個優點在於由於只有當天進行了備份纔會創建相應的目錄,獨立的目錄能夠幫助你快速地檢查每天是否都進行了備份。

'''
第2版

'''

import os
import time

# 1. 需要備份的文件與目錄將被
# 指定在一個列表中。
# 例如在 Windows 下:
# source = ['"C:\\My Documents"', 'C:\\Code']
# 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes']

source = ["D:\\github\\python\\Deep-Learning\\python\\source"]

# 在這裏要注意到我們必須在字符串中使用雙引號
# 用以括起其中包含空格的名稱。

#2. 備份文件必須存儲在一個
#主備份目錄中
#例如在 Windows 下:
# target_dir = 'E:\\Backup'
# 又例如在 Mac OS X 和 Linux 下:target_dir = '/Users/swa/backup'
target_dir = 'E:\\qqfile\\backup'

# 要記得將這裏的目錄地址修改至你將使用的路徑

if not os.path.exists(target_dir):
    os.mkdir(target_dir) # 創建目錄

# 3. 備份文件將打包壓縮成 zip 文件。
# 4. 將當前日期作爲主備份目錄下的子目錄名稱
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 將當前時間作爲 zip 文件的文件名
now = time.strftime('%H%M%S')
# 如果目標目錄還不存在,則進行創建

# zip 文件名稱格式
target = today + os.sep + now +'.zip'

if not os.path.exists(today):
    os.mkdir(today) # 創建目錄
    print('Successfully created directory', today)

# 5. 我們使用 zip 命令將文件打包成 zip 格式
zip_command = 'zip -r {0} {1}'.format(target,' '.join(source))

# 運行備份   
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup Failed')

Successfully created directory E:\qqfile\backup\20180313
Zip command is:
zip -r E:\qqfile\backup\20180313\154421.zip D:\github\python\Deep-Learning\python\source
Running:
Successful backup to E:\qqfile\backup\20180313\154421.zip

第三版

第二版在我要製作多份備份時能夠正常工作,但當備份數量過於龐大時,我便很難找出備份之間有什麼區別了。例如,我可能對我的程序或者演示文稿做了重大修改,然後我想將這些修改與 zip 文件的文件名產生關聯。這可以通過將用戶提供的註釋內容添加到文件名中來實現。

預先提醒:下面給出的程序將不會正常工作,所以不必驚慌,只需跟着案例去做因爲你要在裏面學上一課。

'''
第3版

'''

import os
import time

# 1. 需要備份的文件與目錄將被
# 指定在一個列表中。
# 例如在 Windows 下:
# source = ['"C:\\My Documents"', 'C:\\Code']
# 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes']

source = ["D:\\github\\python\\Deep-Learning\\python\\source"]

# 在這裏要注意到我們必須在字符串中使用雙引號
# 用以括起其中包含空格的名稱。

#2. 備份文件必須存儲在一個
#主備份目錄中
#例如在 Windows 下:
# target_dir = 'E:\\Backup'
# 又例如在 Mac OS X 和 Linux 下:target_dir = '/Users/swa/backup'
target_dir = 'E:\\qqfile\\backup'

# 要記得將這裏的目錄地址修改至你將使用的路徑

if not os.path.exists(target_dir):
    os.mkdir(target_dir) # 創建目錄

# 3. 備份文件將打包壓縮成 zip 文件。
# 4. 將當前日期作爲主備份目錄下的子目錄名稱
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 將當前時間作爲 zip 文件的文件名
now = time.strftime('%H%M%S')
# 如果目標目錄還不存在,則進行創建

# 添加一條來自用戶的註釋以創建
# zip 文件的文件名
comment = input('Enter a comment -->')
# 檢查是否有評論鍵入

if len(comment) == 0:
    target = today + os.sep + now + '_' + comment.replace(' ','_') + '.zip'
else:
    target = today + os.sep + now + '_' + 
    comment.replace(' ','_') + '.zip'


if not os.path.exists(today):
    os.mkdir(today) # 創建目錄
    print('Successfully created directory', today)

# 5. 我們使用 zip 命令將文件打包成 zip 格式
zip_command = 'zip -r {0} {1}'.format(target,' '.join(source))

# 運行備份   
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup Failed')

  File "<ipython-input-2-a0cbe1ca32a2>", line 47
    target = today + os.sep + now + '_' +
                                          ^
SyntaxError: invalid syntax

它是如何(不)工作的

這個程序它跑不起來!Python 會說程序之中存在着語法錯誤,這意味着腳本並未擁有 Python期望看見的結構。當我們觀察 Python 給出的錯誤時,會看見它同時也告訴我們它檢測到錯誤的地方。所以我們開始從那個地方開始對我們的程序進行 Debug 工作。仔細觀察,我們會發現有一獨立的邏輯行被分成了兩行物理行,但我們並未指定這兩行物理行應該是一起的。基本上,Python 已經發現了該邏輯行中的加法運算符( + )沒有任何操作數,因此它不知道接下來應當如何繼續。因此,我們在程序中作出修正。當我們發現程序中的錯誤並對其進行修正時,我們稱爲“錯誤修復(Bug Fixing)”。

'''
第 4 版

'''

import os
import time

# 1. 需要備份的文件與目錄將被
# 指定在一個列表中。
# 例如在 Windows 下:
# source = ['"C:\\My Documents"', 'C:\\Code']
# 又例如在 Mac OS X 與 Linux 下: source = ['/Users/swa/notes']

source = ["D:\\github\\python\\Deep-Learning\\python\\source"]

# 在這裏要注意到我們必須在字符串中使用雙引號
# 用以括起其中包含空格的名稱。

#2. 備份文件必須存儲在一個
#主備份目錄中
#例如在 Windows 下:
# target_dir = 'E:\\Backup'
# 又例如在 Mac OS X 和 Linux 下:target_dir = '/Users/swa/backup'
target_dir = 'E:\\qqfile\\backup'

# 要記得將這裏的目錄地址修改至你將使用的路徑

if not os.path.exists(target_dir):
    os.mkdir(target_dir) # 創建目錄

# 3. 備份文件將打包壓縮成 zip 文件。
# 4. 將當前日期作爲主備份目錄下的子目錄名稱
today = target_dir + os.sep + time.strftime('%Y%m%d')
# 將當前時間作爲 zip 文件的文件名
now = time.strftime('%H%M%S')
# 如果目標目錄還不存在,則進行創建

# 添加一條來自用戶的註釋以創建
# zip 文件的文件名
comment = input('Enter a comment -->')
# 檢查是否有評論鍵入

if len(comment) == 0:
    target = today + os.sep + now + '_' + comment.replace(' ','_') + '.zip'
else:
    target = today + os.sep + now + '_' +\
    comment.replace(' ','_') + '.zip'


if not os.path.exists(today):
    os.mkdir(today) # 創建目錄
    print('Successfully created directory', today)

# 5. 我們使用 zip 命令將文件打包成 zip 格式
zip_command = 'zip -r -v {0} {1}'.format(target,' '.join(source))

# 運行備份   
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup Failed')

# 這裏沒有錯誤的,原書中給出的是 帶有錯誤的示例 for bug fixing
Enter a comment -->test
Zip command is:
zip -r -v E:\qqfile\backup\20180314\102221_test.zip D:\github\python\Deep-Learning\python\source
Running:
Successful backup to E:\qqfile\backup\20180314\102221_test.zip
'''
第 5 版

使用 zipfile 
'''

軟件開發流程

  1. What/做什麼(分析)
  2. How/怎麼做(設計)
  3. Do It/開始做(執行)
  4. Test/測試(測試與修復錯誤)
  5. Use/使用(操作或開發)
  6. Maintain/維護(改進)

面向對象編程

self

你一定會在想 Python 是如何給 self 賦值的,以及爲什麼你不必給它一個值。一個例子或許會讓這些疑問得到解答。假設你有一個 MyClass 的類,這個類下有一個實例 myobject 。當你調用一個這個對象的方法,如myobject.method(arg1, arg2) 時,Python 將會自動將其轉換成 MyClass.method(myobject, arg1, arg2) ——這就是 self 的全部特殊之處所在。這同時意味着,如果你有一個沒有參數的方法,你依舊必須擁有一個參數—— self

class Person:
    pass

p = Person()
print(p)
<__main__.Person object at 0x000001C5D62DEE10>
class Person:
    def say_hi(self): # 這個參數 self 是必須有的
        print('Hello,how are you?')

p = Person()
p.say_hi()
Hello,how are you?

init_ 方法

__init__方法會在類的對象被實例化(Instantiated)時立即運行。這一方法可以對任何你想進行操作的目標對象進行初始化(Initialization)操作。這裏你要注意在 init 前後加上的雙下劃線。

class Person:
    def __init__(self, name):
        self.name = name

    def say_hi(self):
        print('Hello, my name is', self.name)

p = Person('Hinton')        
p.say_hi()

Person('Jack').say_hi()
Hello, my name is Hinton
Hello, my name is Jack

類變量與對象變量

我們已經討論過了類與對象的功能部分(即方法),現在讓我們來學習它們的數據部分。數據部分——也就是字段——只不過是綁定(Bound)到類與對象的命名空間(Namespace)的普通變量。這就代表着這些名稱僅在這些類與對象所存在的上下文中有效。這就是它們被稱作“命名空間”的原因。

字段(Field)有兩種類型——類變量與對象變量,它們根據究竟是類還是對象擁有這些變量來進行分類。
類變量(Class Variable)是共享的(Shared)——它們可以被屬於該類的所有實例訪問。

該類變量只擁有一個副本,當任何一個對象對類變量作出改變時,發生的變動將在其它所有實例中都會得到體現。

對象變量(Object variable)由類的每一個獨立的對象或實例所擁有。在這種情況下,每個對象都擁有屬於它自己的字段的副本,也就是說,它們不會被共享,也不會以任何方式與其它不同實例中的相同名稱的字段產生關聯。

# coding=UTF-8

class Robot:
    """表示有一個帶有名字的機器人。"""

    # 一個類變量,用來計數機器人的數量
    population = 0

    def __init__(self, name):
        """初始化數據"""
        # 對象變量          
        self.name = name
        print('(Initializing {})'.format(self.name))

        # 當有人被創建時,機器人
        # 將會增加人口數量
        Robot.population +=1

    def die(self):
        """我掛了。"""
        print('{} is being destroyed!'.format(self.name))

        Robot.population -= 1

        if Robot.population == 0:
            print('{} was the last one.'.format(self.name))
        else:
            print("There are still {:d} robots working.".format(Robot.population))


    def say_hi(self):
        """來自機器人的誠摯問候

        沒問題,你做得到。"""
        print("Greetings, my masters call me {}".format(self.name))


    @classmethod
    def how_many(cls):
        """打印出當前的人口數量"""
        print("we have {:d} robots.".format(cls.population))


droid1 = Robot("R2-D2")        
droid1.say_hi()
Robot.how_many()

droid2 = Robot('C-3PO')
droid2.say_hi()
Robot.how_many()


print("\nRobots can do some work here.\n")

print("Robots have finished their work. So let's destroy them.")

droid1.die()
droid2.die()

Robot.how_many()
(Initializing R2-D2)
Greetings, my masters call me R2-D2
we have 1 robots.
(Initializing C-3PO)
Greetings, my masters call me C-3PO
we have 2 robots.

Robots can do some work here.

Robots have finished their work. So let's destroy them.
R2-D2 is being destroyed!
There are still 1 robots working.
C-3PO is being destroyed!
C-3PO was the last one.
we have 0 robots.
print(Robot.__doc__)
Robot.say_hi.__doc__
表示有一個帶有名字的機器人。





'來自機器人的誠摯問候\n\n        沒問題,你做得到。'

繼承

面向對象編程的一大優點是對代碼的重用(Reuse),重用的一種實現方法就是通過繼承(Inheritance)機制。繼承最好是想象成在類之間實現類型與子類型(Type and Subtype)關係的工具。

SchoolMember 類會被稱作基類(Base Class) 或是超類(Superclass)。 Teacher 和 Student 類會被稱作派生類(Derived Classes) 或是子類(Subclass)。

# coding=UTF-8

class SchoolMember:
    '''代表任何學校裏的成員。'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
        print('(Initialized SchoolMember: %s)'%self.name)

    def tell(self):
        '''告訴我有關我的細節。'''
        print('Name:"{}" Age:"{}"'.format(self.name, self.age),end=" ")

class Teacher(SchoolMember):
    '''代表一位老師。'''
    def __init__(self, name, age, salary):
        SchoolMember.__init__(self, name, age)
        self.salary = salary
        print('(Initialized Teacher: {})'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Salary: "{:d}"'.format(self.salary))


class Student(SchoolMember):
    '''代表一位學生。'''
    def __init__(self, name, age, marks):
        SchoolMember.__init__(self, name, age)
        self.marks = marks
        print('(Initialized Student: {})'.format(self.name))

    def tell(self):
        SchoolMember.tell(self)
        print('Marks: "{:d}"'.format(self.marks))


t = Teacher('Mrs. Shrividya', 40, 30000)
s = Student('Swaroop', 25, 75)


print()

members = [t, s]

for member in members:
    member.tell()




(Initialized SchoolMember: Mrs. Shrividya)
(Initialized Teacher: Mrs. Shrividya)
(Initialized SchoolMember: Swaroop)
(Initialized Student: Swaroop)

Name:"Mrs. Shrividya" Age:"40" Salary: "30000"
Name:"Swaroop" Age:"25" Marks: "75"

輸入與輸出

有些時候你的程序會與用戶產生交互。舉個例子,你會希望獲取用戶的輸入內容,並向用戶打印出一些返回的結果。我們可以分別通過 input() 函數與 print 函數來實現這一需求。
對於輸入,我們還可以使用 str (String,字符串)類的各種方法。例如,你可以使用rjust 方法來獲得一個右對齊到指定寬度的字符串。你可以查看 help(str) 來了解更多細節。
另一個常見的輸入輸出類型是處理文件。創建、讀取與寫入文件對於很多程序來說是必不可少的功能,而我們將在本章探討這一方面。

'''
用戶輸入內容

palindrome 迴文
'''

def reverse(text):
    # 切片功能翻轉文本 ,-1 從倒數第一個開始倒着輸出
    return text[::-1] 

def is_palindrome(text):
    return text == reverse(text)


something  = input('Enter text:')

if is_palindrome(something):
    print('Yes, it is a palindrome')
else:
    print('No, it is not a palindrome')

Enter text:txtxtxt
Yes, it is a palindrome

我們已經瞭解了我們可以通過使用 seq[a:b] 來從位置 a 開始到位置 b 結束來對序列進行切片 。我們同樣可以提供第三個參數來確定切片的步長(Step)。默認的步長爲 1 ,它會返回一份連續的文本。如果給定一個負數步長,如 -1 ,
將返回翻轉過的文本。

作業練習

要想檢查文本是否屬於迴文需要忽略其中的標點、空格與大小寫。例如,“Rise to vote, sir.”是一段迴文文本,但是我們現有的程序不會這麼認爲。你可以改進上面的程序以使它能夠識別這段迴文嗎?

'''
作業練習

檢查文本是否屬於迴文,忽略其中的標點、空格與大小寫

提示:

使用一個元組(你可以在這裏找到一份列出所有標點符號的列表)來保存所有需要禁
用的字符,然後使用成員資格測試來確定一個字符是否應該被移除,即 forbidden = ( ! ,
? , . , ...)。
'''

文件

你可以通過創建一個屬於 file 類的對象並適當使用它的 read 、 readline 、 write 方法來打開或使用文件,並對它們進行讀取或寫入。讀取或寫入文件的能力取決於你指定以何種方式打開文件。最後,當你完成了文件,你可以調用 close 方法來告訴 Python 我們已經完成了對該文件的使用。

poem = '''\
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
'''

# 打開文件以編輯('w'riting)
f = open('test.txt', 'w')
# 向文件中編寫文本
f.write(poem)
# 關閉文件
f.close()


# 如果沒有特別指定,
# 將假定啓用默認的閱讀('r'ead)模式
f = open('test.txt', 'r')
while True:
    line = f.readline()
    # 零長度指示 EOF
    if len(line) == 0:
        break
    # 每行(`line`)的末尾
    # 都已經有了換行符
    #因爲它是從一個文件中進行讀取的
    print(line, end='')
# 關閉文件
f.close()
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!

首先,我們使用內置的 open 函數並指定文件名以及我們所希望使用的打開模式來打開一個文件。打開模式可以是閱讀模式( ‘r’ ),寫入模式( ‘w’ )和追加模式( ‘a’ )。我們還可以選擇是通過文本模式( ‘t’ )還是二進制模式( ‘b’ )來讀取、寫入或追加文本。實際上還有其它更多的模式可用, help(open) 會給你有關它們的更多細節。在默認情況下, open() 會將文件視作文本(text)文件,並以閱讀(read)模式打開它。

Pickle

Python 提供了一個叫作 Pickle 的標準模塊,通過它你可以將任何純 Python 對象存儲到一個文件中,並在稍後將其取回。這叫作持久地(Persistently)存儲對象。

import pickle


# 我們存儲相關對象的文件的名稱
shoplistfile = 'shoplist.data'
# 需要購買的物品清單
shoplist = ['apple', 'mango', 'carrot']

# 準備寫入文件
f = open(shoplistfile, 'wb')
# 轉儲對象至文件
pickle.dump(shoplist, f)
f.close()

# 清除 shoplist 變量
del shoplist

# 重新打開存儲文件
f = open(shoplistfile, 'rb')

# 從文件中載入對象
storedlist = pickle.load(f)
print(storedlist)
['apple', 'mango', 'carrot']

要想將一個對象存儲到一個文件中,我們首先需要通過 open 以寫入(write)二進制(binary)模式打開文件,然後調用 pickle 模塊的 dump 函數。這一過程被稱作封裝(Pickling)。

接着,我們通過 pickle 模塊的 load 函數接收返回的對象。這個過程被稱作拆封(Unpickling)。

Unicode

截止到現在,當我們編寫或使用字符串、讀取或寫入某一文件時,我們用到的只是簡單的英語字符。

當我們閱讀或寫入某一文件或當我們希望與互聯網上的其它計算機通信時,我們需要將我們的 Unicode 字符串轉換至一個能夠被髮送和接收的格式,這個格式叫作“UTF-8”。我們可以在這一格式下進行讀取與寫入,只需使用一個簡單的關鍵字參數到我們的標準 open 函數中:

print(u"Hello, world")
type(u'Hello, world')
Hello, world





str
# encoding=UTF-8
import io

f = io.open('test.txt', 'at', encoding='utf-8')
f.write(u'Imagine non-English language here')
f.close()

text = io.open('test.txt', encoding='utf-8').read()
print(text)
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
Imagine non-English language hereImagine non-English language here

每當我們諸如上面那番使用 Unicode 字面量編寫一款程序時,我們必須確保 Python 程序已經被告知我們使用的是 UTF-8,因此我們必須將 # encoding=utf-8 這一註釋放置在我們程序的頂端。

異常

Print("Hello World")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
NameError: name 'Print' is not defined

錯誤

NameError 錯誤被拋出,同時 Python 還會打印出檢測到的錯誤發生的位
置。這就是一個錯誤錯誤處理器(Error Handler)

s = input('Enter something --> ')
Enter something --> Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
EOFError

此處 Python 指出了一個稱作 EOFError 的錯誤,代表着它發現了一個文件結尾(End of File)符號(由 ctrl-d 實現)在不該出現的時候出現了。

處理異常

我們可以通過使用 try..except 來處理異常狀況。一般來說我們會把通常的語句放在 try 代碼塊中,將我們的錯誤處理器代碼放置在 except 代碼塊中。

try:
    text = input('Enter something --> ')
except EOFError:
    print('Why did you do an EOF on me?')
except KeyboardInterrupt:
    print('You cancelled the operation.')
else:
    print('You entered {}'.format(text))
Enter something --> what
You entered what
# Press ctrl + d
$ python exceptions_handle.py
Enter something --> Why did you do an EOF on me?

# Press ctrl + c
$ python exceptions_handle.py
Enter something --> ^CYou cancelled the operation.

$ python exceptions_handle.py
Enter something --> No exceptions
You entered No exceptions

拋出異常

你可以通過 raise 語句來引發一次異常,具體方法是提供錯誤名或異常名以及要拋出(Thrown)異常的對象。
你能夠引發的錯誤或異常必須是直接或間接從屬於 Exception (異常) 類的派生類。

# encoding=utf-8

class ShortInputException(Exception):
    '''一個由用戶定義的異常類'''
    def __init__(self, length, atleast):
        Exception.__init__(self)
        self.length = length
        self.atleast = atleast

try:
    text = input('Enter something -->')
    if len(text) < 3:
        raise ShortInputException(len(text), 3)
    # 其他工作能在此處繼續正常運行
except EOFError:
    print('Why did you do an EOF on me?')
except  ShortInputException  as ex:
    print(('ShortInputException: The input was ' + '{0} long, expected at least {1}').format(ex.length, ex.atleast))
else:
    print('No exception was raised.')
Enter something -->w
ShortInputException: The input was 1 long, expected at least 3

Try … Finally

假設你正在你的讀取中讀取一份文件。你應該如何確保文件對象被正確關閉,無論是否會發生異常?這可以通過 finally 塊來完成。

import sys
import time

f = None
try:
    f = open('test.txt')
    # 我們常用的文件閱讀風格
    while True:
        line  = f.readline()
        if len(line) == 0:
            break
        print(line, end='')
        sys.stdout.flush()
        print('Press ctrl +c now')
        # 爲了確保它能運行一段時間
        time.sleep(2)
except IOError:        
    print("Could not find file poem.txt")
except KeyboardInterrupt:
    print("!! You cancelled the reading from the file.")
finally:
    if f:
        f.close()
    print("(Cleaning up: Closed the file)")  
Programming is fun
Press ctrl +c now
When the work is done
Press ctrl +c now
if you wanna make your work also fun:
Press ctrl +c now
    use Python!
Press ctrl +c now
Imagine non-English language hereImagine non-English language herePress ctrl +c now
(Cleaning up: Closed the file)

with 語句

在 try 塊中獲取資源,然後在 finally 塊中釋放資源是一種常見的模式。因此,還有一個with 語句使得這一過程可以以一種乾淨的姿態得以完成。

with open('test.txt') as f:
    for line in f:
        print(line, end='')
Programming is fun
When the work is done
if you wanna make your work also fun:
    use Python!
Imagine non-English language hereImagine non-English language here

程序輸出的內容應與上一個案例所呈現的相同。本例的不同之處在於我們使用的是 open 函數與 with 語句——我們將關閉文件的操作交由 with open 來自動完成。

在幕後發生的事情是有一項 with 語句所使用的協議(Protocol)。它會獲取由 open 語句返回的對象,在本案例中就是“thefile”。

它總會在代碼塊開始之前調用 thefile.enter 函數,並且總會在代碼塊執行完畢之後調用 thefile.exit

因此,我們在 finally 代碼塊中編寫的代碼應該格外留心 exit 方法的自動操作。這能夠幫助我們避免重複顯式使用 try..finally 語句。

標準庫

Python 標準庫(Python Standrad Library)中包含了大量有用的模塊,同時也是每個標準的Python 安裝包中的一部分。熟悉 Python 標準庫十分重要,因爲只要你熟知這些庫可以做到什麼事,許多問題都能夠輕易解決。

我們將探索這個庫中的一些常用模塊。你能在你的 Python 安裝包中附帶的文檔中的“庫概覽(Library Reference)” 部分中查找到所有模塊的全部細節。

sys 模塊

sys 模塊包括了一些針對特定系統的功能。我們已經瞭解過 sys.argv 列表中包括了命令行參數。

import sys
sys.version_info
sys.version_info(major=3, minor=6, micro=2, releaselevel='final', serial=0)
sys.version_info.major == 3
True

sys 模塊包含一個 version_info 元組,它提供給我們版本信息。第一個條目是主版本信息。我們可以調出這些信息並使用它。

日誌模塊

如果你想將一些調試(Debugging)信息或一些重要的信息儲存在某個地方,以便你可以檢查你的程序是否如你所期望那般運行,應該怎麼做?你應該如何將這些信息“儲存在某個地方”?這可以通過 logging 模塊來實現。

import os
import platform
import logging

if platform.platform().startswith('Windows'):
    logging_file = os.path.join(os.getenv('HOMEDRIVE'),
                                os.getenv('HOMEPATH'),'test.log')
else:
    logging_file = os.path.join(os.getenv('HOME'),'test.log')

print("Logging to ",logging_file)    

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s : %(levelname)s : %(message)s',
    filename=logging_file,
    filemode='w')

logging.debug('"Start of the program')
logging.info('Doing something')
logging.warning('Dying now')
Logging to  C:\Users\qhtf\test.log

format=’%(asctime)s : %(levelname)s : %(message)s’

2018-03-15 10:03:40,392 : DEBUG : “Start of the program
2018-03-15 10:03:40,393 : INFO : Doing something
2018-03-15 10:03:40,393 : WARNING : Dying now

我們使用了三款標準庫中的模塊—— os 模塊用以和操作系統交互, platform 模塊用以獲取平臺——操作系統——的信息, logging 模塊用來記錄(Log)信息。

每週模塊系列

標準庫中還有許多模塊值得探索,例如一些用以調試(Debugging)的模塊, 處理命令行選項的模塊,正則表達式(Regular Expressions)模塊 等等等等。
進一步探索標準庫的最好方法是閱讀由 Doug Hellmann 撰寫的優秀的 Python Module of the Week 系列(你還可以閱讀它的實體書或是閱讀 Python 官方文檔)。

總結

我們已經探索了 Python 標準庫中提供的諸多的模塊的一些功能。在此強烈建議你瀏覽Python 標準庫文檔來了解所有可以使用的模塊。
接下來,我們將介紹 Python 的其它各個方面,讓我們的 Python 之旅更加完整。

傳遞元組

你可曾希望從一個函數中返回兩個不同的值?你能做到的。只需要使用一個元組。

'''
在學習 DL 課程的過程中,jupyter notebook 形式的作業裏,
吳恩達老師的團隊,在裏面每個大體的函數框架中,
對於在前向傳播中,得到的一些值,cache 存儲起來,以便反向傳播的時候使用
經常存儲的數據結構使用的就是 DIC 字典,Tuple 元組 ,list

'''

def get_error_details():
    return (2,'details')

errnum, errstr = get_error_details()
print(errnum)
print(errstr)
2
details

特殊方法

諸如 __init____del__ 等一些方法對於類來說有特殊意義。

特殊方法用來模擬內置類型的某些行爲。舉個例子,如果你希望爲你的類使用 x[key] 索引操作(就像你在列表與元組中使用的那樣),那麼你所需要做的只不過是實現__getitem__() 方法,然後你的工作就完成了。如果你試圖理解它,就想想 Python 就是對list 類這樣做的!

  • __init__(self, ...)
    • 這一方法在新創建的對象被返回準備使用時被調用。
  • __del__(self)
    • 這一方法在對象被刪除之前調用(它的使用時機不可預測,所以避免使用它)
  • __str__(self)
    • 當我們使用 print 函數時,或 str() 被使用時就會被調用。
  • __lt__(self, other)
    • 當小於運算符(<)被使用時被調用。類似地,使用其它所有運算符(+、> 等等)時都會有特殊方法被調用。
  • __getitem__(self, key)
    • 使用 x[key] 索引操作時會被調用。
  • __len__(self)
    • 當針對序列對象使用內置 len() 函數時會被調用

Lambda

lambda 語句可以創建一個新的函數對象。從本質上說, lambda 需要一個參數,後跟一個表達式作爲函數體,這一表達式執行的值將作爲這個新函數的返回值。

points = [{'x':2, 'y':3},
          {'x':4, 'y':1}]
# 按照每個字典中 y 值的大小來排序
points.sort(key=lambda i: i['y'])
print(points)
[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]

要注意到一個 list 的 sort 方法可以獲得一個 key 參數,用以決定列表的排序方式(通常我們只知道升序與降序)。在我們的案例中,我們希望進行一次自定義排序,爲此我們需要編寫一個函數,但是又不是爲函數編寫一個獨立的 def 塊,只在這一個地方使用,因此我們使用 Lambda 表達式來創建一個新函數。

列表推導

列表推導(List Comprehension)用於從一份現有的列表中得到一份新列表。想象一下,現在你已經有了一份數字列表,你想得到一個相應的列表,其中的數字在大於 2 的情況下將乘以2。列表推導就是這類情況的理想選擇。

listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]
print(listtwo)
[6, 8]

在函數中接收元組與字典

有一種特殊方法,即分別使用 * 或 ** 作爲元組或字典的前綴,來使它們作爲一個參數爲函數所接收。當函數需要一個可變數量的實參時,這將頗爲有用。

def powersum(power, *args):
    '''Return the sum of each argument raised to the specified power.'''
    total = 0
    for i in args:
        total += pow(i, power)
    return total

print(powersum(2,3,4))    
print(powersum(3,1,3,2,2))
25
44

assert 語句

assert 語句用以斷言(Assert)某事是真的。例如說你非常確定你正在使用的列表中至少包含一個元素,並想確認這一點,如果其不是真的,就拋出一個錯誤, assert 語句就是這種情況下的理想選擇。當語句斷言失敗時,將會拋出 AssertionError 。

mylist = ['wow', 'opps']

assert len(mylist) >= 1

mylist.pop()

assert len(mylist) >= 1

mylist.pop()

assert len(mylist) >= 1
---------------------------------------------------------------------------

AssertionError                            Traceback (most recent call last)

<ipython-input-33-f567f021077a> in <module>()
      9 mylist.pop()
     10 
---> 11 assert len(mylist) >= 1


AssertionError: 

裝飾器(Decorators)

裝飾器(Decorators)是應用包裝函數的快捷方式。這有助於將某一功能與一些代碼一遍又一遍地“包裝”。舉個例子,我爲自己創建了一個 retry 裝飾器,這樣我可以將其運用到任何函數之中,如果在一次運行中拋出了任何錯誤,它就會嘗試重新運行,直到最大次數 5 次,並且每次運行期間都會有一定的延遲。這對於你在對一臺遠程計算機進行網絡調用的情況十分有用:

from time import sleep
from functools import wraps
import logging
logging.basicConfig()
log = logging.getLogger('retry')


def retry(f):
    @wraps(f)
    def wrapped_f(*args, **kwargs):
        MAX_ATTEMPTS = 5
        for attempt in range(1, MAX_ATTEMPTS + 1):
            try:
                return f(*args, **kwargs)
            except:
                log.exception("Attempt %s/%s failed : %s",
                             attempt,
                             MAX_ATTEMPTS,
                             (args, kwargs))
                sleep(10 * attempt)

        log.critical("All %s attempt failed : %s",
                    MAX_ATTEMPTS,
                    (args, kwargs))
    return wrapped_f

counter = 0

@retry
def save_to_database(arg):
    print("Write to a database or make a network call or etc.")
    print("This will be automatically retried if exception is thrown.")

    global counter
    counter += 1

    # 這將在第一次調用時拋出異常
    # 在第二次運行時將正常工作(也就是重試)
    if counter < 2:
        raise ValueError(arg)

if __name__ == '__main__'        :
    save_to_database('Some bad value')

Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.

未來還有很長的路要走。

發佈了186 篇原創文章 · 獲贊 44 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章