[Effective Python] 第1章 用Pythonic方式來思考

# -*- coding: utf-8 -*-

import os

# 第一章 用Pythonic方式來思考

#--------------------------------------
# 第1條:確認自己所使用的Python版本
# import this

#--------------------------------------
# 第2條:遵循PEP 8風格指南

# if not a is b		壞
# if a is not b		好

# if len(somelist) == 0		壞
# if not somelist			好

# 不要編寫單行的if語句、for循環、while循環、except複合語句

# import 放在文件開頭

# 引入模塊時,使用絕對名稱

# import語句分爲三個部分:標準庫模塊、第三方模塊、自用模塊,每部分中,按照模塊的字母順序排列

#--------------------------------------
# 第3條:瞭解bytes、str與unicode的區別

# Python3: bytes, str

# Python2: str, unicode

def to_unicode(unicode_or_str):
	if isinstance(unicode_or_str, str):
		value = unicode_or_str.decode("utf-8")
	else:
		value = unicode_or_str
	return value	# Instance of unicode

def to_str(unicode_or_str):
	if isinstance(unicode_or_str, unicode):
		value = unicode_or_str.encode("utf-8")
	else:
		value = unicode_or_str
	return value	# Instance of str

# 注:Python2 中,若str只包含7位ASCII字符,則unicode和str實例似乎就成了同一種類型
# 可以用+號連接這種str與unicode
# 可以用等價與不都能加操作符
# 可以用'%s'等形式來代表unicode實例

with open("random.bin", "w") as f:
	f.write(os.urandom(10))

#--------------------------------------
# 第4條:用輔助函數來取代複雜的表達式

def get_first_int(values, key, default=0):
	found = values.get(key, [""])
	if found[0]:
		found = int(found[0])
	else:
		found = default
	return found

#--------------------------------------
# 第5條:瞭解切割序列的辦法

a = [1,2,3]
b = a[:]
assert b == a and b is not a	# 原列表的拷貝

b = a
print "Before {}".format(a)
a[:] = [101, 102, 103]
assert a is b				# Still the same list object
print "After {}".format(a)	# Now has different contents

#--------------------------------------
# 第6條:在單次切片操作內,不要同時指定start、end和stride

a =  ["red", "orange", "yellow", "green", "blue", "purple"]
odds = a[::2]
evens = a[1::2]
print odds
print evens

x = "mongoose"	# 翻轉字符串,對字符串和ASCII字符有用,
y = x[::-1]
print y

w = u"謝謝"		# 但對於已經編碼成UTF-8字符串的Unicode字符無效
x = w.encode("utf-8")
y = x[::-1]
# z = y.decode("utf-8")	# UnicodeDecodeError

a = ["a", "b", "c", "d", "e", "f", "g", "h"]	# 拆解爲兩條賦值語句,一條範圍切割,一條步進操作
b = a[::2]
c = b[1:-1]
print c

#--------------------------------------
# 第7條:用列表推到來取代map和filter(更清晰,無需編寫lambda表達式)

a = [1,2,3,4,5,6,7,8,9,10]
squares = [x**2 for x in a]
print squares
squares = map(lambda x: x**2, a)

even_squares = [x**2 for x in a if x % 2 == 0]
print even_squares
alt = map(lambda x: x**2, filter(lambda x: x % 2 == 0, a))
assert even_squares == list(alt)

# dict和set也有列表類似的推到機制

chile_ranks = {"ghost": 1, "habanero": 2, "cayenne": 3}
rank_dict = {rank: name for name, rank in chile_ranks.iteritems()}
print rank_dict
chile_len_set = {len(name) for name in rank_dict.itervalues()}
print chile_len_set
# {1: 'ghost', 2: 'habanero', 3: 'cayenne'}
# set([8, 5, 7])

#--------------------------------------
# 第8條:不要使用含有兩個以上表達式的列表推導
# 可以使用兩個條件、兩個循環、或一個條件搭配一個循環

matrix = [[1,2,3],[4,5,6],[7,8,9]]
flat = [x for row in matrix for x in row]			# 多重循環的用法
print flat
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

squared = [[x**2 for x in row] for row in matrix]	# 多重循環的用法
print squared

a = [1,2,3,4,5,6,7,8,9,10]
b = [x for x in a if x > 4 if x % 2 == 0]
c = [x for x in a if x > 4 and x % 2 == 0]
print b
assert b == c

matrix = [[1,2,3],[4,5,6],[7,8,9]]
filtered = [[x for x in row if x % 3 == 0]			# 晦澀難懂
			for row in matrix if sum(row) >= 10]
print filtered

#--------------------------------------
# 第9條:用生成器表達式來改寫數據量較大的列表推導

value = [len(x) for x in open("hello.txt")]
print value

it = (len(x) for x in open("hello.txt"))	# 返回一個生成器
print it
print next(it)	# 獲取下一個值,不用擔心內存激增
print next(it)

# 生成器表達式可以相互組合
# 外圍的迭代器每次前進時,會推動內部的迭代器,產生連鎖效應
# 使得執行循環、評估條件表達式、對接輸入和輸出等邏輯都組合在了一起

roots = ((x, x**0.5) for x in it)
print next(roots)

#--------------------------------------
# 第10條:儘量用enumerate取代range
# enumerate將各種迭代器包裝爲生成器,以便稍後產生輸出值

fruit_list = ["apple", "banana", "orange"]

# for i in range(len(fruit_list)):
# 	print "%d: %s" % (i+1, fruit_list[i])

for i, fruit in enumerate(fruit_list):	# 簡潔、清爽
	print "%d: %s" % (i+1, fruit)

#--------------------------------------
# 第11條:用zip函數同時遍歷兩個迭代器

# 例子,獲取列表中最長字符串
names = ["Lebron", "Bryant", "Wade", "HongfeiXu"]
letters = [len(n) for n in names]

longest_name = None
max_letters = 0
for i in range(len(names)):		# 同時遍歷兩個列表看起來很混亂
	count = letters[i]
	if count > max_letters:
		longest_name = names[i]
		max_letters = count
print longest_name

longest_name = None
max_letters = 0
for name, count in zip(names, letters):	# 清爽很多
	if count > max_letters:
		longest_name = name
		max_letters = count
print longest_name

# 注1:Python2中zip並不是生成器,會將提供的迭代器全部平行的遍歷一次,將完整的遠足列表返回給調用者,可能會帶來內存劇增
# 如果要遍歷數據量較大的迭代器,應該使用itertools內置模塊中的izip,見第46條

# 注2:如果輸入的迭代器長度不同,則當一個耗盡,zip就不再產生元組了
# 如果不能確定zip所封裝的列表是否等長,則可考慮使用itertools中的zip_longest函數,Python2中叫做izip_longest

#--------------------------------------
# 第12條:不要在for和while循環後面寫else塊

a = 4
b = 9
for i in range(2, min(a, b) + 1):
	print "Testing %d" % i
	if a % 2 == 0 and b % i == 0:
		print "Not coprime"
		break
else:
	print "Coprime"

def coprime(a, b):
	for i in range(2, min(a, b) + 1):
		if a % 2 == 0 and b % i == 0:
			return False
	return True

#--------------------------------------
# 第13條:合理利用try/except/else/finally結構中的每個代碼塊

# 1. finally塊
# 既要將異常向上傳播,又要在異常發生時執行清理工作,則使用try/finally結構
# 常見用途,確保程序能夠可靠地關閉文件句柄

handle = open("hello.txt")	# May raise IOError
try:
	data = handle.read()	# May rasie UnicodeDecodeError
finally:
	handle.close()			# Always runs after try:

# 2. else塊
# try/except/else結構可以清晰地描述出哪些異常會由自己的代碼來處理、哪些異常會傳播到上一級
# 如果try沒有發生異常,那麼就執行else塊
# else塊可以儘量縮減try塊內的代碼量

import json
def load_json_key(data, key):
	try:
		result_dict = json.loads(data)	# May raise ValueError
	except ValueError as e:	# 捕獲處理 ValueError
		raise KeyError(e)
	else:
		return result_dict[key]			# May raise KeyError,若有異常,則向上傳播

# 3. 混合使用

UNDEFINE = object()

def divide_json(path):
	handle = open(path, "r+")		# May raise IOError
	try:
		data = handle.read()		# May raise UnicodeDecodeError
		op = json.loads(data)		# May raise ValueError
		value = (
			op["numerator"]/
			op["denominator"])		# May raise ZeroDivisionError
	except ZeroDivisionError as e:
		return UNDEFINE
	else:
		op["result"] = value
		result = json.dumps(op)
		handle.seek(0)
		handle.write(result)		# May raise IOError
		return value
	finally:
		handle.close()				# Always runs
























發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章