Python文件
Python程序保存為文件以.py結尾,一個簡單的例子:
#!/usr/bin/python
#Filename: helloworld.py
print('Hello World')
.py文件能不能像.exe文件那樣直接運行呢?在Windows上是不行的,但是,在Mac和Linux上是可以的,方法是在.py文件的第一行加上一個特殊的注釋:
#!/usr/bin/env python3
ps:此處指定為python3的可執行程序,python 3版本沒有向前兼容。
然后,通過命令給hello.py以執行權限:
$ chmod a+x hello.py
就可以直接運行hello.py了。
幫助
在 Python 中,如果你想得到任何關于函數或語句的快速信息幫助,就可以使用內置的 help 函數:
>>> help(print)
注意是在Python交互模式下輸入,可以通過在命令行模式下敲命令python3就進入到Python交互模式,它的提示符是>>>
。
不想看時,按 q 來退出幫助。
如果你需要得到關于類似 return 操作符的幫助,需要在內部加上引號, >>> help('return')
, 這樣 Python 就能理解你到底想干什么。
輸入輸出
- 輸出
使用print方法,例如:
>>> print('hello, world')
help獲取到的信息:
print(...)
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file: a file-like object (stream); defaults to the current sys.stdout.
sep: string inserted between values, default a space.
end: string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.
- 輸入
使用input方法,例如:
>>> input('請輸入信息:')
help獲取到的信息:
input(prompt=None, /)
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.
基礎語法
以#
開頭的語句是注釋,注釋是給人看的,可以是任意內容,解釋器會忽略掉注釋。其他每一行都是一個語句,當語句以冒號:
結尾時,縮進的語句視為代碼塊。
Python程序是大小寫敏感的,如果寫錯了大小寫,程序會報錯。
變量
變量是標識符的例子。標識符是用來標識某樣東西的名字。在命名標識符的時候,你要遵循這些規則:
標識符的第一個字符必須是字母表中的字母(大寫或小寫)或者一個下劃線
_
。標識符名稱的其他部分可以由字母(大寫或小寫)、下劃線
_
或數字0-9
組成。標識符名稱是對大小寫敏感的。例如,
myname
和myName
不是一個標識符。注意前者中的小寫n
和后者中的大寫N
。有效標識符名稱的例子有
i
、__my_name
、name_23
和a1b2_c3
。無效標識符名稱的例子:
2things
、this is spaced out
和my-name
。
邏輯行和物理行
物理行是你在編寫程序時所看見的。邏輯行是 Python 看見的單個語句。 Python 假 定每個物理行對應一個邏輯行。
邏輯行的例子如 print ’Hello World’
這樣的語句 —— 如果它本身就是一行(就像 你在編輯器中看到的那樣),那么它也是一個物理行。
默認地, Python 希望每行都只使用一個語句,這樣使得代碼更加易讀。
如果你想要在一個物理行中使用多于一個邏輯行,那么你需要使用分號;
來 特別地標明這種用法。分號表示一個邏輯行/語句的結束。(但是并不建議這樣寫)
僅僅當邏輯行太長的時候, 在多于一個物理行寫一個邏輯行,可以在一行末尾添加\
表示邏輯行并沒有結束:
print \
i
縮進
在 Python 中空白非常重要。實際上,在每行開頭的空白很重要。稱之為縮進。 在行首的主要的空白(空格鍵和制表符)用來決定邏輯行縮進的層次,從而來決定語 句分組。
這意味著同一層次的語句必須有相同的縮進。每一組這樣的語句稱為一個塊。
你需要記住的一樣東西是錯誤的縮進會引發錯誤。例如:
i=5
print('Value is ', i) # Error! Notice a single space at the start of the line
print('I repeat, the value is ', i)
當你運行的時候,會得到下面的出錯信息:
File "whitespace.py", line 4
print('Value is ', i) # Error! Notice a single space at the start
of the line ^
IndentationError: unexpected indent
注意在第二行的開頭有一個空格。Python 給出的錯誤信息告訴我們程序的語法不正確。
不要混合使用制表符和空格來縮進,因為這在跨越不同的平臺的時候,無法正常工作,推薦使用4個空格的縮進。
給靜態語言程序員的注釋:
Python 總是使用縮進來代表代碼塊,不再使用括號。
數據類型
在 Python 中數的類型有三種 —— 整數、浮點數和復數。
- 2是一個整數的例子。
- 3.23 和 52.3E-4 是浮點數的例子。 E 標記表示 10 的冪。在這里,2.3E-4 表示 52.3 * 10?4。
- (-5+4j)和(2.3-4.6j)是復數的例子。
給有經驗的程序員的注釋:
在 Python 中不用區分'long int'類型。默認的整數類型可以任意長。
字符串
Python中默認所有的字符串的編碼是 Unicode。沒有僅僅使用 ASCII 的字符串,原因是 Unicode 是 ASCII 的超集。如果要嚴格使用 ASCII 編碼的 字節流,可用 str.encode("ascii") 。
如果你寫的文本基本上全部是英文的話,用Unicode編碼比ASCII編碼需要多一倍的存儲空間,在存儲和傳輸上就十分不劃算。因此通常會使用UTF-8編碼,可以通過如下方式聲明.py文件的編碼:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
對于不清楚什么UTF-8的同學可以看下:ASCII、Unicode和UTF-8的關系
- 單引號
你可以用單引號指定字符串,如 ’Quote me on this’ 。所有的空白,即空格和制表符都照原樣保留。
- 雙引號
在雙引號中的字符串與單引號中的字符串的使用完全相同,例如 "What’s yourname?" 。
- 三引號
利用三引號("""or”’),你可以指示一個多行的字符串。你可以在三引號中自由 的使用單引號和雙引號。例如:
'''This is a multi-line string. This is the first line.
This is the second line.
"What's your name?," I asked.
He said "Bond, James Bond."
'''
- 轉義序列
假如你有一個字符串包含單引號’
,如何表示這個字符串呢?例如,字符串是What’s your name?
。你不能用 ’What’s your name?’
來表示,因為 Python 不知道字符串的起始和結束位置。所以應該將字符串中間的這個單引號指定為不表示字符串的結束。這可在稱之為轉義序列的協助下實現。可以講單引號指定為 \'
—— 注意反斜杠。現在,就能將字符串表示為 ’What\’s your name?’
。
還有一種方式就是用雙引號"What’s your name?"
。類似地,在用雙引號的字符串 中用雙引號必須用轉義符。還有,必須用轉義符\\
來表示反斜杠。
如果你想指定兩行字符串,該如何做呢?一種方式就是用前面提到的用三引號的 字符串,或者可以用轉義符\n
表示新的一行的開始。例如 This is the first line\nThis is the second line 。
另外一個有用的轉義字符是Tab鍵 ——\t
。有許多轉義序列,這兒僅僅提到了最有用的幾個。
需要說明的是,在一個字符串中,在一行末尾的反斜杠僅僅表示下一行的字符串是上一行的繼續,但并不增加新的行。例如:
"This is the first sentence.\
This is the second sentence."
與"This is the first sentence. This is the second sentence."
等價。
- 自然字符串
如果你想指定一些不被特殊處理,例如像轉義序列,那么,就需要通過在字符 串前面附加 r 或 R 來指定自然字符串。
給有經驗的程序員的注釋:
在 Python 中沒有單獨的 char 數據類型。
記住單引號和雙引號是一樣的 —— 沒有絲毫差異。
用正則表達式的時候請使用自然字符串。否則,可能會用到許多反斜杠。例如,后向引用符可以寫成’\\1’
或r’\1’
。
格式化
- 使用
%
>>> 'Hello, %s' % 'world'
'Hello, world'
>>> 'Hi, %s, you have $%d.' % ('Michael', 1000000)
'Hi, Michael, you have $1000000.'
%
運算符就是用來格式化字符串的。在字符串內部,%s
表示用字符串替換,%d
表示用整數替換,有幾個%?
占位符,后面就跟幾個變量或者值,順序要對應好。如果只有一個%?
,括號可以省略。
常見的占位符有:
占位符 | 替換內容 |
---|---|
%d | 整數 |
%f | 浮點數 |
%s | 字符串 |
%x | 十六進制整數 |
其中,格式化整數和浮點數還可以指定是否補0和整數與小數的位數:
>>> print('%2d-%02d' % (3, 1))
3-01
>>> print('%.2f' % 3.1415926)
3.14
>>>
如果你不太確定應該用什么,%s
永遠起作用,它會把任何數據類型轉換為字符串。
有些時候,字符串里面的%
是一個普通字符怎么辦?這個時候就需要轉義,用%%
來表示一個%
:
>>> 'growth rate: %d %%' % 7
'growth rate: 7 %'
- 使用format()
>>> print('{0} is {1} years old'.format('makey', 12))
makey is 12 years old
>>> '{0:.3}'.format(1/3) # decimal (.) precision of 3 for float
'0.333'
>>> '{0:_^11}'.format('hello') # fill with underscores (_) with the text centered (^) to 11 width
'___hello___'
>>> '{name} wrote {book}'.format(name='Swaroop', book='A Byte of Python') # keyword-based
'Swaroop wrote A Byte of Python'
操作符
操作符 | 解釋 | 示例 |
---|---|---|
+ | 兩數相加或者字符串拼接 | 3 + 5 gives 8. ’a’ + ’b’ gives ’ab’. |
- | 表示負數或者兩數相減 | -5.2 gives a negative number. 50 - 24 gives 26. |
* | 乘法或者復制字符串 | 2 * 3 gives 6. ’la’ * 3 gives ’lalala’. |
** | 次方 | 3 ** 4 gives 81 (i.e. 3 * 3 * 3 * 3 ) |
/ | 除法 | 4 / 3 gives 1.333333333333. |
// | 除法之后的整數部分 | 4 // 3 gives 1. |
% | 整除后的剩余部分 | 8 % 3 gives 2. -25.5 % 2.25 gives 1.5. |
<< | 位運算 | 2 << 2 gives 8. 2 is represented by 10 in bits. left shifting by 2 bits gives 1000 which represents the decimal 8. |
>> | 位運算 | 11 >> 1 gives 5. 11 is represented in bits by 1011 which when right shifted by 1 bit gives 101 which is the decimal 5. |
& | 按位與 | 5 & 3 gives 1. |
| | 按位或 | 5 | 3 gives 7 |
^ | 異或 | 5 ? 3 gives 6 |
~ | The bit-wise inversion of x is -(x+1) | ~5 gives -6 |
<, <= , == , != ,>, >= | 比較 | |
not | 非 | 相當于java中的 !true. x = True; not x returns False. |
and | 邏輯與 | 相當于java中的&& |
or | 邏輯或 | 相當于java中|| |
控制流
if 語句
if 語句用來檢驗一個條件,如果條件為真,我們運行一塊語句(稱為 if-塊),否 則我們處理另外一塊語句(稱為 else-塊)。當然,還有elif 用于判斷另一個條件, elif 和 else 部分是可選的。
if <條件判斷1>:
<執行1>
elif <條件判斷2>:
<執行2>
elif <條件判斷3>:
<執行3>
else:
<執行4>
while 語句
只要在一個條件為真的情況下, while 語句允許你重復執行一塊語句。 while 語 句是所謂循環語句的一個例子。 while 語句有一個可選的 else 從句。
# !/usr/bin/python3
# Filename while.py
number = 23
running = True
while running:
guess=int(input("Enter an integer:"))
if guess == number:
print("Congratulation, you guessed it.")
running=False #this causes the while loop to stop
elif guess < number:
print("No, it is a little higher.")
else:
print("No, it is a little lower.")
else:
print("the while loop is over.")
# Do anything else you want to do here
print("done")
輸出:
$ python3 while.py
Enter an integer : 50
No, it is a little lower than that.
Enter an integer : 22
No, it is a little higher than that.
Enter an integer : 23
Congratulations, you guessed it.
The while loop is over.
Done
當 while 循環的條件為假時 —— 這或許在第一次檢查條件的時候。如果 while 循 環有 else 從句,當不滿足條件時else塊會觸發,除非你的 while 循環將永遠循環下去不會結束!
for 循環
for..in 是另外一個循環語句,它在一序列的對象上迭代,即逐一使用序列中的每 個項目。
for i in range(1, 5):
print (i)
else :
print ( 'The for loop is over' )
range 返回一個序列的數。這個序列從第一個數開 始到第二個數為止。例如, range(1,5) 給出序列 [1, 2, 3, 4]。默認地, range 的步長 為 1。如果我們為 range 提供第三個數,那么它將成為步長。例如,range(1,5,2) 給出 [1,3]。記住,range 向上延伸到第二個數,即它不包含第二個數。
for 循環在這個范圍內遞歸 ——for i in range(1,5) 等價于 for i in [1, 2, 3, 4],這就 如同把序列中的每個數(或對象)賦值給 i ,一次一個,然后以每個 i 的值執行這個 程序塊。在這個例子中,我們只是打印 i 的值。
給有經驗的程序員的注釋:
記得 Python中的循環可以有 else 語句,但是如果循環中觸發break則else不會執行。
break
break 語句是用來終止循環語句的,即哪怕循環條件沒有變為 False 或序列還沒有 被完全迭代結束,也停止執行循環語句。
一個重要的注釋是,如果你從 for 或 while 循環中終止,任何對應的循環 else 塊 將不執行。
continue
continue 語句被用來告訴 Python 跳過當前循環塊中的剩余語句,然后繼續進行下 一輪循環。
數據結構
list
Python內置的一種數據類型是列表:list。list是一種有序的集合,可以隨時添加和刪除其中的元素。
比如,列出班里所有同學的名字,就可以用一個list表示:
>>> classmates = ['Michael', 'Bob', 'Tracy']
>>> classmates
['Michael', 'Bob', 'Tracy']
變量classmates就是一個list。用len()函數可以獲得list元素的個數:
>>> len(classmates)
3
用索引來訪問list中每一個位置的元素,記得索引是從0開始的:
>>> classmates[0]
'Michael'
>>> classmates[1]
'Bob'
>>> classmates[2]
'Tracy'
>>> classmates[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
當索引超出了范圍時,Python會報一個IndexError錯誤,所以,要確保索引不要越界,記得最后一個元素的索引是len(classmates) - 1。
如果要取最后一個元素,除了計算索引位置外,還可以用-1做索引,直接獲取最后一個元素:
>>> classmates[-1]
'Tracy'
以此類推,可以獲取倒數第2個、倒數第3個:
>>> classmates[-2]
'Bob'
>>> classmates[-3]
'Michael'
>>> classmates[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
當然,倒數第4個就越界了。
list是一個可變的有序表,所以,可以往list中追加元素到末尾:
>>> classmates.append('Adam')
>>> classmates
['Michael', 'Bob', 'Tracy', 'Adam']
也可以把元素插入到指定的位置,比如索引號為1的位置:
>>> classmates.insert(1, 'Jack')
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy', 'Adam']
要刪除list末尾的元素,用pop()方法:
>>> classmates.pop()
'Adam'
>>> classmates
['Michael', 'Jack', 'Bob', 'Tracy']
要刪除指定位置的元素,用pop(i)方法,其中i是索引位置:
>>> classmates.pop(1)
'Jack'
>>> classmates
['Michael', 'Bob', 'Tracy']
要把某個元素替換成別的元素,可以直接賦值給對應的索引位置:
>>> classmates[1] = 'Sarah'
>>> classmates
['Michael', 'Sarah', 'Tracy']
list里面的元素的數據類型也可以不同,比如:
>>> L = ['Apple', 123, True]
list元素也可以是另一個list,比如:
>>> s = ['python', 'java', ['asp', 'php'], 'scheme']
>>> len(s)
4
要注意s只有4個元素,其中s[2]又是一個list,如果拆開寫就更容易理解了:
>>> p = ['asp', 'php']
>>> s = ['python', 'java', p, 'scheme']
要拿到'php'可以寫p[1]或者s[2][1],因此s可以看成是一個二維數組,類似的還有三維、四維……數組,不過很少用到。
如果一個list中一個元素也沒有,就是一個空的list,它的長度為0:
>>> L = []
>>> len(L)
0
tuple(元組)
另一種有序列表叫元組:tuple。tuple和list非常類似,但是tuple一旦初始化就不能修改,比如同樣是列出同學的名字:
>>> classmates = ('Michael', 'Bob', 'Tracy')
現在,classmates這個tuple不能變了,它也沒有append()
,insert()
這樣的方法。其他獲取元素的方法和list是一樣的,你可以正常地使用classmates[0]
,classmates[-1]
,但不能賦值成另外的元素。
不可變的tuple有什么意義?因為tuple不可變,所以代碼更安全。如果可能,能用tuple代替list就盡量用tuple。
tuple的陷阱:當你定義一個tuple時,在定義的時候,tuple的元素就必須被確定下來,比如:
>>> t = (1, 2)
>>> t
(1, 2)
如果要定義一個空的tuple,可以寫成():
>>> t = ()
>>> t
()
但是,要定義一個只有1個元素的tuple,如果你這么定義:
>>> t = (1)
>>> t
1
定義的不是tuple,是1這個數!這是因為括號()既可以表示tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是1。
所以,只有1個元素的tuple定義時必須加一個逗號,,來消除歧義:
>>> t = (1,)
>>> t
(1,)
Python在顯示只有1個元素的tuple時,也會加一個逗號,
,以免你誤解成數學計算意義上的括號。
最后來看一個“可變的”tuple:
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])
這個tuple定義的時候有3個元素,分別是a
,b
和一個list。不是說tuple一旦定義后就不可變了嗎?怎么后來又變了?
表面上看,tuple的元素確實變了,但其實變的不是tuple的元素,而是list的元素。tuple一開始指向的list并沒有改成別的list,所以,tuple所謂的“不變”是說,tuple的每個元素,指向永遠不變。即指向a
,就不能改成指向b
,指向一個list,就不能改成指向其他對象,但指向的這個list本身是可變的!
理解了“指向不變”后,要創建一個內容也不變的tuple怎么做?那就必須保證tuple的每一個元素本身也不能變。
dict
Python內置了字典:dict的支持,dict全稱dictionary,在其他語言中也稱為map,使用鍵-值(key-value)存儲,具有極快的查找速度。
舉個例子,假設要根據同學的名字查找對應的成績,用Python寫一個dict如下:
>>> d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
>>> d['Michael']
95
把數據放入dict的方法,除了初始化時指定外,還可以通過key放入:
>>> d['Adam'] = 67
>>> d['Adam']
67
由于一個key只能對應一個value,所以,多次對一個key放入value,后面的值會把前面的值沖掉:
>>> d['Jack'] = 90
>>> d['Jack']
90
>>> d['Jack'] = 88
>>> d['Jack']
88
如果key不存在,dict就會報錯:
>>> d['Thomas']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'Thomas'
要避免key不存在的錯誤,有兩種辦法,一是通過in判斷key是否存在:
>>> 'Thomas' in d
False
二是通過dict提供的get()
方法,如果key不存在,可以返回None
,或者自己指定的value:
>>> d.get('Thomas')
>>> d.get('Thomas', -1)
-1
注意:返回None
的時候Python的交互環境不顯示結果。
要刪除一個key,用pop(key)方法,對應的value也會從dict中刪除:
>>> d.pop('Bob')
75
>>> d
{'Michael': 95, 'Tracy': 85}
請務必注意,dict內部存放的順序和key放入的順序是沒有關系的。
和list比較,dict有以下幾個特點:
查找和插入的速度極快,不會隨著key的增加而變慢;
需要占用大量的內存,內存浪費多。
而list相反:
查找和插入的時間隨著元素的增加而增加;
占用空間小,浪費內存很少。
所以,dict是用空間來換取時間的一種方法。
dict可以用在需要高速查找的很多地方,在Python代碼中幾乎無處不在,正確使用dict非常重要,需要牢記的第一條就是dict的key必須是不可變對象。
這是因為dict根據key來計算value的存儲位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了。這個通過key計算位置的算法稱為哈希算法(Hash)。
要保證hash的正確性,作為key的對象就不能變。在Python中,字符串、整數等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key:
>>> key = [1, 2, 3]
>>> d[key] = 'a list'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
set
set和dict類似,也是一組key的集合,但不存儲value。由于key不能重復,所以,在set中,沒有重復的key。
要創建一個set,需要提供一個list作為輸入集合:
>>> s = set([1, 2, 3])
>>> s
{1, 2, 3}
注意,傳入的參數[1, 2, 3]
是一個list,而顯示的{1, 2, 3}
只是告訴你這個set內部有1,2,3這3個元素,顯示的順序也不表示set是有序的。。
重復元素在set中自動被過濾:
>>> s = set([1, 1, 2, 2, 3, 3])
>>> s
{1, 2, 3}
通過add(key)方法可以添加元素到set中,可以重復添加,但不會有效果:
>>> s.add(4)
>>> s
{1, 2, 3, 4}
>>> s.add(4)
>>> s
{1, 2, 3, 4}
通過remove(key)方法可以刪除元素:
>>> s.remove(4)
>>> s
{1, 2, 3}
set可以看成數學意義上的無序和無重復元素的集合,因此,兩個set可以做數學意義上的交集、并集等操作:
>>> s1 = set([1, 2, 3])
>>> s2 = set([2, 3, 4])
>>> s1 & s2
{2, 3}
>>> s1 | s2
{1, 2, 3, 4}
set和dict的唯一區別僅在于沒有存儲對應的value,但是,set的原理和dict一樣,所以,同樣不可以放入可變對象,因為無法判斷兩個可變對象是否相等,也就無法保證set內部“不會有重復元素”。
切片 Slice
取一個list或tuple的部分元素是非常常見的操作,Python提供了切片(Slice)操作符,能大大簡化這種操作。
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
L[0:3]
表示,從索引0開始取,直到索引3為止,但不包括索引3。即索引0,1,2,正好是3個元素。
如果第一個索引是0,還可以省略:
>>> L[:3]
['Michael', 'Sarah', 'Tracy']
也可以從索引1開始,取出2個元素出來:
>>> L[1:3]
['Sarah', 'Tracy']
類似的,既然Python支持L[-1]
取倒數第一個元素,那么它同樣支持倒數切片,試試:
>>> L[-2:]
['Bob', 'Jack']
>>> L[-2:-1]
['Bob']
記住倒數第一個元素的索引是-1。
中括號中什么都不寫,只寫[:]
就可以原樣復制一個list。
你也可以給切片規定第三個參數,就是切片的步長(默認步長是 1)。
>>> shoplist = ['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::1]
['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::2]
['apple', 'carrot']
>>> shoplist[::3]
['apple', 'banana']
>>> shoplist[::-1]
['banana', 'carrot', 'mango', 'apple']
注意當步長是 2 時,我們得到在位置 0,2,... 的項,當步長是 3 時,得到位置 0,3, 等等的項。
切片操作同樣可以作用于tuple(元組)和str(字符串)
列表生成式
列表生成式即List Comprehensions,是Python內置的非常簡單卻強大的可以用來創建list的生成式。
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
還可以使用兩層循環,可以生成全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
列表生成式也可以使用兩個變量來生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
最后把一個list中所有的字符串變成小寫:
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
生成器
函數
函數是重用的程序段。它們允許你給一個語句塊一個名稱,然后你用這個名字可 以在你的程序的任何地方,任意多次地運行這個語句塊。這被稱為調用函數。
實際上之前我們一直在調用函數,例如:
>>> print('abc')
函數名其實就是指向一個函數對象的引用,完全可以把函數名賦給一個變量,相當于給這個函數起了一個“別名”:
>>> a = abs # 變量a指向abs函數
>>> a(-1) # 所以也可以通過a調用abs函數
1
定義
函數用關鍵字 def 來定義。def
關鍵字后跟一個函數的標識符名稱,然后跟一對圓括號()
。圓括號之中可以包括一些變量名,該行以冒號:
結尾。接下來是一塊語句,它們是函數體。下面這個例子將說明這事實上是十分簡單的:
#!/usr/bin/python3
# Filename: function1.py
def sayHello():
print('Hello World!') # block belonging to the function # End of function
sayHello() # call the function
sayHello() # call the function again
參數
函數取得的參數是你提供給函數的值,這樣函數就可以利用這些值做一些事情。 這些參數就像變量一樣,只不過它們的值是在我們調用函數的時候定義的,而非在函 數本身內賦值。
參數在函數定義的圓括號對內指定,用逗號分割。當我們調用函數的時候,我們 以同樣的方式提供值。注意我們使用過的術語 —— 函數中的參數名稱為形參而你提 供給函數調用的值稱為實參。
局部變量
當你在函數定義內聲明變量的時候,它們與函數外具有相同名稱的其他變量沒有任何關系,即變量名稱對于函數來說是局部的。這稱為變量的作用域。所有變量的作用域是它們被定義的塊,從它們的名稱被定義的那點開始。
#!/usr/bin/python3
# Filename: func_local.py
x = 50
def func(x):
print('x is', x)
x=2
print('Changed local x to', x)
func(x)
print('x is still', x)
輸出:
$ python3 func_local.py
x is 50
Changed local x to 2
x is still 50
在函數中,我們第一次使用x
的值的時候, Python 使用函數聲明的形參的值。 接下來,我們把值 2
賦給x
。 x
是函數的局部變量。所以,當我們在函數內改
變 x
的值的時候,在主塊中定義的 x
不受影響。在最后一個 print
語句中,我們證明 了主塊中的x
的值確實沒有受到影響。
使用全局語句
如果你想要為一個定義在函數外的變量賦值,那么你就得告訴 Python 這個變量 名不是局部的,而是全局的。我們使用 global
語句完成這一功能。沒有 global
語句, 是不可能為定義在函數外的變量賦值的。
你可以使用定義在函數外的變量的值(假設在函數內沒有同名的變量)。然而, 我并不鼓勵你這樣做,并且你應該盡量避免這樣做,因為這使得程序的讀者會不清楚 這個變量是在哪里定義的。使用global
語句可以清楚地表明變量是在外面的塊定義的。
#!/usr/bin/python
#Filename: func_global.py
x = 50
def func():
global x
print('x is',x)
x=2
print('Changed global x to',x)
func()
print('Value of x is',x)
輸出:
$ python func_global.py
x is 50
Changed global x to 2
Value of x is 2
global
語句被用來聲明x
是全局的 —— 因此,當我們在函數內把值賦給 x
的時 候,這個變化也反映在我們在主塊中使用x
的值的時候。
你可以使用同一個 global
語句指定多個全局變量。例如global x, y, z
。
使用非局部語句
上面給出了如何在局部和全局作用域內使用變量。還有一種作用域叫做“非局 部”域,處于這兩種作用域之間。
非局部作用域在你定義函數內的函數時會看到。
由于在 Python 中,任何事物是可執行的代碼,你可以在任何地方定義函數。
#!/usr/bin/python3
# Filename: func_nonlocal.py
def func_outer():
x=2
print('x is',x)
def func_inner():
nonlocal x
x=5
func_inner()
print('Changed local x to',x)
func_outer()
輸出:
$ python3 func_nonlocal.py
x is 2
Changed local x to 5
當在函數 func_inner
的內部時,在函數 func_outer
的第一行定義的 x
相對來講 既不是在局部范圍也不是在全局的作用域中,使用這樣的 x
稱之為非局部 x
,因此可以使用這個變量。
默認參數值
對于一些函數,你可能希望它的一些參數是可選的,如果用戶不想要為這些參數 提供值的話,這些參數就使用默認值。這個功能借助于默認參數值完成。你可以在函 數定義的形參名后加上賦值運算符=
和默認值,從而給形參指定默認參數值。
#!/usr/bin/python3
# Filename: func_default.py
def say(message, times = 1):
print(message * times)
say('Hello')
say('World', 5)
輸出:
$ python3 func_default.py
Hello
WorldWorldWorldWorldWorld
名為say
的函數用來打印一個字符串任意所需的次數。如果我們不提供一個值, 那么默認地,字符串將只被打印一遍。我們通過給形參 times
指定默認參數值 1 來實 現這一功能。
只有在形參表末尾的那些參數可以有默認參數值,即你不能在聲明函數形參的時候,先聲明有默 認值的形參而后聲明沒有默認值的形參。這是因為賦給形參的值是根據位置而賦值的。例如,def func(a, b=5) 是有效的,但是 def func (a=5, b) 是無效的。
默認參數很有用,但使用不當,也會掉坑里。默認參數有個最大的坑,演示如下:
先定義一個函數,傳入一個list,添加一個END再返回:
def add_end(L=[]):
L.append('END')
return L
當你正常調用時,結果似乎不錯:
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
當你使用默認參數調用時,一開始結果也是對的:
>>> add_end()
['END']
但是,再次調用add_end()時,結果就不對了:
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
默認參數是[],但是函數似乎每次都“記住了”上次添加了'END'后的list。
原因解釋如下:
Python函數在定義的時候,默認參數L
的值就被計算出來了,即[]
,因為默認參數L
也是一個變量,它指向對象[]
,每次調用該函數,如果改變了L的內容,則下次調用時,默認參數的內容就變了,不再是函數定義時的[]
了。
定義默認參數要牢記一點:默認參數必須指向不變對象!
要修改上面的例子,我們可以用None這個不變對象來實現:
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
現在,無論調用多少次,都不會有問題:
>>> add_end()
['END']
>>> add_end()
['END']
為什么要設計str
、None
這樣的不變對象呢?因為不變對象一旦創建,對象內部的數據就不能修改,這樣就減少了由于修改數據導致的錯誤。此外,由于對象不變,多任務環境下同時讀取對象不需要加鎖,同時讀一點問題都沒有。我們在編寫程序時,如果可以設計一個不變對象,那就盡量設計成不變對象。
關鍵參數
如果你的某個函數有許多參數,而你只想指定其中的一部分,那么你可以通過命 名來為這些參數賦值 —— 這被稱作關鍵參數 —— 我們使用名字(關鍵字)而不是位 置(我們前面所一直使用的方法)來給函數指定實參。
這樣做有兩個優勢 ——
一、由于我們不必擔心參數的順序,使用函數變得更加 簡單了。
二、假設其他參數都有默認值,我們可以只給我們想要的那些參數賦值。
#!/usr/bin/python3
# Filename: func_key.py
def func(a, b=5, c=10):
print('a is', a, 'and b is', b, 'and c is', c)
func(3, 7)
func(25, c=24)
func(c=50, a=100)
輸出:
$ python3 func_key.py
a is 3 and b is 7 and c is 10
a is 25 and b is 5 and c is 24
a is 100 and b is 5 and c is 50
VarArgs 參數
有時,你或許想定義一個能獲取任意個數參數的函數,這可通過使用*
號來實現。
#!/usr/bin/python3
# Filename: total.py
def total(initial=5, *numbers, **keywords):
count = initial
for number in numbers:
count += number
for key in keywords:
count += keywords[key]
return count
print(total(10, 1, 2, 3, vegetables=50, fruits=100))
輸出:
$ python3 total.py
166
當我們定義一個帶星的參數,像*param
時,從那一點后所有的參數被收集為一 個叫做 ’param’ 的元組(tuple)。在該例中,首先會給initial
的值由 5 變成 10 , 然后numbers
將 1,2,3,收集作為一個元組numbers=(1,2,3)
。
類似地,當我們定義一個帶兩個星的參數,像**param
時,從那一點開始的 所有的關鍵字參數會被收集為一個叫做 ’param’ 的字典(dict)。在該例子中, 從 vegetables=50
后的所有參數收集為一個字典 keywords=’vegetables’: 50, ’fruits’: 100
。
如果已經有一個list或者tuple,要調用一個可變參數怎么辦?可以這樣做:
>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2])
14
這種寫法當然是可行的,問題是太繁瑣,所以Python允許你在list或tuple前面加一個*
號,把list或tuple的元素變成可變參數傳進去:
>>> nums = [1, 2, 3]
>>> calc(*nums)
14
*nums
表示把nums
這個list的所有元素作為可變參數傳進去。這種寫法相當有用,而且很常見。
Keyword-only 參數
如果想指定特定的關鍵參數為 keyword-only 而不是位置參數(只能通過關鍵字進行賦值),可以在帶星的參數后申明:
#!/usr/bin/python3
# Filename: keyword_only.py
def total(initial=5, *numbers, vegetables):
count = initial
for number in numbers:
count += number
count += vegetables
return count
print(total(10, 1, 2, 3, vegetables=50))
print(total(10, 1, 2, 3,))# Raises error because we have not supplied a default argument value for 'vegetables'
輸出:
$ python3 keyword_only.py
66
Traceback (most recent call last):
File "test.py", line 12, in <module>
print(total(10, 1, 2, 3))
TypeError: total() needs keyword-only argument vegetables
在帶星號的參數后面申明參數會導致 keyword-only 參數。如果這些參數沒有默認值,且像上面那樣不給關鍵參數賦值,調用函數的時候會引發錯誤。
如果你想使用 keyword-only 參數,但又不需要帶星的參數,可以簡單地使用不適 用名字的空星,如 def total(initial=5, *, vegetables):
。
參數組合
在Python中定義函數,可以用必選參數、默認參數、可變參數、關鍵字參數和命名關鍵字參數,這5種參數都可以組合使用。但是請注意,參數定義的順序必須是:必選參數、默認參數、可變參數、命名關鍵字參數和關鍵字參數。
return 語句
return
語句用來從一個函數返回即跳出函數。我們也可選是否從函數返回一個值。
沒有返回值的 return
語句等 價于 return None
。 None
是 Python 中表示沒有任何東西的特殊類型。例如,如果一 個變量的值為 None
,可以表示它沒有值。除非你提供你自己的 return
語句,每個函 數都在結尾暗含有 return None
語句。通過運行 print(someFunction())
,你可以明白這 一點,函數 someFunction
沒有使用 return
語句,如同:
def someFunction():
pass
pass
語句在 Python 中表示一個空的語句塊。
pass
語句什么都不做,那有什么用?實際上pass
可以用來作為占位符,比如現在還沒想好怎么寫函數的代碼,就可以先放一個pass
,讓代碼能運行起來。
返回多個值
函數可以返回多個值嗎?答案是肯定的。
比如在游戲中經常需要從一個點移動到另一個點,給出坐標、位移和角度,就可以計算出新的新的坐標:
import math
def move(x, y, step, angle=0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
import math
語句表示導入math
包,并允許后續代碼引用math
包里的sin
、cos
等函數。
然后,我們就可以同時獲得返回值:
>>> x, y = move(100, 100, 60, math.pi / 6)
>>> print(x, y)
151.96152422706632 70.0
實際上返回值是一個tuple!但是,在語法上,返回一個tuple可以省略括號,而多個變量可以同時接收一個tuple,按位置賦給對應的值,所以,Python的函數返回多值其實就是返回一個tuple,但寫起來更方便。
DocStrings
Python 有一個很奇妙的特性,稱為文檔字符串,它通常被簡稱為 docstrings 。 DocStrings 是一個重要的工具,由于它幫助你的程序文檔更加簡單易懂,你應該盡量 使用它。你甚至可以在程序運行的時候,從函數恢復文檔字符串!
#!/usr/bin/python3
# Filename: func_doc.py
def printMax(x, y):
'''Prints the maximum of two numbers.
The two values must be integers.'''
x = int(x) # convert to integers, if possible
y = int(y)
if x > y:
print(x, 'is maximum')
else:
print(y, 'is maximum')
printMax(3, 5)
print(printMax.__doc__)
輸出:
$ python3 func_doc.py
5 is maximum
Prints the maximum of two numbers.
The two values must be integers.
在函數的第一個邏輯行的字符串是這個函數的文檔字符串。注意, DocStrings 也 適用于模塊和類。
文檔字符串的慣例是一個多行字符串,它的首行以大寫字母開始,句號結尾。第二行是空行,從第三行開始是詳細的描述。強烈建議你在你的函數中使用文檔字符串時遵循這個慣例。
你可以使用__doc__
(注意雙下劃線)調用printMax
函數的文檔字符串屬性(屬于函數的名稱)。請記住 Python 把每一樣東西都作為對象,包括這個函數。
如果你已經在 Python 中使用過 help()
,那么你已經看到過 DocStings 的使用了! 它所做的只是抓取函數的__doc__
屬性,然后整潔地展示給你。你可以對上面這個函 數嘗試一下 —— 只是在你的程序中包括help(printMax)
。記住按 q
退出 help
。
自動化工具也可以以同樣的方式從你的程序中提取文檔。因此,我強烈建議你 對你所寫的任何正式函數編寫文檔字符串。隨你的 Python 發行版附帶的 pydoc 命令, 與 help()
類似地使用 DocStrings 。