机器之心编辑
作者: kenwoodjw
对于机器学习算法工程师而言, Python 是不可或缺的语言, 它的优美与简洁令人无法自拔. 那么你了解过 Python 编程面试题吗? 从 Python 基础到网页爬虫你是否能全方位 Hold 住? 今天, 机器之心为读者们推荐一个 GitHub 项目.
在这个项目中, 作者 kenwoodjw 准备了近 300 道 Python 面试题, 同时还包含解决方案与代码. 作者主要从 Python 基础, 高级语句, 网页应用, 数据库和测试等角度提问, 读者可只关注自己需要的领域. 目前该项目已经完成了很多基础和高级面试题, 本文主要摘取一些 Python 面试题供大家参考.
项目地址: https://github.com/kenwoodjw/python_interview_question
总体而言, 项目有近 300 道面试题. 虽然该项目刚开始创建, 但很多 Python 面试题都已经提供了解决方案. 如下所示为面试题示例:
本文截取了一些面试题及解决方案:
Python 基础
文件操作
模块与包
数据类型
企业面试题
Python 高级
设计模式
系统编程
如果希望了解机器学习面试题, 可阅读: 春招已近, 这份 GitHub 万星的 ML 算法面试大全请收下
Python 基础
什么是 Python? 根据 Python 创建者 Guido van Rossum 所言, Python 是一种高级编程语言, 其设计的核心理念是代码的易读性, 以及允许编程者通过若干行代码轻松表达想法创意. 实际上, 很多开发者选择学习 Python 的首要原因是其编程的优美性, 用它编码和表达想法非常自然.
文件操作
1. 若有一个 jsonline 格式的文件 file.txt, 大小约为 10K, 我们的处理方式为:
def get_lines(): l = [] with open('file.txt', 'rb') as f: for eachline in f: l.append(eachline) return lif __name__ == '__main__': for e in get_lines(): process(e) #处理每一行数据
现在要处理一个大小为 10G 的 file.txt 文件, 但是内存只有 4G. 如果在只修改 get_lines 函数而其他代码保持不变的情况下, 应该如何实现? 需要考虑的问题都有那些?
def get_lines(): l = [] with open('file.txt','rb') as f: data = f.readlines(60000) l.append(data) yield l
要考虑的问题有: 内存只有 4G, 无法一次性读入 10G 文件. 而分批读入数据要记录每次读入数据的位置, 且分批每次读取得太小会在读取操作上花费过多时间.
模块与包
2. 如何输入日期, 判断这一天是这一年的第几天?
import datetimedef dayofyear(): year = input("请输入年份:") month = input("请输入月份:") day = input("请输入天:") date1 = datetime.date(year=int(year),month=int(month),day=int(day)) date2 = datetime.date(year=int(year),month=1,day=1) return (date1-date2).days+1
数据类型
3. 如何反转字符串 "aStr"?
print("aStr"[::-1])
4. 下面代码的输出结果将是什么? 会报错吗?
list = ['a','b','c','d','e']print(list[10:])
代码将输出[], 并不会产生 IndexError 错误. 如果尝试用超出成员的个数的 index 来获取某个列表的成员, 那就会报错. 例如, 尝试获取 list[10] 和之后的成员, 会导致 IndexError. 然而当我们尝试获取列表的切片时, 开始的 index 超过成员个数并不会产生 IndexError, 而是仅仅返回一个空列表. 因为并不会报错, 所以这种 Bug 很难追踪到.
5. 请写出一段 Python 代码, 实现删除 list 里面的重复元素?
l1 = ['b','c','d','c','a','a']l2 = list(set(l1))print(l2)
用 list 类的 sort 方法可以保证顺序不变:
l1 = ['b', 'c', 'd', 'c', 'a', 'a']l2 = list(set(l1))l2.sort(key=l1.index)print(l2)
也可以这样写:
l1 = ['b', 'c', 'd', 'c', 'a', 'a']l2 = sorted(set(l1), key=l1.index)print(l2)
也可以用遍历:
l1 = ['b', 'c', 'd', 'c', 'a', 'a']l2 = []for i in l1: if not i in l2: l2.append(i)print(l2)
企业面试题
6. 设计实现遍历目录与子目录, 抓取. pyc 文件
第一种方法:
import osdef getFiles(dir, suffix): res = [] for root, dirs, files in os.walk(dir): for filename in files: name, suf = os.path.splitext(filename) if suf == suffix: res.append(os.path.join(root, filename)) print(res)getFiles("./", '.pyc')
第二种方法:
import osdef pick(obj): try: if obj.[-4:] == ".pyc": print(obj) except: return Nonedef scan_path(ph): file_list = os.listdir(ph) for obj in file_list: if os.path.isfile(obj): pick(obj) elif os.path.isdir(obj): scan_path(obj)if __name__ == '__main__': path = input('输入目录') scan_path(path)
7. 如何反转一个整数, 例如 - 123--> -321?
class Solution(object): def reverse(self, x): if -10 < x < 10: return x str_x = str(x) if str_x[0] != "-": str_x = str_x[::-1] x = int(str_x) else: str_x = str_x[1:][::-1] x = int(str_x) x = -x return x if -2147483648 < x < 2147483647 else 0if __name__ == '__main__': s = Solution() reverse_int = s.reverse(-120) print(reverse_int)
Python 高级
Python 高级包含很多重要的模块, 例如函数, 类和实例, 系统编程, 正则表达式, 网络编程等等. 根据这些高级属性, Python 可用于数据科学, 网页开发, 机器学习等等.
设计模式
8. 对设计模式的理解, 简述你了解的设计模式?
设计模式是为我们经常会碰到的一些编程问题构建的可重用解决方案, 它是总结性和经优化的. 一个设计模式并不像一个类或一个库那样能够直接作用于我们的代码, 反之, 设计模式更为高级, 它是一种在特定情形下实现的方法模板. 常见的是工厂模式和单例模式.
单例模式应用的场景一般发现在以下条件下: 资源共享的情况下, 避免由于资源操作时导致的性能或损耗等, 如日志文件, 应用配置. 控制资源的情况下, 方便资源之间的互相通信.
9. 生成器和迭代器的区别?
迭代器是一个更抽象的概念, 任何对象, 如果它的类有 next 方法和 iter 方法返回自己本身, 它就是可迭代的. 对于 string,list,dict,tuple 等这类容器对象, 使用 for 循环遍历是很方便的, for 语句实际上会对容器对象调用 iter() 函数. iter() 会返回一个定义了 next() 方法的迭代器对象, 它在容器中逐个访问容器内元素, 在没有后续元素时, next()会抛出一个 StopIteration 异常.
生成器 (Generator) 是创建迭代器的简单而强大的工具. 它们写起来就像是正规的函数, 只是在需要返回数据的时候使用 yield 语句. 生成器能做到迭代器能做的所有事, 而且因为自动创建 iter()和 next()方法, 生成器显得特别简洁, 而且生成器也是高效的, 使用生成器表达式取代列表解析可以同时节省内存. 除了创建和保存程序状态的自动方法, 当发生器终结时, 还会自动抛出 StopIteration 异常.
10. 对装饰器的理解, 你能写出一个计时器装饰器, 它能记录函数的执行时间吗?
装饰器本质上是一个 Python 函数, 它可以让其他函数在不需要做任何代码变动的前提下增加额外功能, 装饰器的返回值也是一个函数对象.
import time def timeit(func): def wrapper(): start = time.clock() func() end = time.clock() print('used:',end-start) return wrapper @timeit def foo(): print('in foo()'foo())
系统编程
11. 介绍一下你了解的进程.
程序运行在操作系统上的一个实例, 就称之为进程. 进程需要相应的系统资源: 内存, 时间片, pid. 创建进程: 首先要导入 multiprocessing 中的 Process; 创建一个 Process 对象; 创建 Process 对象时, 可以传递参数.
p = Process(target=XXX, args=(tuple,), kwargs={key: value})target = XXX # 指定的任务函数, 不用加()args = (tuple,)kwargs = {key: value} # 给任务函数传递的参数
使用 start()启动进程 结束进程 给子进程指定函数传递参数 Demo
import osfrom mulitprocessing import Processimport timedef pro_func(name, age, **kwargs): for i in range(5): print("子进程正在运行中, name=%s,age=%d,pid=%d" % (name, age, os.getpid())) print(kwargs) time.sleep(0.2)if __name__ == "__main__": # 创建 Process 对象 p = Process(target=pro_func, args=('小明', 18), kwargs={'m': 20}) # 启动进程 p.start() time.sleep(1) # 1 秒钟之后, 立刻结束子进程 p.terminate() p.join()
12. 谈谈你对多进程, 多线程, 以及协程的理解, 项目是否用?
进程: 一个运行的程序 (代码) 就是一个进程, 没有运行的代码叫程序, 进程是系统资源分配的最小单位, 进程拥有自己独立的内存空间, 所有进程间数据不共享, 开销大. 线程: CPU 调度执行的最小单位, 也叫执行路径, 不能独立存在, 依赖进程存在, 一个进程至少有一个线程, 叫主线程, 而多个线程共享内存可以极大地提高了程序的运行效率. 协程: 是一种用户态的轻量级线程, 协程的调度完全由用户控制, 协程拥有自己的寄存器上下文和栈. 协程调度时, 将寄存器上下文和栈保存到其他地方, 在切回来的时候, 恢复先前保存的寄存器上下文和栈, 直接操中栈则基本没有内核切换的开销, 可以不加锁的访问全局变量, 所以上下文的切换非常快.
关于系统编程还有很多问题, 例如:
来源: https://juejin.im/entry/5c7f2f2ef265da2dac45734f