很难从所有可用模块中挑选出最有用的部分,因此本节将仅关注其实用功能的一小部分。希望这些对您在编码面试中有用,并激发您更多地了解这些模块和其他模块的高级功能的兴趣。
处理丢失的字典键collections.defaultdict()
.get()
和.setdefault()
当您为单个键设置默认值时效果很好,但通常需要为所有可能未设置的键设置默认值,尤其是在编码面试环境中编程时。
假设您有一群学生,您需要跟踪他们的家庭作业成绩。输入值是具有以下格式的元组列表(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
{'elliot': [91, 88], 'neelam': [98], 'bianca': [81]}
在这种方法中,您迭代学生并检查他们的名字是否已经是字典中的属性。如果没有,请将它们添加到字典中,并使用空列表作为默认值。然后是你附加他们的实际成绩到该学生的成绩列表。
但有一种更干净的方法,使用defaultdict
,它扩展了标准dict
功能允许您设置默认值,如果密钥不存在,将对其进行操作:
>>>>>> from collections import defaultdict
>>> student_grades = defaultdict(list)
>>> for name, grade in grades:
... student_grades[name].append(grade)
在本例中,您正在创建一个defaultdict
使用的是list()
不带参数的构造函数作为默认工厂方法。list()
不带参数返回一个空列表,所以defaultdict
来电list()
如果名称不存在,则允许附加成绩。如果您想变得更奇特,您还可以使用 lambda 函数作为工厂值来返回任意常量。
利用一个defaultdict
可以导致更干净的应用程序代码,因为您不必担心键级别的默认值。相反,您可以在defaultdict
级别,然后就好像密钥始终存在一样。有关此技术的更多信息,请查看使用 Python defaultdict 类型处理丢失的键.
计算可哈希对象的数量collections.Counter
您有一长串没有标点符号或大写字母的单词,并且您想要计算每个单词出现的次数。
你可以使用字典或defaultdict
并增加计数,但是collections.Counter
提供了一种更干净、更方便的方法来做到这一点。计数器是一个子类dict
使用0
作为任何缺失元素的默认值,并且可以更轻松地计算对象的出现次数:
>>>>>> from collections import Counter
>>> words = "if there was there was but if \
... there was not there was not".split()
>>> counts = Counter(words)
>>> counts
Counter({'if': 2, 'there': 4, 'was': 4, 'not': 2, 'but': 1})
当您将单词列表传递给Counter
,它存储每个单词以及该单词在列表中出现的次数。
您是否好奇最常见的两个词是什么?只需使用.most_common()
:
>>>>>> counts.most_common(2)
[('there', 4), ('was', 4)]
.most_common()
是一种方便的方法,只需返回n
最常见的输入(按计数)。
访问通用字符串组:string
常数
现在是问答时间!是'A' > 'a'
对或错?
这是错误的,因为 ASCII 代码为A
是 65,但是a
是97,并且65不大于97。
为什么答案很重要?因为如果你想检查一个字符是否是英文字母的一部分,一种流行的方法是查看它是否介于A
和z
(ASCII 图表上的 65 和 122)。
检查 ASCII 代码是可行的,但在编码面试中很笨拙且容易搞砸,尤其是当您不记得小写还是大写 ASCII 字符在前时。使用定义为一部分的常量要容易得多字符串模块.
您可以在其中看到一个正在使用的is_upper()
,它返回字符串中的所有字符是否都是大写字母:
>>>>>> import string
>>> def is_upper(word):
... for letter in word:
... if letter not in string.ascii_uppercase:
... return False
... return True
...
>>> is_upper('Thanks Geir')
False
>>> is_upper('LOL')
True
is_upper()
迭代中的字母word
,并检查字母是否是string.ascii_uppercase
。如果打印出来string.ascii_uppercase
你会发现它只是一根低级的字符串。该值设置为文字'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
.
全部string
常量只是经常引用的字符串值的字符串。它们包括以下内容:
string.ascii_letters
string.ascii_uppercase
string.ascii_lowercase
string.digits
string.hexdigits
string.octdigits
string.punctuation
string.printable
string.whitespace
这些更容易使用,更重要的是,更容易阅读。
面试官喜欢提供现实生活中的场景,让编码面试看起来不那么令人生畏,所以这里有一个人为的例子:你去了一个游乐园,决定找出每一对可以一起坐在过山车上的朋友。
除非生成这些对是面试问题的主要目的,否则生成所有可能的对很可能只是实现可行算法的一个乏味的步骤。您可以使用嵌套自行计算它们for
循环,或者你可以使用强大的迭代工具库.
itertools
有多种工具用于生成可迭代的输入数据序列,但现在我们只关注两个常见函数:itertools.permutations()
和itertools.combinations()
.
itertools.permutations()
构建所有排列的列表,这意味着它是每个可能的输入值分组的列表,其长度与count
范围。这r
关键字参数让我们指定每个分组中有多少个值:
>>>>>> import itertools
>>> friends = ['Monique', 'Ashish', 'Devon', 'Bernie']
>>> list(itertools.permutations(friends, r=2))
[('Monique', 'Ashish'), ('Monique', 'Devon'), ('Monique', 'Bernie'),
('Ashish', 'Monique'), ('Ashish', 'Devon'), ('Ashish', 'Bernie'),
('Devon', 'Monique'), ('Devon', 'Ashish'), ('Devon', 'Bernie'),
('Bernie', 'Monique'), ('Bernie', 'Ashish'), ('Bernie', 'Devon')]
对于排列,元素的顺序很重要,所以('sam', 'devon')
代表不同的配对('devon', 'sam')
,这意味着它们都将包含在列表中。
itertools.combinations()
构建组合。这些也是输入值的可能分组,但现在值的顺序并不重要。因为('sam', 'devon')
和('devon', 'sam')
代表同一对,只有其中一个会包含在输出列表中:
>>>>>> list(itertools.combinations(friends, r=2))
[('Monique', 'Ashish'), ('Monique', 'Devon'), ('Monique', 'Bernie'),
('Ashish', 'Devon'), ('Ashish', 'Bernie'), ('Devon', 'Bernie')]
由于值的顺序与组合无关,因此对于同一输入列表,组合的数量少于排列的数量。再次,因为我们设置r
到2,每个分组有两个名字。
.combinations()
和.permutations()
只是强大库的小例子,但当您尝试快速解决算法问题时,即使这两个函数也非常有用。