挑战
作为粗略数量级,吉尔斯托马斯(联合创始人) pythonanywhere)估计 世界上有1.8至430万个Python开发人员.
那么找到一个Python开发人员,它有多难?好吧,如果目标只是找到可以合法列出Python在他们的简历上的人,那就不是很难。但如果目标是找到一个真正掌握语言细微差别和力量的Python Guru,那么挑战是最肯定的。
首先,需要一种高效的招聘过程,如我们帖子所述 寻找精英少数 - 寻找和雇用该行业中最好的开发商。然后可以用目标问题和技术来增强这样的过程,例如这里提供的那些,它专门针对从多级 - 蟒蛇体验候选的多级的Python Virtuosos进行了辅助。
Python Guru或蛇在草地上?

所以你发现了什么似乎是一个强大的Python Web开发人员。您如何确定他或她是否是您正在寻求雇用的精英前1%的候选人?虽然没有魔法或万无一失的技术,但肯定有可能造成的,这将有助于确定候选人对语言知识的深度和复杂程度。简要采样这些问题如下。
但是,必须牢记这些样本问题是必不可少的。不是每个值得招聘的“A”候选人将能够正确地回答他们所有人,也没有回答他们所有人都保证了“A”候选人。在一天结束时,招聘仍然是一种艺术,因为它是一种科学。
杂草中的Python ......
虽然最好的开发人员不会浪费时间融资到内存可以轻松找到的内存,但是任何专家可以的任何编程语言都有一些关键特征和功能,应该是应该预期的被精通。 以下是一些Python的示例:
问:为什么要使用功能装饰器?举个例子。
装饰器基本上是一个可调用的python对象,用于修改或扩展函数或类定义。装饰器的一个美女是,单个装饰器定义可以应用于多个函数(或类)。由此可以通过装饰器来实现,否则会要求大量的样品板(甚至更冗余!)代码。 烧瓶例如,使用装饰器作为向Web应用程序添加新终点的机制。装饰器的一些更常见用途的示例包括将同步,型强制,记录或预/帖子条件添加到类或函数。
问:什么是lambda表达式,列表全局和生成表达式?每个优势和适当用途是什么?
lambda表达式 是创建单线,匿名功能的简写技术。他们的简单,内联的性质通常 - 尽管并不总是 - 导致比正式函数声明的替代更具可读和简洁的代码。另一方面,根据定义,他们的内联自然,非常限制他们能够做的事情和适用性。匿名和内联,在代码中多个位置使用相同的Lambda函数的唯一方法是冗余指定它。
列表全身 provide a concise syntax for creating lists. List comprehensions are commonly used to make lists where each element is the result of some operation(s) applied to each member of another sequence or iterable. They can also be used to create a subsequence of those elements whose members satisfy a certain condition. In Python, list comprehensions provide an alternative to using the built-in map()
和 filter()
functions.
As the applied usage of lambda expressions and list comprehensions can overlap, opinions vary widely as to when and where to use one vs. the other. One point to bear in mind, though, is that a list comprehension executes somewhat faster than a comparable solution using map
和 lambda
(some quick tests yielded a performance difference of roughly 10%). This is because calling a lambda function creates a new stack frame while the expression in the list comprehension is evaluated without doing so.
发电机表达式 在语法上且在一起类似于列表的全面,但在两个操作的方式之间存在一些相当显着的差异,因此应该使用时。在一个简单的时刻,迭代发电机表达式或列表理解将基本上做同样的事情,但是列表理解将首先在内存中创建整个列表,而生成器表达式将根据需要创建项目。因此,生成器表达可以用于非常大(甚至无限的)序列,它们的懒惰(即,根据需要)的值产生的值导致性能提高和更低的内存使用率。但是,值得注意的是,标准Python列表方法可以用于列表理解的结果,但不是直接就生成器表达式的结果。
问:考虑下面的两种方法,用于初始化阵列和将导致的阵列。结果数组将如何不同,为什么要使用一个初始化方法与另一个初始化方法?
>>> # INITIALIZING AN ARRAY -- METHOD 1
...
>>> x = [[1,2,3,4]] * 3
>>> x
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
>>>
>>>
>>> # INITIALIZING AN ARRAY -- METHOD 2
...
>>> y = [[1,2,3,4] for _ in range(3)]
>>> y
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
>>>
>>> # WHICH METHOD SHOULD YOU USE AND WHY?
虽然两种方法首先出现刷新以产生相同的结果,但两者之间存在极为显着的差异。方法2生成,正如您所期望的,每个3个元素数组,每个元素本身都是独立的4元元阵列。然而,在方法1中,阵列的成员均指向相同的对象。这可能导致最有可能出于最有可能的意义和不期望的行为,如下所示。
>>> # MODIFYING THE x ARRAY FROM THE PRIOR CODE SNIPPET:
>>> x[0][3] = 99
>>> x
[[1, 2, 3, 99], [1, 2, 3, 99], [1, 2, 3, 99]]
>>> # UH-OH, DON’T THINK YOU WANTED THAT TO HAPPEN!
...
>>>
>>> # MODIFYING THE y ARRAY FROM THE PRIOR CODE SNIPPET:
>>> y[0][3] = 99
>>> y
[[1, 2, 3, 99], [1, 2, 3, 4], [1, 2, 3, 4]]
>>> # THAT’S MORE LIKE WHAT YOU EXPECTED!
...
问:下面的第二个附录()陈述将打印出什么?
>>> def append(list=[]):
... # append the length of a list to the list
... list.append(len(list))
... return list
...
>>> append(['a','b'])
['a', 'b', 2]
>>>
>>> append() # calling with no arg uses default list value of []
[0]
>>>
>>> append() # but what happens when we AGAIN call append with no arg?
当函数参数的默认值是表达式时,表达式仅评估一次,而不是每次调用函数时都会评估一次。因此,一旦列表参数已初始化为空数组,就会在没有指定任何参数的情况下申请的后续调用将继续使用最初初始化列表的相同数组。因此,这将产生以下意外,行为:
>>> append() # first call with no arg uses default list value of []
[0]
>>> append() # but then look what happens...
[0, 1]
>>> append() # successive calls keep extending the same default list!
[0, 1, 2]
>>> append() # and so on, and so on, and so on...
[0, 1, 2, 3]
问:如何修改前一个问题中“附加”方法的实施,以避免那里描述的不良行为?
附加方法的以下替代实施方式是避免答案中描述的不良行为的许多方法之一:
>>> def append(list=None):
... if list is None:
list = []
# append the length of a list to the list
... list.append(len(list))
... return list
...
>>> append()
[0]
>>> append()
[0]
问:如何使用单行Python代码交换两个变量的值?
考虑这个简单的例子:
>>> x = 'X'
>>> y = 'Y'
在许多其他语言中,交换x和y的值要求您执行以下操作:
>>> tmp = x
>>> x = y
>>> y = tmp
>>> x, y
('Y', 'X')
但在Python中,可以使用单行代码进行交换(由于隐式元组包装和解包)如下:
>>> x,y = y,x
>>> x,y
('Y', 'X')
问:下面的最后一个陈述将打印出什么?
>>> flist = []
>>> for i in range(3):
... flist.append(lambda: i)
...
>>> [f() for f in flist] # what will this print out?
在Python中的任何闭合中,变量由名称绑定。因此,上面的代码线将打印出以下内容:
[2, 2, 2]
据推测,上述代码的作者意图是什么!
A 解决方法 是创建一个单独的函数或按名称传递args;例如。:
>>> flist = []
>>> for i in range(3):
... flist.append(lambda i = i : i)
...
>>> [f() for f in flist]
[0, 1, 2]
问:Python 2和3之间的关键差异是什么?
虽然Python 2在这一点上正式考虑了遗留,但它的使用仍然很广泛,这对于开发人员来说重要的是识别Python 2和3之间的差异。
以下是开发人员应该了解的一些关键差异:
- 文本和数据而不是Unicode和8位字符串。 Python 3.0使用文本和(二进制)数据而不是Unicode字符串和8位字符串的概念。其中最大的分支是,任何在Python 3.0中混合文本和数据的尝试都会提出一个TypeError(要安全地组合两个,您必须解码字节或编码Unicode,但您需要知道正确的编码,例如UTF-8)
- 这解决了NaïvePython程序员的长期缺点。在Python 2中,如果字符串遇到仅包含7位(ASCII)字节,则将Unicode和8位数据混合将起作用,但如果它包含非ASCII值,则会获取UnicodedecodeError。此外,异常将在组合点处发生,而不是在非ASCII字符被放入STR对象的点处。这种行为是Neophyte Python程序员的常见困惑和协调的源泉。
- 打印功能。 The
print
statement has been replaced with a print()
function
- xrange - buh-bye。
xrange()
no longer exists (range()
now behaves like xrange()
used to behave, except it works with values of arbitrary size)
- API更改:
zip()
, map()
和 filter()
all now return iterators instead of lists
dict.keys()
, dict.items()
和 dict.values()
now return “views” instead of lists
dict.iterkeys()
, dict.iteritems()
和 dict.itervalues()
are no longer supported
- 比较运算符。 The ordering comparison operators (
<
, <=
, >=
, >
) now raise a TypeError
exception when the operands don’t have a meaningful natural ordering. Some examples of the ramifications of this include:
- Expressions like
1 < ''
, 0 > None
or len <= len
are no longer valid
None < None
now raises a TypeError
instead of returning False
- 对异构名单进行排序不再有意义 - 所有元素必须彼此相媲美
有关Python 2和3差异的更多详细信息 这里.
问:Python解释还是编译?
如上所述 为什么有这么多蟒蛇?,这是坦率地,这是一个戏法问题,因为它是畸形的。 Python本身只不过是界面定义(与任何语言规范一样,如此)有多种实现。因此,解释或编译“Python”是否解释或编译的问题不适用于Python语言本身;相反,它适用于Python规范的每个特定实现。
进一步复杂化这个问题的答案是,在CPython(最常见的Python实现)的情况下,答案确实是“有点”。具体而言,使用cpython,首先编译代码,然后解释。更准确地说,它不是预先编译到本机机代码,而是要传统。虽然机器代码肯定是更快的,但字节码更加便携,并更安全。然后在CPython的情况下解释字节码(或在运行时解释并编译和编译以在运行时优化的机器代码 甲p)。
问:CPython有什么替代实施?你什么时候可以使用它们?
一个更突出的替代实施之一是 jython.,在Java中编写的Python实现,它使用Java虚拟机(JVM)。虽然cpython生成字节码以在CPython VM上运行,但Jython会生成Java字节码以在JVM上运行。
另一个是 ironpython.,用C#编写并定位.NET堆栈。 IronPython在Microsoft的公共语言运行时运行(CLR)。
也据指出 为什么有这么多蟒蛇?,完全可以在没有触摸Python的非CPython实现的情况下生存,但是从切换有优势,其中大部分都取决于您的技术堆栈。
另一个值得注意的替代实施是 甲p 其主要功能包括:
- 速度。 由于其即时(JIT)编译器,Python程序通常在Pypy上运行更快。
- 内存使用情况。 大型内存饥饿的Python计划可能最终占用含量的空间比在CPython中的少于他们。
- 兼容性。 PYPY与现有的Python代码高度兼容。它支持 CFFI. 并且可以像这样运行流行的Python库 扭曲 和 django..
- 沙箱。 PYPY提供了以完全安全的方式运行不受信任的代码的能力。
- 堆积模式。 默认情况下,PYPY默认情况下,支持堆栈模式,为大线程提供巨大并发。
问:您在Python中的单位测试方法是什么?
这个问题周围的最根本的答案在Python附近 单元测试 测试框架。基本上,如果候选人在回答这个问题时候选人没有提及,那应该是一个巨大的红旗。
单元测试支持测试自动化,共享测试和关闭代码,测试,测试集聚到集合,以及从报告框架的测试的独立性。 Unittest Module提供了一组测试的易于支持这些品质的类。
假设候选人确实提到了unittest(如果他们没有,你可能只想结束那么那里的面试!),你还应该要求他们描述毫无可能的框架的关键要素;即,测试夹具,测试用例,测试套件和测试跑步者。
更新的替代框架是 嘲笑。 Mock允许您用模拟对象替换系统的部分,并使断言如何使用它们。 Mock现在是Python标准库的一部分,可在Python 3.3中作为unittest.mock提供。
模拟的价值和力量很好地解释 在Python嘲笑的概论。如在其中,系统调用是嘲笑的主要候选:是否编写脚本来弹出CD驱动器,一个Web服务器从/ tmp删除陈旧的缓存文件,或绑定到TCP端口的套接字服务器,这些都呼叫所有功能不受欢迎在单元测试的背景下副作用。同样,保持您的单位测试的高效和性能意味着保持尽可能多的“慢码”,尽可能远离自动化测试运行,即文件系统和网络访问。
[注意:这个问题是在Java中经历的Python开发人员。]
问:在Python Vs. Java编码时,请记住一些关键差异是什么?
免责声明#1。 Java和Python之间的差异很多,可能是一个值得自己的主题(漫长的)帖子。以下是两种语言之间的一些关键差异的简要采样。
免责声明#2。 这里的意图不是在Python与Java的优点上发射宗教战斗(尽可能多的乐趣!)。相反,这个问题真的只是因为看到开发人员了解两种语言之间存在一些实际差异的问题。因此,下面的列表故意避免讨论从编程生产力的角度来讨论Python对Java的可争解优势。
通过上述两种免责声明,这里是在Python与Java编码时牢记的一些关键差异的抽样:
- 动态VS静态键入。 两种语言之间的最大差异之一是Java仅限于静态键入,而Python支持动态键入变量。
- 静态与类方法。 Java中的静态方法不会转换为Python类方法。
- 在Python中,调用类方法涉及呼叫静态方法或函数的附加内存分配。
- 在Java中,编译器抬头向java(例如,foo.bar.method)被抬头看,所以在运行时,它真的无论你有多少都没关系。但是,在Python中,查找在运行时发生,因此“每个点计数”。
- 方法重载。 虽然Java需要具有不同签名的多个相同命名函数的显式规范,但是在Python中可以通过单个函数来完成,该函数包括如果未由调用者指定的默认值,则包括默认值。
- 单个与双引号。 虽然使用单引号与双引号在Java中具有重要意义,但它们可以在Python中互换使用(但不,它不会允许开始 相同的 用双引号的字符串并尝试用单个报价结束,反之亦然!)。
- Getter和Setter(不是!)。 Python的Getters和Setter是多余的;相反,你应该使用'物业'内置(这就是它的东西!)。在Python中,Getters和Setsers是CPU和程序员时间的浪费。
- 类是可选的。 虽然Java需要在封闭类定义的上下文中定义的每个功能,但Python都没有此类要求。
- 缩进问题...... 在Python。这咬了很多新手Python程序员。
大局
Python的专家知识远远超出了语言的技术细节。 Python专家将深入了解,对Python的福利以及其局限性的深入了解和欣赏。因此,以下是一些示例问题,可以帮助评估候选人专业知识的这一维度:
问:Python是什么特别好的?什么时候使用Python为项目的“正确选择”?
虽然喜欢和不喜欢的是高度个人的,但是一个“值得他或她的盐”的开发商将突出蟒蛇语言的特征通常被认为是有利的(这也有助于回答Python的问题“特别好”)。此问题的一些更常见的有效答案包括:
- 易于使用和易于重构, 由于Python语法的灵活性,这使得它对快速原型设计尤其有用。
- 更紧凑的代码, 再次感谢Python的语法,以及丰富的功能丰富的Python库(通过大多数Python语言实现自由分发)。
- 一种动态类型和强类型的语言, 提供罕见的代码灵活性的组合,同时避免了讨厌的隐式类型转换错误。
- 它是免费的和开放的源! Need we say more?
关于使用Python时的问题是项目的“正确选择”,完整的答案还取决于与语言本身正交的许多问题,例如先前的技术投资,团队技能组,等等。 。虽然上面规定的问题意味着对一个严格的技术答案的兴趣,一个开发者,他们将在面试中提高这些额外问题将随时与我“得分更多”,因为它表明了对“更大的图景”的意识和敏感性“ (即,除了雇用的技术之外)。相反,Python始终是正确的选择的响应是一个简单的开发人员的清晰标志。
问:Python语言的一些缺点是什么?
对于初学者来说,如果你知道一种语言,你知道它的缺点,所以回答,如“我没有什么我不喜欢它”或“它没有缺点”是非常讲述的。
这个问题的两个最常见的有效答案(绝不是旨在作为详尽列表)的是:
- 全球翻译锁(GIL). CPython(最常见的Python实现)不是完全线程安全的。为了支持多线程的Python程序,CPython提供了一个全局锁,必须在安全地访问Python对象之前由当前线程保持。结果,无论存在多少个线程或处理器,在任何给定时间都只执行一个线程。相比之下,值得注意的是,小明型实施 本文早些时候讨论过 提供一种堆叠模式,支持用于大规模并发的微线程。
- 执行速度。 自解释后,Python可以比编译语言慢。 (嗯,有点。看看我们的 早先的讨论 on this topic.)
包起来
这里提出的问题和提示可以是识别真正的Python开发主人的极其有价值的援助。我们希望您发现它们是“将小麦从谷壳中分离挑选”在Python软件开发人员中的精英中的一个有用的基础。然而,重要的是要记住,这些只是旨在作为将其整体招聘工具箱和策略的更大背景中纳入的工具。
而且,对于那些可能错误地阅读本指南的人希望学习如何捕捉爬行动物(对不起DUDE,错误的Python!),我们建议检查佛罗里达州的野生动物基础 Python挑战.