1:選擇正確的內置函數
Python有一個大型標準庫,但只有一個內置函數的小型庫,這些函數總是可用的,不需要導入。它們每一個都值得我們仔細研究,尤其是在其中一些函數的情況下,可以用什麼替代更好
1.1 使用enumerate()而不是range()進行迭代
有一個元素列表,您需要遍歷列表,同時訪問索引和值。
有一個名爲FizzBuzz的經典編碼面試問題可以通過迭代索引和值來解決。在FizzBuzz中,你將獲得一個整數列表,任務是執行以下操作:
(1)用“fizz”替換所有可被3整除的整數
(2)用“buzz”替換所有可被5整除的整數
(3)將所有可被3和5整除的整數替換爲“fizzbuzz”
# 普通用法,使用range()解決此問題
numbers = [45, 22, 14, 65, 97, 72]
for i in range(len(numbers)):
if numbers[i] % 3 == 0 and numbers[i] % 5 == 0:
numbers[i] = 'fizzbuzz'
elif numbers[i] % 3 == 0:
numbers[i] = 'fizz'
elif numbers[i] % 5 == 0:
numbers[i] = 'buzz'
numbers
運行結果:
Range允許你通過索引訪問數字元素,並且對於某些特殊情況也是一個很有用的工具。但在這種情況下,我們希望同時獲取每個元素的索引和值,更優雅的解決方案使用enumerate():
numbers = [45, 22, 14, 65, 97, 72]
for i,num in enumerate(numbers):
if num % 3 == 0 and num % 5 == 0:
numbers[i] = 'fizzbuzz'
elif num % 3 == 0:
numbers[i] = 'fizz'
elif num % 5 == 0:
numbers[i] = 'buzz'
numbers
運行結果:
此外,使用內置函數enumerate():
對於每個元素,enumerate()返回一個計數器和元素值。計數器默認爲0,也是元素的索引。不想在0開始你的計數?只需使用可選的start參數來設置偏移量
numbers = [45, 22, 14, 65, 97, 72]
for i,num in enumerate(numbers,start = 50):
print(i,num)
運行結果:
我們可以看到,通過使用start參數,我們訪問所有相同的元素,從第一個索引開始,但現在我們的計數從指定的整數值開始。
1.2 使用遞推式構造列表而不是map()和filter()
讓我們首先看看我們如何構造對map()的調用以及等效的遞推構造列表:
numbers= [3, 2, 5, 1, 10, 7]
def square(x):
return x*x
print(list(map(square,numbers)))
print([square(x) for x in numbers])
運行結果:
使用map()和列表推導的兩種方法都返回相同的值,但列表推導更容易閱讀和理解
numbers= [3, 2, 5, 1, 10, 7]
def is_old(x):
return bool(x % 2)
print(list(map(is_old,numbers))) # bool()和bool(0) 返回False,其他爲True
print(list(filter(is_old,numbers)))
print([x for x in numbers if is_old(x)])
print([square(x) for x in numbers if is_old(x)]) # 結合使用
運行結果:
就像在map中看到的那樣,filter和列表推導方法返回相同的值,但列表推導更容易理解。
1.3 使用f-Strings格式化字符串
Python有很多不同的方法來處理字符串格式化,有時候不知道使用哪個,f-strings支持使用字符串格式化迷你語言,以及強大的字符串插值。這些功能允許添加變量甚至有效的Python表達式,並在添加到字符串之前在運行時對它們進行評估。
def get_name_and_decades(name, age):
return f"My name is {name} and I'm {age} years old."
print(get_name_and_decades("Maria", 31))
運行結果:
1.4 使用sorted()對複雜列表進行排序
大量的編碼面試問題需要進行某種排序,並且有多種有效的方法可以進行排序。除非你需要實現自己的排序算法,否則通常最好使用sorted()。 你可能已經看到了排序的最簡單用法,例如按升序或降序排序數字或字符串列表:
默認情況下,sorted()已按升序對輸入進行排序,而reverse關鍵字參數則按降序排序
sorted([6,5,3,7,2,4,1])
# 輸出結果:[1, 2, 3, 4, 5, 6, 7]
sorted(['cat', 'dog', 'cheetah', 'rhino', 'bear'], reverse=True)
# 輸出結果:['rhino', 'dog', 'cheetah', 'cat', 'bear']
值得了解的是可選關鍵字key,它允許你在排序之前指定將在每個元素上調用的函數。添加函數允許自定義排序規則,如果要對更復雜的數據類型進行排序,這些規則特別有用。
animals = [{'type': 'penguin', 'name': 'Stephanie', 'age': 8},
{'type': 'elephant', 'name': 'Devon', 'age': 3},
{'type': 'puma', 'name': 'Moe', 'age': 5},]
sorted(animals, key=lambda animal: animal['age'])
運行結果:
通過傳入一個返回每個元素年齡的lambda函數,可以輕鬆地按每個字典的單個值對字典列表進行排序。在這種情況下,字典現在按年齡按升序排序。
1.5 有效利用數據結構
選擇正確的數據結構會對性能產生重大影響。除了理論數據結構之外,Python還在其標準數據結構實現中內置了強大而方便的功能
- 1:使用set存儲唯一值
如果需要從現有數據集中刪除重複元素。新的開發人員有時會在列表應該使用集合時執行此操作,這會強制執行所有元素的唯一性。
import random
all_words = "all the words in the world".split()
def get_random_word(all_words):
return random.choice(all_words)
get_random_word(all_words)
隨機選擇的結果:
應該重複調用get_random_word()以獲取1000個隨機單詞,然後返回包含每個唯一單詞的數據結構。以下是兩種常見的次優方法和一種好的方法。
# 糟糕的方法
def get_unique_words():
words = []
for i in range(1000):
word = get_random_word()
if word not in words:
words.append(word)
return words
這種方法很糟糕,因爲必須將每個新單詞與列表中已有的每個單詞進行比較。這意味着隨着單詞數量的增加,查找次數呈二次方式增長。換句話說,時間複雜度在O(N^2)的量級上增長。
換一種更好的方法:
def get_unique_words():
words = set()
for i in range(1000):
words.add(get_random_word())
return words
那麼爲什麼使用與第二種方法不同的集合呢? 它們是不同的,因爲集合存儲元素的方式允許接近恆定時間檢查值是否在集合中,而不像需要線性時間查找的列表。查找時間的差異意味着添加到集合的時間複雜度以O(N)的速率增長,這在大多數情況下比第二種方法的O(N^2)好得多。
- 2:使用生成器節省內存
前面提到,列表推導是方便的工具,但有時會導致不必要的內存使用。
比如:找到前1000個正方形的總和,從1開始
res = sum([i * i for i in range(1,1001)])
print(res) # 輸出結果:333833500
很快將結果輸出,但是,這裏發生了什麼? 它正在列出你要求的每個完美的方塊,並將它們全部加起來。 具有1000個完美正方形的列表在計算機術語中可能不會很大,但是1億或10億是相當多的信息,並且很容易佔用計算機的可用內存資源。 有一種解決內存問題的快捷方法:只需用括號替換方括號。
sum((i * i for i in range(1, 1001)))
# 輸出結果:333833500
生成器表達式並不真正的創建數字列表,而是返回一個生成器對象,此對象在每次計算出一個條目後,把這個條目"產生"(yield)出來。生成器表達式使用了"惰性計算"或稱作"延時求值"的機制。 序列過長,並且每次只需要獲取一個元素時,應該考慮生成器表達式而不是列表解析。
因此,當sum通過重複調用. next ()來迭代生成器對象時,生成器檢查i 等於多少,計算i * i,在內部遞增i,並將正確的值返回到sum。該設計允許生成器用於大量數據序列,因爲一次只有一個元素存在於內存中
- 3:使用.get()和.setdefault()在字典中定義默認值
最常見的編程任務之一涉及添加,修改或檢索可能在字典中或可能不在字典中的項。Python字典具有優雅的功能,可以使這些任務簡潔明瞭。
# 假設,我們要找出cowboy字典中的name字段對應的值
# 如果存在,則返回相應的值。否則,它返回默認值。
cowboy = {'age': 32, 'horse': 'mustang', 'hat_size': 'large'}
if 'name' in cowboy:
name = cowboy['name']
else:
name = 'The Man with No Name'
name
運行結果:
雖然上述方法可以清楚地檢查key確實有效,但如果使用.get(),它可以很容易地用一行代替
Python 字典(Dictionary) get() 函數返回指定鍵的值,如果值不在字典中返回默認值
name = cowboy.get('name','The Man with No Name')
name
輸出結果:
2 利用Python的標準庫
2.1 使用collections.defaultdict()處理缺少的字典鍵
假設你有一羣學生,你需要記錄他們在家庭作業上的成績。輸入值是具有格式(student_name,grade)的元組列表,但是你希望輕鬆查找單個學生的所有成績而無需迭代列表。
student_grades = {}
grades = [('elliot', 91),
('neelam', 98),
('bianca', 81),
('elliot', 88),]
for name,grade in grades:
if name not in student_grades:
student_grades[name] = []
student_grades[name].append(grade)
student_grades
輸出結果:
其實還有一個更簡潔的方法,可以使用defaultdict,它擴展了標準的dict功能,允許你設置一個默認值,如果key不存在,它將按默認值操作:
from collections import defaultdict
student_grades = defaultdict(list)
for name,grade in grades:
student_grades[name].append(grade)
student_grades
2.2 使用collections.Counter計算Hashable對象
假如有一長串沒有標點符號或大寫字母的單詞,想要計算每個單詞出現的次數,可以使用字典或defaultdict增加計數,但collections.Counter提供了一種更清晰,更方便的方法。Counter是dict的子類,它使用0作爲任何缺失元素的默認值,並且更容易計算對象的出現次數:
# 當你將單詞列表傳遞給Counter時,它會存儲每個單詞以及該單詞在列表中出現的次數
from collections import Counter
words = "if there was there was but if there was not there was not".split()
counts = Counter(words)
counts
運行結果:
如果需要找出兩個最常見的詞是什麼,只需使用.most_common()
counts.most_common(2)
輸出結果:
2.3 使用字符串常量訪問公共字符串組
檢查字母是否都是大寫字母
import string
def is_upper(word):
for letter in word:
if letter not in string.ascii_uppercase:
return False
return True
print(is_upper('Thanks Sir'))
print(is_upper('LOL'))
運行結果:
2.4 使用Itertools生成排列和組合¶
對於排列,元素的順序很重要,因此(“sam”、“devon”)表示與(“devon”、“sam”)不同的配對,這意味着它們都將包含在列表中。
import itertools
friends = ['BeiJing', 'ShangHai', 'ChongQing', 'GuangZhou']
list(itertools.permutations(friends, r=2))
運行結果:
itertools.combinations()生成組合。這些也是輸入值的可能分組,但現在值的順序無關緊要。因爲(‘sam’、‘devon’)和(‘devon’、‘sam’)代表同一對,所以輸出列表中只會包含它們中的一個。
list(itertools.combinations(friends, r=2))
運行結果: