记搜狗一次不成功的Python后端面试经历
面试搜狗Python后端结束快一个月了,终于有时间来做一个简单的总结了。
简介:工作不久,基础后端岗位,一面结束,失败。
先做了几个笔试题,面试开始会根据笔试题问一些内容。下面将整理一下还能想起来的内容。
1.一个代码题,要求输出的结果是什么,并解释原因
def f(x, l=[]):
for i in xrange(x):
l.append(i**2)
print l
f(2) # [0, 1]
f(3, [3, 2, 1]) # [3, 2, 1, 0, 1, 4]
f(3) # [0, 1, 0, 1, 4]
面试过程:输出结果如上所示,当时我回答的原因是列表是可变对象,设置为函数的默认参数的时候,会保留值,应该默认设置为None这种类型。面试官又问为什么可变对象就不行呢?我回答在Python中函数也是一个对象,他的一个属性会保存相应的默认值。
简要总结:根据官方文档,默认参数在定义的时候,只初始化一次。可以在函数对象的func_defaults属性中查看默认参数。如下所示,通过把函数对象的默认参数的内存地址打印出来,可以看到确实是同一个列表地址。
print id(f.func_defaults[0])
f(2)
print id(f.func_defaults[0])
f(3, [3, 2, 1])
print id(f.func_defaults[0])
f(3)
2.*args 和 **kwargs 是什么,为什么要使用它们?
面试过程: 回答了前者是位置参数,后者是关键字参数,在函数定义时,参数不确定的情况下可以使用,是一个pack过程,在函数调用时,是一个unpack过程。
3.说一下Python的内存管理,说一下垃圾回收机制?
面试过程: 内存管理包括垃圾回收、引用计数、内存池,垃圾回收机制是引用计数为主、标记清除和分代回收为辅。然后我就用大白话解释了一下我对这些内容的理解,导致说的比较尴尬。。
回顾总结: 关于内存管理,个人觉得这篇比较好,点击链接,说的比较明白。垃圾回收的三种机制是互相关联的,可以直接搜索找到合适自己的答案。
4.编程实现二分查找?
当时写的代码如下
def b_search(data, k):
if not data:
return -1
left, right = 0, len(data) - 1
while left <= right:
mid = (left + right) / 2
if data[mid] == k:
return mid
elif data[mid] > k:
right = mid - 1
else:
left = mid + 1
return -1
追问,如果给定的数组中中有多个重复值,如何进行查找?描述一下思路。当时理解成查找数组中这个值的始末索引了,回答的是使用二分法分别查找始末索引就可以了。面试官说直接使用二分法就可以。额?
5.经典问题,一次可以上1个台阶,也可以上2个...n个,问一共有多少种上法
当时写的代码大概如下,并用公式表示了一下。
def fib(n):
a, b = 1, 2
for i in range(n):
yield a
a, b = b, 2 * b
6.当从b模块中导入a,运行b,会输出什么?a,b内容如下所示
# a.py
print 'm'
def f():
print 'fm'
class A(object):
print 'A.m'
def fa(self):
print 'A.f.m'
# b.py
import a
运行b后,输入结果如下所示:
m
A.m
面试过程:我当时记得说了Python中的函数会延迟生成,在导入时不直接执行,而模块的顶级代码会直接执行。类变量没说,理解不够深入。
原因:Python模块导入时,会创建一个module对象,并插入到sys.modules中,可以通过以下代码查看模块的属性
print dir(sys.modules['a'])
模块中的顶层变量会在模块导入时就执行,类的类变量也是如此,因此在模块中应该尽量避免这种,放在函数中进行延迟生成。这里需要深入了解模块的导入机制
7.说一下对装饰器的理解,原理是什么?如何使用装饰器实现求函数的执行时间?
面试过程:装饰器可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用,为已经存在的对象添加额外的功能,比如性能测试等。原理当时一下忘记了是闭包了,最后写的求时间的装饰器也不符合要求.
回顾总结:装饰器原理是使用了闭包实现的,是一种面向切面编程的模式(对设计模式理解不多,下一步需要抽时间学习设计模式了!)。对装饰器实现的执行时间,代码如下所示:(Python中有自带的timeit模块用来求执行时间)。
import time
from functools import wraps
def timeit(func):
@wraps(func)
def wrapper():
start = time.clock()
func()
end = time.clock()
print 'used:', end - start
return wrapper
@timeit
def f():
time.sleep(3)
f()
8.对闭包的理解?
面试回答:我答的是在一个函数中返回的内部函数,在函数结束后,内部函数还可以继续使用。(完全大白话!又一次尴尬了)
回顾总结:闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
9.对函数对象的理解?
回顾总结:忘了咋回答的了!函数可以赋值给变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值。对于函数对象的理解,可以看这里
10.如何理解Python中一切皆对象?
回顾总结:Python中所有的对象都有身份、类型和值。具体内容可以直接搜索,感觉这个博客写的也不错。当然要最好的理解还是看源码。
11.对协程的理解,协程和线程的区别?
回顾总结: 协程是使用装饰器实现的,GIL,程序员自己来控制。协程适合I/O密集型操作,异步功能。
12.二叉树求深度?
递归思路
def get_depth(tree):
if not tree:
return 0
if not tree.left and not tree.right:
return 1
return 1 + max(get_depth(tree.left), get_depth(tree.right))
13.MySQL索引优化?
回顾总结:这个在很多面试中会问到,工作中也会用到,需要认真学好MySQL。关键点:最左匹配,联合索引,在哪种情况下建立索引,B树原理,explain查看语句等。网上搜一下挺多内容的,不过还是最好看一下《高性能MySQL》这本书。
14.Redis缓存,数据类型等?
回顾总结:字符串、列表、hash、set、有序集合等。
总结
面试内容相对比较深,广度一般,关注了很多Python的深层原理。
反思
- 对于很多特性自己还是了解的不够深入;
- 在学习过程中,自己习惯于把一些概念转化成大白话来加深理解,导致对概念的专业术语反而记不住了,说起来显得不专业;
- 代码能力有待提高;
- 要想面试好,还是需要更好的准备的,工作之余直接去面试有点仓促。