首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >近期遇到的关于 Python 的面试题

近期遇到的关于 Python 的面试题

作者头像
somenzz
发布2021-08-19 16:08:56
5630
发布2021-08-19 16:08:56
举报
文章被收录于专栏:Python七号Python七号

前段时间去面试了一些招聘 Python 软件开发的公司,看看现在的公司都关注 Python 的那些方面。因为疫情原因,现在面试都是电话或者视频面试,也可以约晚上,不用请假,也不影响白天的工作,面试的成本非常低,收益却很高,面的好的话就意味着涨薪水,面的不好就说明自己掌握的还不够,因此面试是一个很好的学习交流形式,推荐大家每 2 年都去面试一波。

今天来聊一聊我近期遇到的关于 Python 的面试题。

1、说说你对 Python 多线程的理解。

这道题是开放题目,就是考察候选人对 Python 知识了解的广度。

总之,你要提到线程的定义,线程的状态转换,CPython 的 GIL 对多线程的影响,线程同步的几种方式,线程池,多线程的使用场景,甚至你还可以扯一些协程的区别。

这个问题,可以自己思考一下答案,也可以参考文章:Python多线程

2、说说对 python 协程的理解。

这个题目我认为是考察对事件循环的理解。

首先可以聊一聊为什么会有协程,我们知道,在处理 I/O 操作时,使用多线程与普通的单线程相比,效率得到了极大的提高。但是也会遇到问题,比如,多线程运行过程容易被打断,因此有可能出现 race condition 的情况;再如,线程切换本身存在一定的损耗,线程数不能无限增加,因此,如果你的 I/O 操作非常繁重,多线程很有可能满足不了高效率、高质量的需求。为了解决这些问题,协程应运而生。

协程的实现原理,就是事件循环,事件循环 “是一种等待程序分配事件或消息的编程架构”。基本上来说事件循环就是,“当A发生时,执行B”。

简单说,就是在程序中设置两个线程:一个负责程序本身的运行,称为"主线程";另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为"消息线程")。

每当遇到 I/O 的时候,主线程就让 Event Loop 线程去通知相应的 I/O 程序,然后接着往后运行,所以不存在等待时间。等到 I/O程序完成操作,Event Loop 线程再把结果返回主线程。主线程就调用事先设定的回调函数,完成整个任务。

协程虽然单线程,却实现了多线程并发的效果,也不涉及线程的切换,因此节省资源,更为高效。

从使用体验上来说,多线程编码简单,线程的切换由操作系统控制,而协程编码复杂,代码执行时机的切换由程序员自己控制。

关于线程和协程,前文并发使用多线程还是协程有介绍。

3、Python 中的迭代器和生成器有什么区别,都说生成器是一种特殊的迭代器,请问特殊在哪里?

首先明确迭代器的定义,Python 中一切皆对象,只要一个对象有实现了 __iter__ 方法和 __next__ 方法,那么他就是一个迭代器。

class MyListIterator(object):  # 定义迭代器类
    def __init__(self, data):
        self.data = data  # 上边界
        self.now = 0  # 当前迭代值,初始为0

    def __iter__(self):
        return self  # 返回该对象的迭代器类的实例;因为自己就是迭代器,所以返回 self

    def __next__(self):  # 迭代器类必须实现的方法
        while self.now < self.data:
            self.now += 1
            return self.now - 1  # 返回当前迭代值
        raise StopIteration  # 超出上边界,抛出异常

from collections import Iterator

print(isinstance(MyListIterator(10), Iterator))
# True

迭代器可以通过 next() 函数来遍历,for in 语句将这个过程隐式化,比如上面的对象:

mylist = MyListIterator(3)
print(next(mylist)) # 0
print(next(mylist)) # 1
print(next(mylist)) # 2
print(next(mylist)) # StopIteration

用 for 循环也可以

mylist = MyListIterator(3)
for i in mylist:
    print(i)

而生成器也是迭代器,只不过是使用 yield 关键字或者 (i for i in range(10)) 这种推导式形式创建出来的迭代器。

def fun(n):
    i = 0
    while i < n:
        yield i
        i += 1

等价于

(i for i in range(n))

生成器的特殊之处是生成器比较懒,不会一下次将数据全部加载到内存,而且只能遍历一次。下面的两行代码一个是迭代器,一个是生成器(也是迭代器),可以看出他们的性能差异了吧:

In [14]: timeit [i for i in range(1000000)]
49.2 ms ± 723 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [15]: timeit (i for i in range(1000000))
505 ns ± 3.21 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

关于迭代器和生成器,可以参考前文Python迭代器还可以这样玩

4、你知道 GIL 吗,说说你的理解。

可以把这个讲给面试官听Python有可能删除 GIL 吗?

5、Django 就如何防止跨站请求伪造的?

跨站请求伪造的英文 Cross-site request forgery (CSRF),只要你用过 Django,对这个 CSRF 一定不会陌生,因为稍不注意,Django 就会提示你 403 没有权限访问。

简单来说,Django 会生成一个随机的字符串(csrftoken),放在表单的隐藏字段里,然后在提交表单时会将这个 csrftoken 一起提交到后端,后端的中间件django.middleware.csrf.CsrfViewMiddleware会去校验这个字符串跟之前的是否一致,不一致则认为是跨站请求伪造,拒绝访问。

官方文档 CSRF[1]

6、前后端分离的项目,如何解决跨域问题的?

CORS(Cross-origin resource sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。它的核心思想,使用自定义的 HTTP 头部信息让浏览器和后端进行沟通,来决定是否允许跨域请求。

其实有三种解决方案:

  • 后端解决,后面可以配置跨域站点的白名单,或者干脆允许跨域请求。比如 Django 可通过第三方的跨域库 django-cors-headers 添加支持,常用在开发环境。
  • 前端解决,前端可以使用代理实现,常用在开发环境,以 Vue 为例,在 Vue 的配置文件中加入以下代码:
proxy: {
    '/api': {
        target: 'http://127.0.0.1:8001',
        changeOrigin: true,
        pathRewrite: {
            '^/api': '/api',//重写,
        }
    },
  • 反向代理实现,Nignx 作为反向代理来解决跨域问题,生产环境通常这样做,比如典型的 nginx 配置:
location /static {
    autoindex off;
    alias /Users/aaronbrant/gitee/KeJiTuan/frontEnd/dist/static;
}

location ~/(api|admin) {
    set $Real $proxy_add_x_forwarded_for;
    if ( $Real ~ (\d+)\.(\d+)\.(\d+)\.(\d+),(.*) ){
        set $Real $1.$2.$3.$4;
    }


    uwsgi_pass  127.0.0.1:8000;
    uwsgi_param X-Real-IP $Real;
    uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
    uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
    uwsgi_param UWSGI_SCRIPT rearEnd.wsgi;
    uwsgi_param UWSGI_CHDIR /Users/aaronbrant/gitee/KeJiTuan/rearEnd;
    include uwsgi_params;
}

具体实施的话,可以看教你玩转Vue和Django的前后端分离

7、Django ORM 的 get 和 filter 方法有什么区别?

这个就很简单了,get 只获取一个对象,对象不存在时抛出异常,filter 获取一组对象,对象不存在时,返回空,不抛出异常。

以下是手撕代码题目:

所谓手撕代码,打开编辑器,开始写代码,没有限制,自己命名函数,自己处理输入输出,如果自己不写一些测试用例,很有可能出现考虑不周的情况。

8、请用两种方式实现单例。

这个其实考察懒汉和饿汉,所谓的懒汉就是用的时候在创建对象,饿汉就是不管用不用先创建了再说,这里分别给出:

方法一,懒汉:

# 懒汉式
class Singleton(object):
    __instance = None
    def __init__(self):
        if not self.__instance:
            print('调用__init__, 实例未创建')
        else:
            print('调用__init__,实例已经创建过了:', __instance)

    @classmethod
    def get_instance(cls):
        # 调用get_instance类方法的时候才会生成Singleton实例
        if not cls.__instance:
            cls.__instance = Singleton()
        return cls.__instance

只有在使用的时候才创建对象,因此运行的速度稍快,但线程不安全,多个线程同时访问到 if not cls.__instance: 就有可能创建出多个不同的对象。

方法二,饿汉:

一开始就创建好 Singleton 实例

# 饿汉式
class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._instance

线程安全,但是由于要先创建对象再使用,当对象比较大时,比较耗时间。

9、算法题

这类题目基本就是 LeetCode 上的原题,看来面试官也是懒得创新,直接拿原题考一考得了,不过有水平的面试官会拿一道简单题目开始,然后逐渐增加难度,这样更能考察候选人的真实水平。

10、实现 LRU 缓存淘汰算法:

这是老生常谈了,这里直接附上两种实现的代码:

LRU 缓存淘汰算法-双链表+hash 表[2]

当然还可以使用 Python 的有序字典:

LRU 缓存淘汰算法-Python 有序字典[3]

最后的话

技术面试,还是实力最重要,其他的回答技巧基本不起什么作用。如果本文对你有帮助,还请点个在看,感谢支持。至于面试的结果,且听下回分解。

留言讨论

参考资料

[1]

官方文档 CSRF: https://docs.djangoproject.com/zh-hans/3.2/ref/csrf/

[2]

LRU 缓存淘汰算法-双链表+hash 表: https://github.com/somenzz/geekbang/blob/master/algorthms/lru_use_link_table.py

[3]

LRU 缓存淘汰算法-Python 有序字典: https://github.com/somenzz/geekbang/blob/master/algorthms/lru_use_ordered_dict.py

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、说说你对 Python 多线程的理解。
  • 2、说说对 python 协程的理解。
  • 3、Python 中的迭代器和生成器有什么区别,都说生成器是一种特殊的迭代器,请问特殊在哪里?
  • 4、你知道 GIL 吗,说说你的理解。
  • 5、Django 就如何防止跨站请求伪造的?
  • 6、前后端分离的项目,如何解决跨域问题的?
  • 7、Django ORM 的 get 和 filter 方法有什么区别?
  • 8、请用两种方式实现单例。
  • 9、算法题
  • 10、实现 LRU 缓存淘汰算法:
  • 最后的话
  • 参考资料
相关产品与服务
消息队列 TDMQ
消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档