Python 源碼格式有點特殊。首先,可能因為出生年代久遠的緣故,編譯器默認編碼采用 ASCII,而非當前通行的 UTF-8。其次,就是強制縮進格式讓很多人 "糾結(jié)",甚至 "望而卻步"。
源文件編碼
下面這樣的錯誤,初學時很常見。究其原因,還是編譯器默認將文件當成 ASCII 碼的緣故。
SyntaxError: Non-ASCII character '\xe4' in file ./main.py on line 4, but no encoding
declared; see http://www.python.org/peps/pep-0263.html for details
解決方法:在文件頭部添加正確的編碼標識。
$ cat main.py
#/usr/bin/env python
# coding=utf-8
def main():
print "世界末日!" # 瑪雅人都是騙人的
if __name__ == "__main__":
main()
也可以寫成:
# -*- coding:utf-8 -*-
強制縮進
縮進是強制性的語法規(guī)則。通常建議用 4 個空格代替 TAB,好在多數(shù)編輯器都能自動轉(zhuǎn)換。
最大的麻煩就是從網(wǎng)頁拷貝代碼時,縮進丟失導致源碼成了亂碼。解決方法是:
#/usr/bin/env python
# coding=utf-8
__builtins__.end = None # 看這里,看這里……
def test(x):
if x > 0:
print "a"
else:
print "b"
end
end
def main():
print "世界末日!" # 再次鄙視瑪雅人!(*_*)
end
if __name__ == "__main__":
main()
只要找到 end,就能確定 code block 的縮進范圍了。
注釋
注釋從 # 開始,到行尾結(jié)束,不支持跨行。大段的描述可以用 """doc"""。
語句
可以用 ";" 將多條語句寫在同一行,或者用 "\" 將一條語句拆分成多行。
>>> d = {}; d["a"] = 1; d.items()
[('a', 1)]
>>> for k, v in \
... d.items():
... print k, v
a 1
某些 ()、[]、{} 表達式無需 "\" 就可寫成多行。
>>> d = {
... "a": 1,
... "b": 2
... }
>>> d.pop("a",
... 2)
1
幫助
可以非常方便地為函數(shù)、模塊和類添加幫助信息。
>>> def test():
... """
... func help
... """
... pass
>>> test.__doc__
'\n func help\n '
>>> class User(object):
... """User Model"""
...
... def __init__(self):
... """user.__init__"""
... pass
>>> User.__doc__
'User Model'
>>> User.__init__.__doc__
'user.__init__'
在 shell 用 help() 查看幫助信息,它會合并對象所有成員的幫助內(nèi)容。
命名規(guī)則不算復(fù)雜,只不過涉及私有成員命名時有點講究。
>>> s = set("abc")
>>> s.pop()
'a'
>>> _
'a'
保留字 (包括 Python 3):
False class finally is return
None continue for lambda try
True def from nolcoal while
and del global not with
as elif if or yield
assert else import pass
break except in raise
除非在函數(shù)中使用關(guān)鍵字 global、nolocal 指明外部名字,否則賦值語句總是在當前名字空間創(chuàng)建或修改 {name:object} 關(guān)聯(lián)。
與 C 以 block 為隔離,能在函數(shù)中創(chuàng)建多個同名變量不同,Python 函數(shù)所有代碼共享同一名字空間,會出現(xiàn)下面這樣的狀況。
>>> def test():
... while True:
... x = 10
... break
... print locals()
... print x # 這個寫法在 C 里面會報錯。
>>> test()
{'x': 10}
10
支持用序列類型或迭代器對多個名字同時賦值。
>>> a, b = "a", "b"
>>> a, b = "ab"
>>> a, b = [1, 2]
>>> a, b = xrange(2)
一旦值多過名字數(shù)量,會引發(fā)異常。要么切片,要么用 "_" 補位。
>>> a, b = "abc"
Traceback (most recent call last):
a, b = "abc"
ValueError: too many values to unpack
>>> a, b, _ = "abc"
>>> a, b = "abc"[:2]
Python 3 對此提供了更好的支持。
Python 3.3.0 (default, Nov 4 2012, 20:26:43)
>>> a, *b, c = "a1234c"
>>> a, b, c
('a', ['1', '2', '3', '4'], 'c')
if
只需記住將 "else if" 換成 "elif" 即可。
>>> x = 10
>>> if x > 0:
... print "+"
... elif x < 0:
... print "-"
... else:
... print "0"
+
可以改造得簡單一些。
>>> x = 1
>>> print "+" if x > 0 else ("-" if x < 0 else "0")
+
>>> x = 0
>>> print "+" if x > 0 else ("-" if x < 0 else "0")
0
>>> x = -1
>>> print "+" if x > 0 else ("-" if x < 0 else "0")
-
或者利用 and、or 條件短路,寫得更簡潔些。
>>> x = 1
>>> print (x > 0 and "+") or (x < 0 and "-") or "0"
+
>>> x = 0
>>> print (x > 0 and "+") or (x < 0 and "-") or "0"
0
>>> x = -1
>>> print (x > 0 and "+") or (x < 0 and "-") or "0"
-
可以將兩次比較合并成一個表達式。
>>> x = 10
>>> if (5 < x <= 10): print "haha"
haha!
條件表達式不能包含賦值語句,習慣此種寫法的需要調(diào)整一下了。
>>> if (x = 1) > 0: pass
File "<ipython-input-4-bc2d73931d91>", line 1
if (x = 1) > 0: pass
^
SyntaxError: invalid syntax
while
比我們熟悉的 while 多了個可選的 else 分支。如果循環(huán)沒有被中斷,那么 else 就會執(zhí)行。
>>> x = 3
>>> while x > 0:
... x -= 1
... else:
... print "over"
over!
>>> while True:
... x += 1
... if x > 3: break
... else:
... print "over"
利用 else 分支標記循環(huán)邏輯被完整處理是個不錯的主意。
for
更名為 foreach 可能更合適一些,用來循環(huán)處理序列和迭代器對象。
>>> for i in xrange(3): print i
0
1
2
>>> for k, v in {"a":1, "b":2}.items(): print k, v # 多變量賦值
a 1
b 2
>>> d = ((1, ["a", "b"]), (2, ["x", "y"]))
>>> for i, (c1, c2) in d: # 多層展開
... print i, c1, c2
1 a b
2 x y
同樣有個可選的 else 分支。
>>> for x in xrange(3):
... print x
... else:
... print "over"
0
1
2
over!
>>> for x in xrange(3):
... print x
... if x > 1: break
... else:
... print "over"
0
1
2
要實現(xiàn)傳統(tǒng)的 for 循環(huán),需要借助 enumerate() 返回序號。
>>> for i, c in enumerate("abc"):
... print "s[{0}] = {1}".format(i, c)
s[0] = a
s[1] = b
s[2] = c
pass
占位符,用來標記空代碼塊。
>>> def test():
... pass
>>> class User(object):
... pass
break / continue
break 中斷循環(huán),continue 開始下一次循環(huán)。
沒有 goto、label,也無法用 break、continue 跳出多層嵌套循環(huán)。
>>> while True:
... while True:
... flag = True
... break
... if "flag" in locals(): break
如果嫌 "跳出標記" 不好看,可以考慮拋出異常。
>>> class BreakException(Exception): pass
>>> try:
... while True:
... while True:
... raise BreakException()
... except BreakException:
... print "越獄成功"
其實也沒好看到哪去,但好歹保持內(nèi)部邏輯的干凈。
del
可刪除名字、序列元素、字典鍵值,以及對象成員。
>>> x = 1
>>> "x" in globals()
True
>>> del x
>>> "x" in globals()
False
>>> x = range(10)
>>> del x[1]
>>> x
[0, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x = range(10)
>>> del x[1:5] # 按切片刪除
>>> x
[0, 5, 6, 7, 8, 9]
>>> d = {"a":1, "b":2}
>>> del d["a"] # key 不存在時,會拋出異常。
>>> d
{'b': 2}
>>> class User(object): pass
>>> o = User()
>>> o.name = "user1"
>>> hasattr(o, "name")
True
>>> del o.name
>>> hasattr(o, "name")
False
Generator
用一種優(yōu)雅的方式創(chuàng)建列表、字典或集合。
>>> [x for x in range(10)] # 列表
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> {x for x in range(10)} # 集合
set([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> {c:ord(c) for c in "abc"} # 字典
{'a': 97, 'c': 99, 'b': 98}
>>> (x for x in range(10))
<generator object <genexpr> at 0x10328a690>
可帶上條件進行過濾。
>>> [x for x in range(10) if x % 2]
[1, 3, 5, 7, 9]
或用多個 for 子句實現(xiàn)嵌套。
>>> ["{0}{1}".format(c, x) for c in "abc" for x in range(3)]
['a0', 'a1', 'a2', 'b0', 'b1', 'b2', 'c0', 'c1', 'c2']
這相當于:
>>> n = []
>>> for c in "abc":
... for x in range(3):
... n.append("{0}{1}".format(c, x))
每個子句都可有條件表達式,內(nèi)層可引用外層名字。
>>> ["{0}{1}".format(c, x) \
... for c in "aBcD" if c.isupper() \
... for x in range(5) if x % 2 \
... ]
['B1', 'B3', 'D1', 'D3']
甚至可直接用做函數(shù)實參。
>>> def test(it):
... for i, x in enumerate(it):
... print "{0} = {1}".format(i, x)
>>> test(hex(x) for x in range(3))
0 = 0x0
1 = 0x1
2 = 0x2
這東西沒啥好說的,只要記得沒 "++"、"--" 就行。
http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/1.png" alt="" />
http://wiki.jikexueyuan.com/project/the-python-study-notes-second-edition/images/2.png" alt="" />
切片
序列類型支持 "切片 (slice)" 操作,可通過兩個索引序號獲取片段。
>>> x = range(10)
>>> x[2:6] # [2, 6)
[2, 3, 4, 5]
>>> x[2:-2] # [2, len(x) - 2)
[2, 3, 4, 5, 6, 7]
支持大于 1 的步進。
>>> x[2:6:2]
[2, 4]
可以忽略起始或結(jié)束序號。
>>> x[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x[:6]
[0, 1, 2, 3, 4, 5]
>>> x[7:]
[7, 8, 9]
支持倒序。
>>> x[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
>>> x[7:3:-2]
[7, 5]
可按切片范圍刪除序列元素。
>>> x = range(10)
>>> del x[4:8]; x
[0, 1, 2, 3, 8, 9]
>>> x = range(10)
>>> del x[::2]; x
[1, 3, 5, 7, 9]
甚至不等長的切片替換。
>>> a = [1, 2, 3]
>>> a[:1] = ["a", "b", "c"]
>>> a
['a', 'b', 'c', 2, 3]
布爾 and 返回短路時的最后一個值,or 返回第一個真值。要是沒短路的話,返回最后一個值。
>>> 1 and 2 # True: 最后一個值
2
>>> 1 and 2 and 0 # False: 最后一個值
0
>>> 1 and 0 and 2 # False: 第一個短路值 0
0
>>> 1 or 0 # True: 第一個真值 1
1
>>> 0 or [] or 1 # True: 第一個真值 1
1
>>> 0 or 1 or ["a"] # True: 第一個真值 1
1
用 and、or 實現(xiàn) "三元表達式 (:)"。
>>> x = 5
>>> print x > 0 and "A" or "B"
A
用 or 提供默認值。
>>> x = 5
>>> y = x or 0
>>> y
5
>>> x = None
>>> y = x or 0
>>> y
0
相等
操作符 "==" 可被重載,不適合用來判斷兩個名字是否指向同一對象。
>>> class User(object):
... def __init__(self, name):
... self.name = name
... def __eq__(self, o):
... if not o or not isinstance(o, User): return False
... return cmp(self.name, o.name) == 0
>>> a, b = User("tom"), User("tom")
>>> a is b # is 總是判斷指針是否相同。
False
>>> a == b # 通過 __eq__ 進行判斷。
True
各種類型和字符串間的轉(zhuǎn)換。
>>> str(123), int('123') # int
>>> bin(17), int('0b10001', 2)
>>> oct(20), int('024', 8)
>>> hex(22), int('0x16', 16)
>>> str(0.9), float("0.9") # float
>>> ord('a'), chr(97), unichr(97) # char
>>> str([0, 1, 2]), eval("[0, 1, 2]") # list
>>> str((0, 1, 2)), eval("(0, 1, 2)") # tuple
>>> str({"a":1, "b":2}), eval("{'a': 1, 'b': 2}") # dict
>>> str({1, 2, 3}), eval("{1, 2, 3}") # set
Python 2.7 可使用 print 表達式,Python 3 就只能用函數(shù)了。
>>> import sys
>>> print >> sys.stderr, "Error", 456
Error 456
>>> from __future__ import print_function
>>> print("Hello", "World", sep = ",", end = "\r\n", file = sys.stdout)
Hello,World
用標準庫中的 pprint.pprint() 代替 print,能看到更漂亮的輸出結(jié)果。要輸出到 /dev/null,可以使用 open(os.devnull, "w")。
input
input 會將輸入的字符串進行 eval 處理,raw_input 直接返回用戶輸入的原始字符串。
>>> input("$ ")
$ 1+2+3
6
>>> raw_input("$ ")
$ 1+2+3
'1+2+3'
Python 3 已經(jīng)將 raw_input 重命名為 input。
用標準庫 getpass 輸入密碼。
>>> from getpass import getpass, getuser
>>> pwd = getpass("%s password: " % getuser())
yuhen password:
>>> pwd
'123456'
exit
exit([status]) 調(diào)用所有退出函數(shù)后終止進程,并返回 ExitCode。
$ cat main.py
#/usr/bin/env python
#coding=utf-8
import atexit
def clean():
print "clean..."
def main():
atexit.register(clean)
exit("Failure")
if __name__ == "__main__":
main()
$ ./main.py
Failure
clean...
$ echo $
1
sys.exit() 和 exit() 完全相同。os._exit() 直接終止進程,不調(diào)用退出函數(shù),且退出碼必須是數(shù)字。
vars
獲取 locals 或指定對象的名字空間。
vars() is locals() True
import sys
vars(sys) is sys.dict True
dir
獲取 locals 名字空間中的所有名字,或指定對象所有可訪問成員 (包括基類)。
>>> set(locals().keys()) == set(dir())
True