Python入門手記(2017新春賀歲版) 頂 原

##免責聲明:本文的目的是進行計算機編程和一些注意事項的純技術探討,對於讀者應用本文所述事項進行非法行爲探索造成的後果一概不負責任。

本文代碼引用和免責聲明:

/**************************************************************************              *
 *                                                                        *
 * DISCLAIMER: The author of this blog have used their     *
 * best efforts in preparing the work. These efforts include the          *
 * development, research, and testing of the theories and programs        *
 * to determine their effectiveness. The author makes       *
 * no warranty of any kind, expressed or implied, with regard to these    *
 * programs or to the documentation contained in this work. The author *
 * shall not be liable in any event for incidental or       *
 * consequential damages in connection with, or arising out of, the       *
 * furnishing, performance, or use of these programs.                     *
 **************************************************************************/

一、Python基本運算

Python的基本運算基本可以參考其他高級語言的基本運算,本節僅交代其中的一些特例。

  • 一般來說,編程語言中兩個整數相除會得到向下取整的整數。但是Python中可以通過如下功能實現兩個整數相除獲得小數的計算。
from __future__ import division
1 / 2

運行結果爲0.5

  • Python // (求整除)運算,若相除兩數爲浮點數,則結果爲浮點型整數。若求餘數符號(%)的運算對象爲浮點數,則結果爲(小余被除數)的浮點數。

  • Python的乘方運算可與模運算在同一個函數中進行,比如 pow(2, 4, 3)即2的4次方模3的值,結果爲16%3,等於1。運用此操作效率比%符號更高。

  • Python大整數結尾可加上字母(推薦大寫,因爲小寫l容易與數字1混淆)L表示長整數

  • Python支持複數運算,複數結尾需要加上英文字母j(或其大寫J)

  • 此外,Python的運算符是有優先級的。如

 force = g * m1 * m2 / r * r 

這個萬有引力公式問題就是沒有遵循運算的優先級(參考自普林斯頓大學程序設計導論練習題)。正確的寫法應該是

 force = g * m1 * m2 / (r * r)

二、Python語句:分支、循環以及若干表達式:

A、表達式:

**1. print:**在顯示屏(或接入計算機的可顯示設備)輸出指定變量,多個變量之間用逗號隔開。輸出時,變量之間會自動被添加一個空格。在python3中,print被作爲一個函數了。在print中使用加號就不會導致輸出中含有空格,在print語句該行結尾使用逗號就不會導致輸出被換行。注意:print不會自動將隔開的數字轉化爲元組。print的對象均爲字符串,還可以用“+”號相連。作用有二:一、忽略逗號產生的空格影響。二、作爲一個整體的字符串輸出(參考資料1第172頁)

**2. import:**Python中的import語句形式多樣,可能會出現如下幾種形式:

import somemodule
from somemodule import somefunction
from somemodule import *

其中,當導入模塊中含有需要調用的重名函數時,有以下幾種可能的解決方案:

#假設模塊1和模塊2都含有open方法:
module1.open(...)
module2.open(...)

#賦予被導入模塊新名稱:
import math as foobar 
from math import sqrt as foobar

3.賦值語句: Python賦值語句的本質事實上是創造了一個數據字典。這個數據字典可以通過vars()方法獲得。舉例如下:

>>> x = 1
>>> scope = vars()
>>> scope['x']
1
>>> scope['x'] += 2
>>> x
3

3.1 序列打包:通過序列打包,可以實現在一個語句中同時對多個數據對象進行操作。詳見示例。 3.2 連續賦值:將同一個值連續賦給不同的變量。注意:分別賦值和連續賦值可能會導致一些區別,在本文後面講述is和雙等號區別的時候會進一步提到。例如:

#假設somefunction已預先定義:
>>> x = y = somefunction()
#與
>>> y = somefunction()
>>> x = y
# 相同,但是可能與以下語句不同
>>>x = somefunction()
>>>y = somefunction()

也就是說,Python中的等號只是拷貝了存儲地址,沒有複製內容。需要複製內容的情況在此處需要用兩個語句分別執行,在python列表中需要使用a = b[ : ]的操作方式

3.3 增強型賦值(augmented assignments):像其他高級語言一樣,可使用+=,-=,*=,/=以及%=等進行賦值。對於能操作字符串的符號來說也適用。但是python不含++和--,需要寫成+=1和-=1。

4. Python語句塊:

Blocks are used as a means of grouping statements through indentation. (Ref 1, p.g.111)

語句塊是指符合特定條件時(例如判斷爲True或循環)被一起執行的語句的集合。一般的高級語言對於語句塊的書寫格式沒有講究,只要以分號隔開不同語句即可,空格和換行忽略不計。

然而在Python中,空格符號不一定是忽略不計的。同一個級別的語句塊必須被縮排(indent),在本級以及下一集語句塊之間要換行並且打一個tab符號(一般Python IDE會自動在敲入換行時爲程序員設置好一個tab)。

5. 邏輯謂詞:

Python中,以下所有的邏輯謂詞意思同False: False None 0 "" () [] {} 其餘的謂詞,在用戶不特殊說明的情形下都是True。Python中的邏輯謂詞可以參與基本運算,例如:

>>> True + False + 42
43

Python中,可以用類型轉化bool()將其他形式的表達式轉化爲邏輯變量True或者False。

此外,python中邏輯代數與、或非分別爲關鍵字and, or和!。

**6.變量比較:**Python支持與其他高級語言類似的變量比較,如==,>=, <=等,此處列舉幾個特殊的比較符。 x is y:區別於x == y,作用是比較x和y是否是同一個對象(種類)。x is not y爲類似比較,同理可得。需要注意的是,對於對象的比較推薦使用cmp函數,這個在後文列表與元組中會提到。

7. pass語句:pass語句表示什麼都不做。Python中,由於不接受空的控制語句(如空elif),因此如果希望elif的部分什麼都不做,就可以插入pass語句(例子見參考資料1 107頁)。與pass語句相同的做法是在該處插入一個多行註釋

表達式應用舉例:

  • 序列打包示例:
>>> x, y, z = 1, 2, 3
>>> print x, y, z
1 2 3

"""Python標準的交換變量寫法,
如果還在使用中間變量,
那麼你就OUT了。"""

>>> x, y = y, x      
>>> print x, y, z
2 1 3
>>> values = 1, 2, 3
>>> values
(1, 2, 3)
>>> x, y, z = values
>>> x
1

""" 從數據字典隨機輸出一個元素,並賦值給一個簡直對
之後解包鍵值對,取得相應變量。
"""
>>> scoundrel = {'name': 'Robin', 'girlfriend': 'Marion'}
>>> key, value = scoundrel.popitem()
>>> key
'girlfriend'
>>> value
'Marion'

Python3還支持不對等數量打包和解包,需要打包的元素個數必須多於變量數目,變量序列結尾多於的變量全部會被打包給最後一個變量。

>>> a, b, rest* = [1, 2, 3, 4]
>>> rest
[3, 4]
  • 變量比較示例:
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x == y
True  #因爲x和y值相同
>>> x is y
False #因爲x和y是不同的兩個列表

此外,python還提供比較數據結構的功能,例如:

>>> [1, 2] < [2, 1]
True
>>> [2, [1, 4]] < [2, [1, 5]]
True
  • 邏輯謂詞使用示例:
"""提示並獲得用戶輸入,
若用戶沒有輸入,則name變量
被賦值給'<unknown>' """
name = raw_input('Please enter your name: ') or '<unknown>'

B、分支語句:

Python如其他高級語言一樣支持if型分支語句,格式爲if-elif(else if)-else,舉例如下:

num = input('Enter a number: ')
if num > 0:
    print 'The number is positive'
elif num < 0:
    print 'The number is negative'
else:
    print 'The number is zero'

通過不同級別的縮進,Python可支持嵌套分支。 此外,python還提供一種輕量級的分支語句:a if b else c。當b爲True時,返回a,否則返回c。作用有點像其他高級語言的一個三元操作符?: 。

python用assert關鍵字提供斷言機制,除了不用導入模塊之外,使用方式類似C++的斷言機制。詳細請見博客:《淺談C++編程中可能會用到的預處理指令》https://my.oschina.net/SamYjy/blog/827660 的調試類預處理指令部分。

C、循環語句:

Python的循環語句大體包括while型、for型以及列表推導式,其中,python的for語句還有一些特殊用法。與其他高級語言類似。由於列表推導式的重要性,因此專門單獨列出一個小節整理歸納。

Python支持while語句,舉例如下:

name = ""
#當用戶輸入爲空時一直提示輸入:
while not name.strip():  
    name = raw_input('Please enter your name: ')    
print 'Hello, %s!' % name

Python的for語句支持對於所有迭代型變量的遍歷,舉例如下:

words = ['this', 'is', 'an', 'ex', 'parrot']
for word in words:
    print word

xrange([start], stop):返回一個包含從start到但不包括stop的迭代。start參數不選時,默認從0開始。xrange的效率一般來說好於range。 range([start], stop):返回一個包含從start到但不包括stop的數值列表。start參數不選時,默認從0開始,這是py2函數。

Python的range函數和分片操作的數值範圍都包含起始值,但是不包括終止值。

循環語句可以對字典元素進行迭代。對字典元素進行迭代時,既可通過d[k]獲得對應真值,也可將鍵值對打包,其中,打包處理的值必須是轉化爲序列後的字典元素(使用d.items()函數)。示例如下:

>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> for key in d:
	print key, ' corresponds to ', d[key]

y  corresponds to  2
x  corresponds to  1
z  corresponds to  3

>>> for k, v in d.items():
	print k, ' corresponds to ', v

y  corresponds to  2
x  corresponds to  1
z  corresponds to  3

python迭代完成後,迭代變量儲存最後一步的值並可以被繼續使用:

>>> print k
z

Python中的for語句可以與打包函數zip(iterA, iterB)一起使用,支持對於打包序列按長度較短的迭代進行解包,並且返回值爲元組序列。舉例如下:

>>> names = ['anne', 'beth', 'george', 'damon']
>>> ages = [12, 45, 32, 102]
>>> for (name, age) in zip(names, ages):
	print(name + " is " + str(age) + " years old.")

anne is 12 years old.
beth is 45 years old.
george is 32 years old.
damon is 102 years old.

>>> zip(xrange(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

Python中,有時需要在遍歷對象的同時記錄特定對象的下標,此時,enumerate(seqs) 就是一個可使用的函數:

>>> strings = ["Hello", "John", "xxx"]
>>> for index, string in enumerate(strings):
	if 'xxx' in string:
		strings[index] = '[censored]'
>>> strings
['Hello', 'John', '[censored]']
>>> index
2     

python也支持在循環語句中使用break和continue關鍵字(詳見參考資料1 102-105頁)。其中,可以通過在循環體後的同一個級別加一個else從句,設置若循環體中的break關鍵字未被調用的情形下(也即循環體被完整執行的情況下)需要執行的代碼,示例如下:

import math
broke_out = False

#此處81是不會被打印的:
for x in xrange(99, 81, -1):
    root = math.sqrt(x)
    if root == int(root):
        print x
        break
else:
    print "Did not call break!"
    x += 5

print "x is:", x

執行效果:
Did not call break!
x is: 87

D、列表推導式:(參考資料1 105-106頁)

Python列表推導式的作用是從一個列表生成另一個列表,其本質類似數據庫或集合論中的集合連接運算(set comprehension)。幾個使用示例如下:

>>> [x * x for x in xrange(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [x*x for x in range(10) if x % 3 == 0]
[0, 9, 36, 81]

列表推導式還可以對元組列表進行操作,示例如下:

>>> a = [(x, y) for x in xrange(3) for y in xrange(3)]
>>> print a
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
>>> (b, c) = a[6]
>>> b
2
>>> c
0
>>> 

列表推導式能夠操作兩個相關集合,舉例如下:

>>> girls = ['Alice', 'Bernice', 'Clarice']
>>> boys = ['Chris', 'Arnold', 'Bob']
>>> [b + ' loves ' + g for b in boys for g in girls if b[0] == g[0]]
['Chris loves Clarice', 'Arnold loves Alice', 'Bob loves Bernice']

然而在此例中,由於要檢查girls和boys中的每個可能的元素,因此此算法的時間複雜度並不好,爲O(n ^ 2)。此例的線性解可以通過將列表推導式與數據字典的使用相結合。以下作用完全相同的兩個版本代碼通過額外開銷數據字典的方式,使得該算法時間複雜度降爲O(N): 版本一:

girls = ['Alice', 'Bernice', 'Clarice']
boys = ['Chris', 'Arnold', 'Bob']
letterGirls = {}
for girl in girls:
    letterGirls.setdefault(girl[0], girl)

print [b + ' loves '+ letterGirls[b[0]] for b in boys]

這個版本的代碼通過直接向字典中添加girls中的開頭字母以及對應的名字,然而在列表推導式中就略顯複雜了。(注意此處列表推導式中的g是女生名字開頭第一個字母,b是男孩名字)

版本二:

girls = ['Alice', 'Bernice', 'Clarice']
boys = ['Chris', 'Arnold', 'Bob']
letterGirls = {}
for girl in girls:
    tempL = [girl]
    letterGirls.setdefault(girl[0], tempL)
    """This is identical to:
    letterGirls.setdefault(girl[0], []).append(girl),
    however, might be confusing."""

print [b + ' loves '+ g for b in boys for g in letterGirls[b[0]]]

這個版本的代碼列表推導式簡單了,然而中間需要將girl字符轉化爲列表以便於獲取。

F、對於字符串進行估值或執行的函數exec和eval:

_代碼潛在安全漏洞聲明: Caution In this section, you learn to execute Python code stored in a string. This is a potential security hole of great dimensions. If you execute a string where parts of the contents have been supplied by a user, you have little or no control over what code you are executing. This is especially dangerous in network applications, such as Common Gateway Interface (CGI) scripts. _

exec語句通常同來執行相應本質爲python代碼的表達式,在python 2中爲表達式,python 3中則變爲一個函數(方法)。

>>> exec "print 'Hello, world!' "
Hello, world!
>>> from math import sqrt
>>> exec "a = sqrt(4)"
>>> a
2.0

因爲exec語句中可能存在潛在干擾正常程序的代碼(參考資料1 109頁),因此將exec語句放在數據字典中執行是一種可能的規避風險的方式。

>>> from math import sqrt
>>> scope = {}
>>> exec 'sqrt = 1' in scope
>>> sqrt(4)
2.0
>>> scope['sqrt']
1

此處,由於將sqrt表示的語句放在了數據字典scope中執行,因此sqrt對於庫函數math.sqrt的正常使用沒有造成干擾。

eval表達式則用於對字符串進行估值,與exec不同,eval會返回經過計算的python表達式。

>>> eval(raw_input("Enter an arithmetic expression: "))
Enter an arithmetic expression: 6 + 18 * 2
42

此外,eval和exec可相互交叉作用,用於對命名空間(namespace)中的語句進行估值。

>>> scope = {}
>>> exec 'x = 2' in scope
>>> exec 'y = 3' in scope
>>> eval('x * y', scope)
6

G、將數值轉化爲單個字符的chr函數和將數字轉化爲字符的ord函數:

>>> a = 15
>>> chr(a)
'\x0f'
>>> ord('A')
65

三、Python字符串:

Python中的單行字符串可用''符號或""符號。此外,由於python是講究格式對齊的編程語言,多行字符串需要被包括在""" """符號之間,此符號亦可用來註釋多行代碼。(python中註釋單行代碼的是#符號。)

str()和repr()都是將Python表達式轉爲Python字符的方式。由於在Python 3中,使用雙斜點的方式已經被廢除,因此repr是推薦的方式。

Python使用 \ 既可用作顯示字符串中的標點符號,也可以表示代碼換行。string模塊有一些常量,可以通過import string進行使用。詳情見參考資料1第60頁。

Python生字符串前必須有小寫字母r。代碼舉例:

print r'Let\'s go!'
print r'C:\Program Files\foo\bar' '\\'

Python中,格式化字符串(format string)的方式與其他編程語言大致相同,使用百分符號,例如:

>>> format = "Hello, %s. %s enough for ya?"
>>> values = ('world', 'Hot')
>>> print format % values
Hello, world. Hot enough for ya?
>>> format = "Pi with three decimals: %.3f"
>>> from math import pi
>>> print format % pi
Pi with three decimals: 3.142

>>> '%+10.2f' % pi    # "-"號有特殊意義,表示左對齊,此處效果就不是這樣了。  
'     +3.14'
>>> '%010.2f' % pi 
'0000003.14'

>>> '%s plus %s equals %s' % (1, 1, 2)
'1 plus 1 equals 2'

其中,%符號(conversion specifiers,中文譯名轉換說明符)後面的值如果有多個,這多個值需要放入元組或數據字典中。如果需要在格式化的字符串中輸出百分符號,需要連打%%。如果元組出現在轉換說明符之後,則該元素需要加上括號,否則容易導致編譯錯誤。

除此之外,Python提供模版型字符串用來格式化字符串,使用方式舉例如下。在模版型字符串格式化中,對於輸出美元符號$來說,情形類似%符號,需要連打。除非使用safe_substitute函數。

>>> from string import Template
>>> s = Template('$x, glorious $x!')
>>> s.substitute(x='slurm')
'slurm, glorious slurm!'

>>> s = Template("It's ${x}tastic!") #使用{}符號界定了字符串開始和結束的位置
>>> s.substitute(x='slurm')
"It's slurmtastic!"

>>> s = Template('A $thing must never $action.')
>>> d = {}
>>> d['thing'] = 'gentleman'
>>> d['action'] = 'show his socks'
>>> s.substitute(d)
'A gentleman must never show his socks.'

數據字典也可以參與字符串格式化,其效果與Template有類似之處。在%與種類之間加入括號,並在括號中標明鍵值,輸出即可爲對應的真值。舉例如下:(關於什麼是數據字典,本文後面部分會詳細展開)

>>> phonebook = {'Beth': '9102', 'Alice': '2341', 'Cecil': '3258'}
>>> "Cecil's phone number is %(Cecil)s." % phonebook
"Cecil's phone number is 3258."
>>> template = '''<html>
<head><title>%(title)s</title></head>
<body>
<h1>%(title)s</h1>
<p>%(text)s</p>
</body>'''
>>> data = {'title': 'My Home Page', 'text': 'Welcome to my home page!'}
>>> print(template % data)
<html>
<head><title>My Home Page</title></head>
<body>
<h1>My Home Page</h1>
<p>Welcome to my home page!</p>
</body>

Python字符串還可使用星號控制讀入格式化字符的長度,例如:

>>> '%.*s' % (5, 'Guido van Rossum')
'Guido'

其他Python字符串常見方法:

  1. find函數:find(ele, [start], [end]):從起始下標(可選)到終止下標(可選)找ele元素第一次出現的位置並返回。若沒有找到,返回-1。類似方法:rfind, index, rindex, count, startswith, endswith。注意index和find的區別是index找不到時拋出異常,find函數找不到元素時返回-1。
  2. join函數:與split函數相反,用於拼接字符串。類似方法:rsplit, splitlines(返回所有行的列表,可以指定換行符爲可選參數).
  3. lower函數:返回用小寫字母寫的字符串。類似方法:islower, capitalize, swapcase, title, istitle, upper, isupper, title, capwords (跟title函數用法一樣)
  4. replace, expandtabs(用空格替代tab,可以指定空格數量,默認爲8), maketrans 和 translate的組合使用(需要from string import maketrans)
  5. strip(從開頭和結尾處出去相應字符)。類似方法:lstrip(從開頭處除去), rstrip(從結尾處除去)

常見方法使用示例:

>>> dirs = '', 'usr', 'bin', 'env'
>>> '/'.join(dirs)
'/usr/bin/env'
>>> print 'C:' + '\\'.join(dirs)
C:\usr\bin\env
>>> 'This is a test'.replace('is', 'eez')
'Theez eez a test'

strip函數裁剪字符串左右雙側的特定字符。

'*** SPAM * for * everyone!!! ***'.strip(' *!')
'SPAM * for * everyone'
>>> from string import maketrans
>>> table = maketrans('cs', 'kz')
>>> a = "this is an incredible test"
>>> b = a.translate(table)
>>> b
'thiz iz an inkredible tezt'
 “”“可選的參數爲需要去掉的符號。
如此處模擬快速講德語可以去掉空格。“”“
>>> c = a.translate(table, ' ')
>>> c
'thizizaninkredibletezt'

四、Python基本數據結構總覽:

Python容器(container)分爲兩大類,一類爲列表,還有一類爲映射(mapping)。一種數據結構中理論上可包含另一種或同種結構,示例如下:

>>> edward = ['Edward Gumby', 42]
>>> john = ['John Smith', 50]
>>> database = [edward, john]
>>> database

[['Edward Gumby', 42], ['John Smith', 50]]

Python的六個內置序列型數據結構:

列表(list)、元組(tuple)、字符串、Unicode字符串、緩衝對象(buffer objects)和範圍(xrange)。其中,元組具有不可修改性

對於Python的序列型數據結構(sequence types),可進行的操作包括查下標(indexing)、分片(slicing)、相加(adding)、乘法(multiplying)、確定相互關係、求長度、查找最大最小元素和迭代。

Python分片示例:

>>> tag = '<a href="http://www.python.org">Python web site</a>'
>>> tag[9:30]
'http://www.python.org'
>>> tag[32:-4]
'Python web site'

>>> numbers[0:10:2] #最後一個數字爲所需取的序列步長
[1, 3, 5, 7, 9]

>>> numbers[-3:] #取numbers數組的最後三個元素,注意不能是[-3:0]。否則將會取得空序列。
[8, 9, 10]

Python中取得域名的方法:

# Split up a URL of the form http://www.something.com
url = raw_input('Please enter the URL: ')
domain = url[11:-4]  #第11項包括,倒數第4項不包括
print "Domain name: " + domain

Python使用相乘初始化序列示例:

>>> sequence = [None] * 10

Python確定關係關鍵詞(in),示例:

>>> subject = '$$$ Get rich now!!! $$$'
>>> '$$$' in subject
True

Python中計算長度(len方法)、查找最大(max方法)最小元素(min方法)示例

>>> numbers = [100, 34, 678]
>>> len(numbers)
3
>>> max(numbers)
678
>>> min(numbers)
34

五、Python列表與元組

Python中,通過list函數,可以將非列表元素轉化爲列表元素。例如:

>>> list('Hello')
['H', 'e', 'l', 'l', 'o']

通過 ''.join(somelist)的方式(‘’爲無空格單字符),可以將列表轉化爲字符串。

Python列表除了在三中提到的各種操作之外, 還包含了修改元素、刪除元素、分片賦值以及一些列表方法的操作。

常用list方法: del, append, count(可操作單個或列表元素) extend, reverse, reversed, sort, sorted(只可操作列表元素),
index, insert, pop, remove, random.choice(p.g.159, Ref 1)(只可操作單個元素)。

部分列表方法示例:

del方法示例:

>>> names = ['Alice', 'Beth', 'Cecil', 'Dee-Dee', 'Earl']
>>> del names[2]
>>> names
['Alice', 'Beth', 'Dee-Dee', 'Earl']
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> scoundrel = None
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = None

其中,del方法還能夠用來刪除字典元素。注意,python中若將對象賦值給None,變量名並未刪除,對象數據被交給了垃圾回收機制。然而若是使用del方法,則對象的變量名被刪除,其值則不一定。對象的值仍然存在於此對象的拷貝對象中或者被python垃圾回收機制回收。詳見參考資料1第108頁。

>>> scoundrel = {'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = scoundrel
>>> scoundrel
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> scoundrel = None
>>> robin
{'age': 42, 'first name': 'Robin', 'last name': 'of Locksley'}
>>> robin = None
>>> robin
>>> 

分片賦值操作示例:

>>> name = list('Perl')
>>> name[1:] = list('ython')
>>> name
['P', 'y', 't', 'h', 'o', 'n']
>>> numbers = [1, 5]
>>> numbers[1:1] = [2, 3, 4] #用列表[2, 3, 4]取代了空分片[1:1]達到插入效果
>>> numbers
[1, 2, 3, 4, 5]
>>> numbers
[1, 2, 3, 4, 5]
>>> numbers[1:4] = []   #將有代碼的分片賦值給空列表,達到刪除的效果
#與此操作效果相同:del numbers[1:4]
>>> numbers
[1, 5]
>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a.extend(b)   #效率比 a = a + b更高,此處a, b均爲列表
>>> a
[1, 2, 3, 4, 5, 6]
>>> knights = ['We', 'are', 'the', 'knights', 'who', 'say', 'ni']
>>> knights.index('who')
4
>>> numbers = [1, 2, 3, 5, 6, 7]
>>> numbers.insert(3, 'four')
>>> numbers
[1, 2, 3, 'four', 5, 6, 7]
>>> x = [1, 2, 3]
>>> x.pop()
3
>>> x
[1, 2]
>>> x.pop(0)
1
>>> x         #pop方法是列表方法中唯一既修改列表,又返回值的方法。
[2]

備註:pop方法可與append方法結合構造stack,與insert方法結合構造queue

>>> x = ['to', 'be', 'or', 'not', 'to', 'be']
>>> x.remove('be')
>>> x
['to', 'or', 'not', 'to', 'be']
>>> x = [1, 2, 3]
>>> x.reverse() #sort用法類似   

注意,reversed不返回一個數組,而只是返回了迭代。具體用法如下:

>>> x = [1, 2, 3]
>>> list(reversed(x))
[3, 2, 1]
>>> x = [4, 6, 2, 1, 7, 9]
>>> y = sorted(x)   
#sort僅僅對當前列表操作,不返回任何值。而sorted與reversed類似,事實上僅僅是返回了迭代。(詳見參考資料1 102頁)
>>> x
[4, 6, 2, 1, 7, 9]
>>> y
[1, 2, 4, 6, 7, 9]

特殊的排序方法示例: Python中的默認排序的順序是非遞減。如果要改變默認排序順序,需要重寫函數compare(x, y)。cmp函數是顯示默認排序標準的函數。

>>> cmp(42, 32)
1
>>> cmp(99, 100)
-1
>>> cmp(10, 10)
0
>>> numbers = [5, 2, 9, 7]
>>> numbers.sort(cmp)
>>> numbers
[2, 5, 7, 9]

>>> x = [4, 6, 2, 1, 7, 9]
>>> x.sort(reverse=True)
>>> x
[9, 7, 6, 4, 2, 1]
>>> x = ['aardvark', 'abalone', 'acme', 'add', 'aerate']
>>> x.sort(key=len)
>>> x
['add', 'acme', 'aerate', 'abalone', 'aardvark']

Python元組是一種與列表不同的結構,它的元素不可新修改。可通過用逗號把數值隔開而獲得,或者在值串的前後加上括號。

>>> 1, 2, 3
(1, 2, 3)
>>> (1, 2, 3)
(1, 2, 3)

單個值的元組,必須在元組元素後面加上逗號,否則會導致歧義。例如:

>>> 3*(40+2)
126
>>> 3*(40+2,)
(42, 42, 42)

通過tuple()函數,可將非元組元素變爲元組元素。如果原來已經是元組,則不變。

>>> tuple([1, 2, 3])
(1, 2, 3)
>>> tuple('abc')
('a', 'b', 'c')
>>> tuple((1, 2, 3))
(1, 2, 3)

元組中的元素可進行分片操作,例如:

>>> x = 1, 2, 3
>>> x[1]
2
>>> y = x[0:2]
>>> y
(1, 2)

六、Python數據字典

根據鍵值(key,可以是數字、字符或元組等不可變類型)能夠找到對應真值(value),且鍵值的順序不固定的數據結構叫做映射(mapping)。Python中唯一符合映射條件的數據結構爲數據字典。注意,數據字典的鍵值只能是唯一的,然而真值可以不唯一。因爲採用的機構是映射,因此在數據字典中找鍵的速度要快於在列表中線性查找元素的速度。(哈希表找鍵值的時間複雜度爲O(1))

使用dict函數可以將其他數據結構轉化爲數據字典。注意,dict和tuple, list, str等一樣,事實上不是函數,而只是一種類型。

數據字典建立示例:

>>> phonebook = {'Alice': '2341', 'Beth': '9102', 'Cecil': '3258'}

>>> items = [('name', 'Gumby'), ('age', 42)]
>>> d = dict(items)
>>> d
{'age': 42, 'name': 'Gumby'}
>>> d['name']
'Gumby'

>>> d = dict(name='Gumby', age=42)
>>> d
{'age': 42, 'name': 'Gumby'}

數據字典的建立還可以使用fromkeys的方式,這針對於一個列表中全部是鍵值需要建立字典的情形,可以與dict方法結合使用:

>>> {}.fromkeys(['name', 'age'])
{'age': None, 'name': None}
>>> dict.fromkeys(['name', 'age'])
{'age': None, 'name': None}
>>> dict.fromkeys(['name', 'age'], '(unknown)')
{'age': '(unknown)', 'name': '(unknown)'}

數據字典有以下常用操作:

  • len(d) returns the number of items (key-value pairs) in d.
  • d[k] returns the value associated with the key k.
  • d[k] = v associates the value v with the key k.
  • del d[k] deletes the item with key k.
  • k in d checks whether there is an item in d that has the key k. (注意,此處說明in方法在字典中是用來判斷鍵值是否存在的)

數據字典常用方法:

  1. d.clear():爲對數據字典地址進行清除的函數。調用時會清除所有指向該地址的變量。
  2. d.copy():返回對於該數據字典的淺複製,淺複製的意思是當“複製品”中的基本數值被改變時,原字典數值不影響。然而對字典所在位置調用地址方法(如d[k].remove(val)時),字典就會收到影響。若需要複製一個字典的值以及地址,需要用語句from copy import deepcopy導入deepcopy模塊,然後調用d.deepcopy()方法進行復制。
  3. d.get(key, [default_not_found_value]),若在字典中存在key,返回key對應的真值。否則,返回None。如果規定了找不到真值時的返回值default_not_found_value(比如N/A),則返回用戶規定的值。其對應的一個類似方法是d.setdefault(key, [default_add_in_value])。該方法若字典d中存在k,則返回對應真值並且不對字典進行修改,否則插入default_add_in_value,未選擇該可選參數時設定真值爲None。
  4. items(d)和iteritems(d):都是以不定順序返回數據字典的所有鍵值對。區別是items返回列表形式,而iteritems返回迭代形式,被返回的迭代需要作爲list()方法的參數才能被轉化爲列表。但是,iteritems的迭代效率高於items。類似的方法:d.keys()和d.iterkeys(),d.values()和d.itervalues()。
  5. d.pop(k)和d.popitem():從字典中刪除一個鍵值,並返回該鍵值對應的真值。其中,popitem對於鍵值的刪除是任意的。
  6. d1.update(d2):將字典d2添加進d1。若兩字典含有鍵值相同的元素,則d1中對應元素的真值被替換爲d2中該鍵值對應的真值。此處,d2可以不侷限於數據字典,可以是任何形式的鍵-值對或對應的變量。

數據字典常用方法示例:

調用clear方法對數據字典內存地址進行清除示例:

>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x = {}
>>> y
{'key': 'value'}

>>> x = {}
>>> y = x
>>> x['key'] = 'value'
>>> y
{'key': 'value'}
>>> x.clear()
>>> y
{}

如果上例中,想要clear x字典而不影響y字典,可以採用以下辦法:

>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y
{'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> x.clear()
>>> y
{'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> x = {'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> y = x.copy()
>>> y['username'] = 'mlh'
>>> y['machines'].remove('bar')
>>> y
{'username': 'mlh', 'machines': ['foo', 'baz']}
>>> x
{'username': 'admin', 'machines': ['foo', 'baz']}  

 “”“” 'username'對應的值仍爲'admin',沒有被修改過。
然而machines[bar]這個元素被成功remove。 ”“”

另外,若上述例子中調用del刪除一整個鍵值,備份中的鍵值和真值不受影響:

>>> del y['machines']
>>> y
{'username': 'admin'}
>>> x
{'username': 'admin', 'machines': ['foo', 'bar', 'baz']}
>>> d = {'title': 'Python Web Site', 'url': 'http://www.python.org', 'spam': 0}
>>> d.items()
[('url', 'http://www.python.org'), ('spam', 0), ('title', 'Python Web Site')]

>>> it = d.iteritems()
>>> it
<dictionary-iterator object at 169050>
>>> list(it) # Convert the iterator to a list
[('url', 'http://www.python.org'), ('spam', 0), ('title', 'Python Web Site')]

七、Python方法:

Python中,使用def關鍵字定義一個模塊爲python函數。在def關鍵字之後的表示方法名稱的名詞叫做方法頭(formal parameters),方法頭後面括號中的參數叫做傳入參數(actual parameters或arguments或values)例如,一個計算並返回n個斐波那契數的方法如下:

# -*- coding: cp936 -*-
def fibs(num):    
    result = [0, 1]
    for i in range(num-2):
        result.append(result[-2] + result[-1])
    return result

#計算並輸出10個斐波那契數:
print fibs(10)

其中,在def聲明(或class聲明)一行之後加一個字符串(可以是引號表示的單行或三引號表示的多行)則表示爲此模塊添加說明(documentation)。可以採用f.__doc__或help(f)的方法查看此模塊的編制說明(f爲需要查看的方法、類等特定模塊)。

當分支語句與函數一起使用時,各分支的情況都需要考慮,以防函數返回None值。

與其他高級語言類似,python的基本數據類型在方法中一般是值傳遞的,而內置類型或對象一般是地址傳遞的。Python也支持傳入默認函數,規則與一般高級語言類似。

此外,Python還支持不定數量的參數以及賦值表達式作爲參數傳入方法。傳入不定數量參數時,這不定數量個參數(可以是0個)被作爲元組處理(傳入參數前加*號)。傳入賦值表達式時,被作爲數據字典處理(傳入參數前加**號)。(詳見參考資料1 第125-127頁):

例如,print_parameters這個參數就展示了默認函數、列表參數和字典參數的傳入和使用方式:

>>> def print_parameters(num1, num2, num3 = 3, * paras, **dicts):
	print num1, num2, num3
	print paras
	print dicts

>>> print_parameters(1, 2, 3, 4, 5, 6, 7, foo = 1, bar = 2)
1 2 3
(4, 5, 6, 7)
{'foo': 1, 'bar': 2}
>>> print_parameters(1, 2)
1 2 3
()
{}
>>> 

在向方法傳遞的過程中,若加上*或**符號,可自動生成相應傳入列表,而不需要對原元組或數據字典進行解包。關於傳入參數的快速入門示例,詳見參考資料1 129-130頁:

>>> def add(x, y): return x + y

>>> params = (1, 2)
>>> add(*params)
3

函數中,當本地變量與全局變量重名時,可以通過global()[para]的方法調用變量名爲para的全局變量,舉例如下(詳見參考資料1 132頁):

>>> def combine(parameter):
	print parameter + globals()['parameter']

>>> parameter = 'berry'
>>> combine('Straw')
Strawberry

Python還支持方法的嵌套使用(或稱作用域嵌套)和遞歸。其中,方法的嵌套使用舉例如下(參考資料1 133頁):

>>> def multiplier(factor):
            def multiplyByFact(num):
		return num * factor
            return multiplyByFact

>>> double = multiplier(2)
>>> double(5)
10
>>> multiplier(5)(4)
20

Python中,還可使用map(f, seq)可以通過f函數處理seq序列,也可使用filter(f, seq)函數根據f函數的情況運行seq序列。在python 3中,它們都在functools模塊。也可以通過lambda函數與它們的結合解決一些數值問題。(舉例詳見參考資料1 第138頁)然而,列表推導式仍然是應對此類問題的更佳方案。

>>> numbers = [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]
>>> reduce(lambda x, y: x+y, numbers)
1161

備註:此處也可以調用operator模塊的add函數。

八、Python類基本知識筆記:

_Python語言是面向對象編程語言之一,因此它與其他語言一樣,支持對象的繼承、多態、封裝這樣的面向對象編程的基本特徵。 _

Python類Person創建以及使用案例(148 - 150頁):

__metaclass__ = type # Make sure we get new style classes
class Person:
    
    def setName(self, name):
        self.name = name

    def getName(self):
        return self.name

    def greet(self):
        print "Hello, world! I'm %s." % self.name

Python語言中的類有新樣式類和舊樣式類之分。其中,在文件開頭加上__metaclass__ = type 或類繼承object的類均爲新類,否則爲舊類。在python 3中,不存在舊樣式類。(參考資料1 175頁)

注意,self關鍵字用在python的類書寫中,特指類指代的對象本身,作用是將此類的變量以及類方法與普通的變量和方法區別開來。

class MemberCounter:
    members = 0
    
    def init(self):
        MemberCounter.members += 1

此例中,由於members並不是self關鍵字修飾的變量,因此它的存儲是與對象分開的,類似於C++中static關鍵字變量處理的方法。但是,若此類中的變量被認爲修改之後,則變爲本地變量。舉例如下:

>>> m1 = MemberCounter()
>>> m1.init()
>>> MemberCounter.members
1
>>> m2 = MemberCounter()
>>> m2.init()
>>> MemberCounter.members
2
>>> m1.members
2
>>> m2.members
2
>>> m1.members = 'Two'
>>> m1.members
'Two'
>>> m2.members
2

對於Person類的調用示例:

>>> foo = Person()
>>> bar = Person()
>>> foo.setName('Luke Skywalker')
>>> bar.setName('Anakin Skywalker')
>>> foo.greet()
Hello, world! I'm Luke Skywalker.
>>> bar.greet()
Hello, world! I'm Anakin Skywalker.
#If I already know foo is an instance of Person.
>>> Person.greet(foo)
Hello, world! I'm Luke Skywalker.

Python也支持對類通過使用構造函數的方式進行初始化。構造函數爲寫在類中的__init__方法,舉例如下:

class FooBar:
def __init__(self, value=42):
self.somevar = value

>>> f = FooBar('This is a constructor argument')
>>> f.somevar
'This is a constructor argument'

Python中也可使用__del__函數作爲destructor,然而一般情況下這個析構函數並沒有什麼卵用。

Python類中的變量封裝只是形式上的(因爲python代碼是開源的)。對於變量有三種封裝層次,以下列Person類爲例:

class Secretive:
    _name = "SY"

    def __inaccessible(self):
        print "Bet you can't see me..."

    def accessible(self):
        print "The secret message is: "
        self.__inaccessible()

    def setName(self, name):
        self._name = name

    def getName(self):
        return self._name

>>> s = Secretive()
>>> s.getName()
'SY'
>>> s.name
報錯
>>> s.setName("John")
>>> s.getName()
'John'
>>> s.__inaccessible()
報錯
>>> s._Secretive__inaccessible()
Bet you can't see me...
>>> s._Secretive_name
報錯

由此可見,python封裝的級別通過下劃線確定。其中,單下劃線表示類內的私有變量,(理論上)無法被調用(形式上仍然可以通過s._name進行調用)。雙下劃線不能通過調用類名直接獲得,而要通過調用單下劃線加類名和方法名(變量名)的方法獲得。

Python語言支持繼承機制以及多繼承,格式如:

class subClass(superClass1, superClass2...):

關於Python語言的繼承機制,詳細請參考參考資料1 153 - 156頁。其中,有幾個與繼承機制有關的方法和定值值得參考:

  1. issublclass(Subclass, Superclass)方法可以用來確定Subclass(是類名,而不是類對應的變量名)是否是Superclass(是類名,而不是類對應的變量名)的子類。
  2. Subclass.__bases__可以用來查看子類的(一個或多個)父類信息。
  3. isinstance(obj, Class)方法可以用來確定obj是否是class的一個對象。
  4. obj.class__變量可以用來獲得對象obj的類的信息,注意,若類中含有__metaclass = type這樣表示這是一個新類以及其子類,都可以使用type(obj)來獲得對象obj的類的信息。
  5. hasattr(obj, func):檢查對象obj對應的類中是否有方法(或變量)func。在python 3.0中,hasattr(x, 'call')可以用來代替callable(x)函數(使用方式見參考資料1第156頁)。
  6. getattr(obj, func, defalt_if_not_exist): 得到obj中func(用字符串表示方法名)有關信息。若不能返回,則指定特定返回值default_if_not_exist(如None)。這個方法往往在callable(x)中作爲x的部分。 7.setattr(obj, func, value):將對象中的func變量(方法或值,包括在字符串中)設置成value值。 8.Class.dict:用於查看一個類有關的詳細信息。

Python中的繼承與其他高級語言有所不同,使用的是協議機制,即不要求對象一定要繼承父類的全部或部分特點,而是對象要遵守以下可選的一個或幾個協議。具體的一些python協議如下:

引用自參考資料1第182頁 len(self): 返回集合中對象的數量 getitem(self, key): 返回通過key取得的值 setitem(self, key, value): 修改對象的值(用於可更改對象) delitem(self, key): 刪除相應對象(用於可更改對象)

如:

class CounterList(list):
    def __init__(self, *args):
        super(CounterList, self).__init__(*args)
        self.counter = 0
        
    def __getitem__(self, index):
        self.counter += 1
        return super(CounterList, self).__getitem__(index)

可以這樣調用:

>>> cl = CounterList(range(10))
>>> cl
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> cl.reverse()
>>> cl
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> del cl[3:6]
>>> cl
[9, 8, 7, 3, 2, 1, 0]
>>> cl.counter
0
>>> cl[4] + cl[2]
9
>>> cl.counter
2

在python繼承機制中,子類的初始化必須通過一定手段調用父類,否則可能出現問題。以SongBird類繼承Bird類爲例,調用方式有兩種:

  1. 在子類中調用父類的構造函數:
class SongBird(Bird):
    def __init__(self):
        Bird.__init__(self)
        self.sound = 'Squawk!'

    def sing(self):
        print self.sound
  1. 使用super()函數:
class SongBird(Bird):
    def __init__(self):
        super(SongBird, self).__init__()
        self.sound = 'Squawk!'
    def sing(self):
        print self.sound

總結:

本文從python語言的基本運算規則出發,接着講了python的控制語句的使用方法,梳理了python語句中的分支、循環以及一些特殊的控制語句。接着本文梳理介紹了把這些控制語句用於python數據結構的簡要學習和處理當中,着重討論了python中兩種最重要最基本的數據結構列表和字典。在對於基本控制語句和基本數據機構有所瞭解之後,本文探討了在python中使用方法(函數)以實現模塊化和過程化編程的一些常用方式方法。本文以對於python語言面向對象機制的初步探討作爲結尾。

任何面向對象編程語言的基本知識學習初級階段路線也可以按照類似方法循序漸進地進行,以便於順利完成對於該編程語言的入門。然而對於任何一門編程語言來說,學習的過程可以是永無止境的,入門僅僅只是小小的冰山一角。在一門編程語言的進階篇學習階段,深入瞭解這些基本知識在其他方面如文件傳輸和用戶界面編程方面的使用也是同樣至關重要和精彩紛呈的。進入高級階段之後,程序員們更要“跟上時代的步伐”,深入挖掘該語言在網絡計算、數據處理乃至並行和系統處理、大數據計算和科學計算等各方面最新的應用情況。對於編程語言界正冉冉升起的新星python來說情況更是如此。希望本文能真正幫助IT界的同行們以及python的學習者們迅速紮實地掌握python編程的要點,走好python編程的“入門級”。

參考資料:

  1. Magnus Lie Hetland, Beginning Python, From Novice to Professional, Second Edition, in Apress。羣主新年善心大發,居然上傳了可以0代價下載的原書英文版哦!地址:http://download.csdn.net/detail/samyjy/9744964
  2. Robert Sedgewick, Kevin Wayne 以及Robert Dondero,普林斯頓大學Python程序設計導論,URL:http://introcs.cs.princeton.edu/python/home/
  3. 有關Python pow函數的使用:URL:https://zhidao.baidu.com/question/571409325.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章