1. 近期遇到的關(guān)于 Python 的面試題

        共 7695字,需瀏覽 16分鐘

         ·

        2021-08-11 09:50

        前段時間去面試了一些招聘 Python 軟件開發(fā)的公司,看看現(xiàn)在的公司都關(guān)注 Python 的那些方面。因?yàn)橐咔樵?,現(xiàn)在面試都是電話或者視頻面試,也可以約晚上,不用請假,也不影響白天的工作,面試的成本非常低,收益卻很高,面的好的話就意味著漲薪水,面的不好就說明自己掌握的還不夠,因此面試是一個很好的學(xué)習(xí)交流形式,推薦大家每 2 年都去面試一波。

        今天來聊一聊我近期遇到的關(guān)于 Python 的面試題。

        1、說說你對 Python 多線程的理解。

        這道題是開放題目,就是考察候選人對 Python 知識了解的廣度。

        總之,你要提到線程的定義,線程的狀態(tài)轉(zhuǎn)換,CPython 的 GIL 對多線程的影響,線程同步的幾種方式,線程池,多線程的使用場景,甚至你還可以扯一些協(xié)程的區(qū)別。

        這個問題,可以自己思考一下答案,也可以參考文章:Python多線程

        2、說說對 python 協(xié)程的理解。

        這個題目我認(rèn)為是考察對事件循環(huán)的理解。

        首先可以聊一聊為什么會有協(xié)程,我們知道,在處理 I/O 操作時,使用多線程與普通的單線程相比,效率得到了極大的提高。但是也會遇到問題,比如,多線程運(yùn)行過程容易被打斷,因此有可能出現(xiàn) race condition 的情況;再如,線程切換本身存在一定的損耗,線程數(shù)不能無限增加,因此,如果你的 I/O 操作非常繁重,多線程很有可能滿足不了高效率、高質(zhì)量的需求。為了解決這些問題,協(xié)程應(yīng)運(yùn)而生。

        協(xié)程的實(shí)現(xiàn)原理,就是事件循環(huán),事件循環(huán) “是一種等待程序分配事件或消息的編程架構(gòu)”?;旧蟻碚f事件循環(huán)就是,“當(dāng)A發(fā)生時,執(zhí)行B”。

        簡單說,就是在程序中設(shè)置兩個線程:一個負(fù)責(zé)程序本身的運(yùn)行,稱為"主線程";另一個負(fù)責(zé)主線程與其他進(jìn)程(主要是各種I/O操作)的通信,被稱為"Event Loop線程"(可以譯為"消息線程")。

        每當(dāng)遇到 I/O 的時候,主線程就讓 Event Loop 線程去通知相應(yīng)的 I/O 程序,然后接著往后運(yùn)行,所以不存在等待時間。等到 I/O程序完成操作,Event Loop 線程再把結(jié)果返回主線程。主線程就調(diào)用事先設(shè)定的回調(diào)函數(shù),完成整個任務(wù)。

        協(xié)程雖然單線程,卻實(shí)現(xiàn)了多線程并發(fā)的效果,也不涉及線程的切換,因此節(jié)省資源,更為高效。

        從使用體驗(yàn)上來說,多線程編碼簡單,線程的切換由操作系統(tǒng)控制,而協(xié)程編碼復(fù)雜,代碼執(zhí)行時機(jī)的切換由程序員自己控制。

        關(guān)于線程和協(xié)程,前文并發(fā)使用多線程還是協(xié)程有介紹。

        3、Python 中的迭代器和生成器有什么區(qū)別,都說生成器是一種特殊的迭代器,請問特殊在哪里?

        首先明確迭代器的定義,Python 中一切皆對象,只要一個對象有實(shí)現(xiàn)了 __iter__ 方法和 __next__ 方法,那么他就是一個迭代器。

        class MyListIterator(object):  # 定義迭代器類
            def __init__(self, data):
                self.data = data  # 上邊界
                self.now = 0  # 當(dāng)前迭代值,初始為0

            def __iter__(self):
                return self  # 返回該對象的迭代器類的實(shí)例;因?yàn)樽约壕褪堑?,所以返?self

            def __next__(self):  # 迭代器類必須實(shí)現(xiàn)的方法
                while self.now < self.data:
                    self.now += 1
                    return self.now - 1  # 返回當(dāng)前迭代值
                raise StopIteration  # 超出上邊界,拋出異常

        from collections import Iterator

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

        迭代器可以通過 next() 函數(shù)來遍歷,for in 語句將這個過程隱式化,比如上面的對象:

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

        用 for 循環(huán)也可以

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

        而生成器也是迭代器,只不過是使用 yield 關(guān)鍵字或者 (i for i in range(10)) 這種推導(dǎo)式形式創(chuàng)建出來的迭代器。

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

        等價(jià)于

        (i for i in range(n))

        生成器的特殊之處是生成器比較懶,不會一下次將數(shù)據(jù)全部加載到內(nèi)存,而且只能遍歷一次。下面的兩行代碼一個是迭代器,一個是生成器(也是迭代器),可以看出他們的性能差異了吧:

        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)

        關(guān)于迭代器和生成器,可以參考前文Python迭代器還可以這樣玩

        4、你知道 GIL 嗎,說說你的理解。

        可以把這個講給面試官聽Python有可能刪除 GIL 嗎?

        5、Django 就如何防止跨站請求偽造的?

        跨站請求偽造的英文 Cross-site request forgery (CSRF),只要你用過 Django,對這個 CSRF 一定不會陌生,因?yàn)樯圆蛔⒁?,Django 就會提示你 403 沒有權(quán)限訪問。

        簡單來說,Django 會生成一個隨機(jī)的字符串(csrftoken),放在表單的隱藏字段里,然后在提交表單時會將這個 csrftoken 一起提交到后端,后端的中間件django.middleware.csrf.CsrfViewMiddleware會去校驗(yàn)這個字符串跟之前的是否一致,不一致則認(rèn)為是跨站請求偽造,拒絕訪問。

        官方文檔 CSRF[1]

        6、前后端分離的項(xiàng)目,如何解決跨域問題的?

        CORS(Cross-origin resource sharing,跨域資源共享)是一個 W3C 標(biāo)準(zhǔn),定義了在必須訪問跨域資源時,瀏覽器與服務(wù)器應(yīng)該如何溝通。它的核心思想,使用自定義的 HTTP 頭部信息讓瀏覽器和后端進(jìn)行溝通,來決定是否允許跨域請求。

        其實(shí)有三種解決方案:

        • 后端解決,后面可以配置跨域站點(diǎn)的白名單,或者干脆允許跨域請求。比如 Django 可通過第三方的跨域庫 django-cors-headers 添加支持,常用在開發(fā)環(huán)境。

        • 前端解決,前端可以使用代理實(shí)現(xiàn),常用在開發(fā)環(huán)境,以 Vue 為例,在 Vue 的配置文件中加入以下代碼:

        proxy: {
            '/api': {
                target'http://127.0.0.1:8001',
                changeOrigintrue,
                pathRewrite: {
                    '^/api''/api',//重寫,
                }
            },
        • 反向代理實(shí)現(xiàn),Nignx 作為反向代理來解決跨域問題,生產(chǎn)環(huán)境通常這樣做,比如典型的 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;
        }

        具體實(shí)施的話,可以看教你玩轉(zhuǎn)Vue和Django的前后端分離

        7、Django ORM 的 get 和 filter 方法有什么區(qū)別?

        這個就很簡單了,get 只獲取一個對象,對象不存在時拋出異常,filter 獲取一組對象,對象不存在時,返回空,不拋出異常。

        以下是手撕代碼題目:

        所謂手撕代碼,打開編輯器,開始寫代碼,沒有限制,自己命名函數(shù),自己處理輸入輸出,如果自己不寫一些測試用例,很有可能出現(xiàn)考慮不周的情況。

        8、請用兩種方式實(shí)現(xiàn)單例。

        這個其實(shí)考察懶漢和餓漢,所謂的懶漢就是用的時候在創(chuàng)建對象,餓漢就是不管用不用先創(chuàng)建了再說,這里分別給出:

        方法一,懶漢:

        # 懶漢式
        class Singleton(object):
            __instance = None
            def __init__(self):
                if not self.__instance:
                    print('調(diào)用__init__, 實(shí)例未創(chuàng)建')
                else:
                    print('調(diào)用__init__,實(shí)例已經(jīng)創(chuàng)建過了:', __instance)

            @classmethod
            def get_instance(cls):
                # 調(diào)用get_instance類方法的時候才會生成Singleton實(shí)例
                if not cls.__instance:
                    cls.__instance = Singleton()
                return cls.__instance

        只有在使用的時候才創(chuàng)建對象,因此運(yùn)行的速度稍快,但線程不安全,多個線程同時訪問到 if not cls.__instance: 就有可能創(chuàng)建出多個不同的對象。

        方法二,餓漢:

        一開始就創(chuàng)建好 Singleton 實(shí)例

        # 餓漢式
        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

        線程安全,但是由于要先創(chuàng)建對象再使用,當(dāng)對象比較大時,比較耗時間。

        9、算法題

        這類題目基本就是 LeetCode 上的原題,看來面試官也是懶得創(chuàng)新,直接拿原題考一考得了,不過有水平的面試官會拿一道簡單題目開始,然后逐漸增加難度,這樣更能考察候選人的真實(shí)水平。

        10、實(shí)現(xiàn) LRU 緩存淘汰算法:

        這是老生常談了,這里直接附上兩種實(shí)現(xiàn)的代碼:

        LRU 緩存淘汰算法-雙鏈表+hash 表[2]

        當(dāng)然還可以使用 Python 的有序字典:

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

        最后的話

        技術(shù)面試,還是實(shí)力最重要,其他的回答技巧基本不起什么作用。如果本文對你有幫助,還請點(diǎn)個在看,感謝支持。至于面試的結(jié)果,且聽下回分解。

        留言討論

        參考資料

        [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


        瀏覽 28
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 三级中文字幕 | 毛片操逼一级 | www.91爱爱.com | 最新中文字幕MV第三季歌词 | 免费无码在线播放 |