【Python】Python學到什麼程度可以面試工作?------持續更新 ...

前言: 從事python學習,有爬蟲、web後臺、深度學習相關經驗,

座標北京歡迎騷擾。

本答案力求簡潔和直擊重點,代碼部分使用Python3,更詳細的解釋請Google,回答有誤請務必提醒答主,我將及時改正。內容較多,建議關注+收藏,我將隨時更新。

1.  列出 5 個常用 Python 標準庫?

import os                  # 操作系統接口  示例:os.system('ls')
import sys                 # 命令行參數    示例:sys.path
import re                  # 正則模塊      示例:re.match('www', 'www.zhihu.com')
import math                # 數學模塊      示例:math.cos(math.pi / 3)
import time                # 日期模塊      示例:time.sleep(3)
import random              # 隨機數模塊    示例:random.random()
import threading           # 線程模塊      示例:threading.Thread(target=lambda a, b:a.append(b), args=([1, 2], 3))
import multiprocessing     # 進程模塊      示例:multiprocessing.Process(target=method, args=([1, 2], 3))   # pickle模塊不能序列化lambda,需要自定義函數

 

2. Python的內建數據類型有哪些?

# 數值型--int、float、complex

# 布爾型--bool

# 字符串--str

# 列表--list

# 元組--tuple

# 字典--dict

 

3. 簡述 with 方法打開處理文件幫我我們做了什麼?

  • with 語句適用於對資源進行訪問的場合,確保不管使用過程中是否發生異常都會執行必要的“清理”操作,釋放資源,比如文件使用後自動關閉、線程中鎖的自動獲取和釋放等。
  • with語句即“上下文管理器”,在程序中用來表示代碼執行過程中所處的前後環境 上下文管理器:含有__enter__和__exit__方法的對象就是上下文管理器。
  • __enter__():在執行語句之前,首先執行該方法,通常返回一個實例對象,如果with語句有as目標,則將對象賦值給as目標。
  • __exit__():執行語句結束後,自動調用__exit__()方法,用戶釋放資源,若此方法返回布爾值True,程序會忽略異常。
  • 使用環境:文件讀寫、線程鎖的自動釋放等。

 

4. Python的可變和不可變數據類型?

# 不可變數據類型:即數據被創建之後,數據的值將不再發生改變,有數值、字符、元祖類型。

# 可變數據類型:數據別創建之後,數據的值可以發生變化,有列表、字典、集合類型。

 

5.Python 獲取當前日期?

import datetime

print(datetime.datetime.now())

 

6.統計字符串每個單詞出現的次數

# coding:utf-8


def word_amount(sentence):
    """計算句子的單詞數量
    
    計算句子中的單詞數量,並返回結果字典
    :param sentence: 句子對象
    :return: 計算結果字典
    """
    # 使用split()方法切割句子
    split_list = sentence.split()
    dict_result = {}
    # 遍歷列表,判斷每個值是否在新字典的鍵中,如果存在則鍵值加一,不存在設爲1
    for word_name in split_list:
        if word_name not in dict_result.keys():
            dict_result[word_name] = 1
        else:
            dict_result[word_name] += 1

    return dict_result


if __name__ == '__main__':
    sentence = "I can because i think i can"
    dict_result = word_amount(sentence)
    print(dict_result)

評論區 @Felix 有更優的解法,非常感謝:

sentence = "I can because i think i can"
# count()用來統計字符串中word出現的次數,count(sub, start=None, end=None)有三個參數,依次是待統計字符,字符索引開頭,索引結尾
result = {word: sentence.split().count(word) for word in set(sentence.split())}

print(result)

 

 還有更優解,萬分感謝!

from collections import Counter
sentence = "I can because i think i can"
# Counter是一個簡單的計數器,可以數組中統計字符出現的個數:
counts = Counter(sentence.split())
print(counts)  # Counter({'can': 2, 'i': 2, 'I': 1, 'because': 1, 'think': 1})

collections是一個很棒的內建模塊,可以參考廖雪峯教程

collections​www.liaoxuefeng.com圖標

7. 用 python 刪除文件和用 linux 命令刪除文件方法

# Python方法
import os

os.remove("demo.txt")

# linux方法
rm demo.txt

 

8. 寫一段自定義異常代碼?

# coding:utf-8


def judge_value(num_value):
    """自定義異常函數

    自定義異常函數,用於拋出大於一定值的異常
    :param num_value:用於判斷的值
    :return:異常信息
    """
    if num_value > 10:
        # raise用於拋出自定義異常,格式爲:raise 異常類型(異常註明)
        # 一旦觸發則不再執行raise之後的代碼
        raise ValueError("數量不能大於10")
    else:
        return "200"


if __name__ == '__main__':
    judge_value(10)

 

9. 舉例說明異常模塊中 try except else finally 的相關意義

# coding:utf-8

def read_filedata(file_name):
    """讀取文件數據
    
     讀取指定文件中的所有數據,返回數據或者異常信息
    :param file_name: 文件路徑
    :return: 文件數據或者異常信息
    """
    file_obj = ""
    try:
        # 需要檢測的異常代碼片段
        file_obj = open(file_name, "r")
        result_data = file_obj.read()
    except IOError, e:
        # 發生“IOError”異常進行處理的代碼片段
        file_obj = "文件不存在:"+str(e)
    else:
        # 沒有引發“IOError”異常執行的代碼片段
        # 返回讀取到的數據
        return result_data
    finally:
        # 不管有沒有引發錯誤都會執行的代碼片段
        # isinstance()用於判斷一個數據類型
        if isinstance(file_obj, str):
            return file_obj
        elif isinstance(file_obj, file):
            file_obj.close()
        else:
            return "未知錯誤,請檢查您的代碼..."


if __name__ == '__main__':
    result = read_filedata("abc.txt")
    print(result)

 

10. 遇到 bug 如何處理?

  • 首先查看報錯信息,根據報錯信息找到相應代碼,通常一般的數據結構或者算法錯誤只要找到報錯代碼就可以順利解決。
  • 如果遇到暫時不能解決的錯誤先不要慌,我們可以使用編譯器的Debug模式或者自己在代碼中加註斷點進行代碼排查。
  • 如果依然不能解決bug,我們可以拷貝報錯信息,在搜索引擎中進行搜索。
  • 沒有人寫代碼不出bug,如果你在一個bug上耗費時間超過半小時,可以與其他同事探討(注意節制,可能有些費同事)。
  • 另闢蹊徑:方法總比困難多,在進行快速開發時,我們應該優先實現功能而不是拘泥於運行效率,所以遇到一些暫時不能解決的BUG可以考慮另外的實現方法。

語言特性

  1. 談談對 Python 的瞭解和其他語言的區別?
Python是一門語法簡潔優美,功能強大無比,應用領域非常廣泛,具有強大完備的第三方庫的一門強類型的動態,可移植,可擴展,可嵌入的解釋型編程語言

經評論區 

Corbin Zhou

 提醒,Python爲強類型語言,這一點其實一直有爭議,還有人說Python之父說過是“弱類型”,這裏進行一個補充,回答時只要合理解釋即可:

如果語言經常隱式地轉換變量的類型,那這個語言就是弱類型語言,如果很少會這樣做,那就是強類型語言
Python很少會隱式地轉換變量的類型,所以Python是強類型的語言。

解釋性:解釋型語言使用解釋器將源碼逐行解釋成機器碼並立即執行,不會進行整體性的編譯和鏈接處理,相當於把編譯語言中的編譯和解釋混合到一起同時完成。

優點:跨平臺容易,只需提供特定平臺的解釋器;

缺點:運行效率較低,因爲每次執行相當於都要進行一次編譯。

編譯型語言和解釋型語言

簡潔優雅: 省略了各種大括號和分號,還有一些關鍵字,類型說明(自省);

面向對象:Python和C++、Java一樣都是面向對象編程語言;

跨平臺:簡單搭建Python解釋器可以在大部分平臺運行。

Python和Java相比:

Python是動態類型語言,而Java是靜態類型語言.
動態類型語言不需要事先聲明變量的類型,而且變量的數據類型可以被修改
靜態類型語言需要事先聲明,並且不能修改;
Python和C相比:
對於使用:
Python的類庫齊全並且使用簡潔,很少代碼實現的功能用C可能要很複雜
對於速度:
Python的運行速度相較於C,絕對是很慢了。Python的CPython解釋器是C語言編寫的。

 

2. 簡述解釋型和編譯型編程語言?

請看上一條

 

3.Python 的解釋器種類以及相關特點?

摘自廖雪峯教程
CPython
當我們從Python官方網站下載並安裝好Python 3.x後,我們就直接獲得了一個官方版本的解釋器:CPython。這個解釋器是用C語言開發的,所以叫CPython。在命令行下運行python就是啓動CPython解釋器。
CPython是使用最廣的Python解釋器。

IPython
IPython是基於CPython之上的一個交互式解釋器,也就是說,IPython只是在交互方式上有所增強,但是執行Python代碼的功能和CPython是完全一樣的。好比很多國產瀏覽器雖然外觀不同,但內核其實都是調用了IE。
CPython用>>>作爲提示符,而IPython用In [序號]:作爲提示符。

PyPy
PyPy是另一個Python解釋器,它的目標是執行速度。PyPy採用JIT技術,對Python代碼進行動態編譯(注意不是解釋),所以可以顯著提高Python代碼的執行速度。
絕大部分Python代碼都可以在PyPy下運行,但是PyPy和CPython有一些是不同的,這就導致相同的Python代碼在兩種解釋器下執行可能會有不同的結果。如果你的代碼要放到PyPy下執行,就需要了解PyPy和CPython的不同點

Jython
Jython是運行在Java平臺上的Python解釋器,可以直接把Python代碼編譯成Java字節碼執行。

IronPython
IronPython和Jython類似,只不過IronPython是運行在微軟.Net平臺上的Python解釋器,可以直接把Python代碼編譯成.Net的字節碼。

 

4. 說說你知道的Python3 和 Python2 之間的區別?

編碼

Python2 中字符的類型:
str: 已經編碼後的字節序列
unicode: 編碼前的文本字符

Python3 中字符的類型:
str: 編碼過的 unicode 文本字符
bytes: 編碼前的字節序列

我們可以認爲字符串有兩種狀態,即文本狀態和字節(二進制)狀態。Python2 和 Python3 中的兩種字符類型都分別對應這兩種狀態,然後相互之間進行編解碼轉化。編碼就是將字符串轉換成字節碼,涉及到字符串的內部表示;解碼就是將字節碼轉換爲字符串,將比特位顯示成字符。
在 Python2 中,str 和 unicode 都有 encode 和 decode 方法。但是不建議對 str 使用 encode,對 unicode 使用 decode, 這是 Python2 設計上的缺陷。Python3 則進行了優化,str 只有一個 encode 方法將字符串轉化爲一個字節碼,而且 bytes 也只有一個 decode 方法將字節碼轉化爲一個文本字符串。

Python2中需要在文件頭打上註釋 # coding:utf-8 指定該程序使用的編碼格式爲UTF-8

print

Python2中的print是class

Python3中的print是函數

*Python 2 的 print 聲明已經被 print() 函數取代了,這意味着我們必須包裝我們想打印在小括號中的對象。

所以我們輸出格式爲

print("")    # py3

print ""     # py2
print("")

input

Python3:input 解析輸入爲str字符型
Python2: input 解析輸入爲int型,raw_input解析輸入爲 str 類型

算術符

Python3中/表示真除,%表示取餘,//結果取整;Python2中帶上小數點/表示真除,%表示取餘,//結果取整

xrange

Python2中使用xrange()來創建一個迭代器對象,使用range()創建一個list數組;
Python3中使用range()創建迭代器對象,移除了xrange()方法。

 

5. Python3 和 Python2 中 int 和 long 區別?

Python 3裏,只有一種整數類型 int,大多數情況下,它很像Python2裏的長整型。
Python 2有爲非浮點數準備的 int 和 long 類型。 int 類型的最大值不能超過 sys.maxint,而且這個最大值是平臺相關的。

 

6. xrange 和 range 的區別?

Python2中使用xrange()來創建一個迭代器對象,使用range()創建一個list數組;
Python3中使用range()創建迭代器對象,移除了xrange()方法。

編碼規範

7. 什麼是 PEP8?

PEP是 Python Enhancement Proposal 的縮寫,翻譯過來就是 Python增強建議書
PEP8 ,簡單說就是一種編碼規範,是爲了讓代碼“更好看”,更容易被閱讀。

 

8. 瞭解 Python 之禪麼?

import this

The Zen of Python, by Tim Peters                                        
Beautiful is better than ugly.                                          
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

優美勝於醜陋(Python 以編寫優美的代碼爲目標)
明瞭勝於晦澀(優美的代碼應當是明瞭的,命名規範,風格相似)
簡潔勝於複雜(優美的代碼應當是簡潔的,不要有複雜的內部實現)
複雜勝於凌亂(如果複雜不可避免,那代碼間也不能有難懂的關係,要保持接口簡潔)
扁平勝於嵌套(優美的代碼應當是扁平的,不能有太多的嵌套)
間隔勝於緊湊(優美的代碼有適當的間隔,不要奢望一行代碼解決問題)
可讀性很重要(優美的代碼是可讀的)
即便假借特例的實用性之名,也不可違背這些規則(這些規則至高無上)
不要包容所有錯誤,除非你確定需要這樣做(精準地捕獲異常,不寫 except:pass 風格的代碼)
當存在多種可能,不要嘗試去猜測
而是儘量找一種,最好是唯一一種明顯的解決方案(如果不確定,就用窮舉法)
雖然這並不容易,因爲你不是 Python 之父(這裏的 Dutch 是指 Guido )
做也許好過不做,但不假思索就動手還不如不做(動手之前要細思量)
如果你無法向人描述你的方案,那肯定不是一個好方案;反之亦然(方案測評標準)
命名空間是一種絕妙的理念,我們應當多加利用(倡導與號召)

 

9.瞭解 docstring 麼?

DocStrings 文檔字符串是一個重要工具,用於解釋文檔程序,幫助你的程序文檔更加簡單易懂。
我們可以在函數體的第一行使用一對三個單引號 ''' 或者一對三個雙引號 """ 來定義文檔字符串。
你可以使用 __doc__(注意雙下劃線)調用函數中的文檔字符串屬性。
DocStrings文檔字符串使用慣例:它的首行簡述函數功能,第二行空行,第三行爲函數的具體描述。
# coding:utf-8


def create_iterator(list_param):
    """創建迭代器

    使用生成器推導式創建一個迭代器,並返回迭代器
    :param list_parm: 迭代對象
    :return: 迭代器
    """
    # 將列表推導式的“[]”改爲“()”即爲生成器推導式,衆所周知,生成器返回一個迭代器對象
    return (value for value in list_param)


if __name__ == '__main__':
    # 遍歷迭代器
    for i in create_iterator([1, 2, 3]):
        print(i)
    # 使用__doc__輸出函數中的文檔字符串屬性
    print(create_iterator.__doc__)
    # 使用__dir__輸出函數中的所有屬性和方法
    print(create_iterator.__dir__())
補充
類的函數稱爲方法(method),模塊裏的函數稱爲函數(function);
每一個包,模塊,類,函數,方法都應該包含文檔,包括類的__init__方法;
包的文檔寫在__init__.py文件中;
文檔有單行文檔和多行文檔;
單行文檔:
不要重複函數的聲明語句,例如:function(a, b) -> list;
指明做什麼和返回什麼,例如Do X and return a list.;
使用三引號,方便換行;

多行文檔:
如果模塊是一個腳本,也就是單文件程序,模塊的文檔應該寫明腳本的使用方法;
模塊的文檔需要寫明包含的類,異常,函數;
如果是包,在__init__.py中,寫明包裏面包含的模塊,子包;
如果是函數或類方法,應該寫明函數或方法的作用,參數,返回,副作用,異常和調用的限制等;
如果是類,寫明類的行爲,和實例參數,構造方法寫在__init__中;
使用三引號,而且兩個三引號都應該單獨成行。

 

10. 瞭解類型註解麼?

首先,Python是一種動態語言,變量和函數的參數是不區分類型的。

Python解釋器會在運行的時候動態判斷變量和參數的類型,這樣的好處是編寫代碼速度很快,很靈活,但是壞處也很明顯,不好維護,可能代碼寫過一段時間重新看就很難理解了,因爲那些變量、參數、函數返回值的類型,全都給忘記了。

在閱讀別人的代碼時,無法看出變量或參數的類型,這樣對工作效率帶來很大影響。

因此,在Python3中新添加了“類型註解”特性,可以給參數、函數返回值和變量的類型加上註解,該註解僅僅是註釋而已,對代碼運行不會產生任何影響,真正的變量類型還是由Python解釋器決定,你所做的只是提高代碼可讀性,並不會像靜態語言中變量類型定義後就無法修改(強轉除外)。

上碼:

# coding:utf-8


def list_to_str(param_list: list, connect_str: str=" ") -> str:
    """列表轉字符串

    使用join方法將列表轉爲字符串並返回
    :param param_list: 列表
    :param connect_str: 需要插入的字符,默認爲一個空格
    :return:轉換成功的字符串
    """
    demo_tuple: tuple = (1, 2)
    demo_dict: dict = {"1": 1}
    return connect_str.join(param_list)


if __name__ == '__main__':
    result = list_to_str(["Hello", "world"])
    print(result)

以上代碼可以看出,一般變量和函數參數註解格式爲“參數:類型”,默認參數是在類型的後面加“=默認值”,函數的返回值註解格式爲“-> 類型:”,函數的冒號在註解後方。

類型註解僅僅起到了註釋作用,那我們應該如何知道它的正確性呢?

Python提供了一個工具方便我們測試類型註解的正確性

pip install mypy

使用方法:

mypy demo.py

若無錯誤則無輸出,反之會輸出如下:

D:\code\web\flaskweb>mypy demo.py
demo.py:12: error: Incompatible return value type (got "str", expected "int")

 

11. 例舉你知道 Python 對象的命名規範,例如方法或者類等

變量命名:字母數字下劃線,不能以數字開頭

單下劃線開頭變量
單下劃線開頭的變量標明是一個受保護(protected)的變量,原則上不允許直接訪問,但外部類還是可以訪問到這個變量。
這只是程序員之間的一個約定,用於警告說明這是一個私有變量,外部類不要去訪問它。
雙下劃線開頭變量
雙下劃線開頭的,表示的是私有類型(private)的變量。 只能是允許這個類本身進行訪問了, 連子類也不可以.
以雙下劃線開頭,並且以雙下劃線結尾的,是內置變量.
內置變量是可以直接訪問的,不是 private 變量,如__init__,__import__或是__file__。
★不要自己定義內置變量
xxx_,單下劃線結尾的變量一般只是爲了避免與 Python 關鍵字的命名衝突
USER_CONSTANT,大寫加下劃線,對於不會發生改變的全局變量,使用大寫加下劃線

函數和方法(類中叫做方法,模塊中稱作函數)命名:

總體而言應該使用小寫和下劃線,如:create_user():
私有方法 : 小寫和一個前導下劃線,如 def _create_user(self):
私有方法和私有變量一樣,並不是真正的私有訪問權限。
一般函數不要使用兩個前導下劃線(當遇到兩個前導下劃線時,Python 的名稱改編特性將發揮作用)。
特殊方法 : 小寫和兩個前導下劃線,兩個後置下劃線 def __init__(self):
這種風格只應用於特殊函數,比如操作符重載等。
函數參數 : 小寫和下劃線,缺省值等號兩邊無空格 def __init__(self, param=None):
不要濫用 *args 和 **kwargs,可能會破壞函數的健壯性

類命名:

類總是使用駝峯格式命名,即所有單詞首字母大寫其餘字母小寫/
如:Class CreateUser():
類名應該簡明,精確,並足以從中理解類所完成的工作。
常見的一個方法是使用表示其類型或者特性的後綴,例如:SQLEngine ,MimeTypes
對於基類而言,可以使用一個 Base 或者 Abstract 前綴

包和模塊:

小寫字母、數字和下劃線

 

12.Python 中的註釋有幾種?

單行註釋以“#”開頭

# 單行註釋

多行註釋使用三個單引號或者雙引號

"""
雙引號多行註釋
"""

'''
單引號多行註釋
'''

單引號和雙引號混用,使用一種引號後其註釋中的所有引號應該使用另一種

"""
'雙引號'中使用'單引號'
"""

'''
"單引號"中使用"雙引號"
'''

 

13. 如何優雅的給一個函數加註釋?

見第9條

 

14. 如何給變量加註釋?

變量註釋使用行內註釋,根據pep8規範應該在代碼後至少有兩個空格,註釋由#和一個空格開始:

user_name = "Robin"    # 用戶姓名
user_age = 26          # 用戶年齡
user_gender = 1        # 用戶性別,男爲1,女爲0

有代碼潔癖的同學可以保持註釋的對齊,但一行文本不宜超過79個字符(PEP8規範)。

 

15. Python 代碼縮進中是否支持 Tab 鍵和空格混用。

Python是一門用空格縮進來區分代碼層次的語言,其實Python並沒有強制要求你用Tab縮進或者用空格縮進,甚至空格按幾個都沒有強制要求(但在PEP8中建議了使用4個空格作爲縮進)
但是卻絕對!絕對不能混用Tab和空格 !
python中不提倡使用tab縮進 
不同編輯器對於TAB的解釋是不同的,有的編輯器tab是4個字符寬,有的8個字符寬。 
如果有的地方用TAB,有的地方用空格,在不同的地方,原本對齊的代碼就可能會不對齊。

 

16. 是否可以在一句 import 中導入多個庫?

可以在一巨import中打入多個庫,但是一般我們不這樣做,原因有以下幾點

更易於閱讀
更易於搜索
更易於編輯
多行import更易於維護

另,導入多個模塊語句最好以下面方式書寫,使用空行將其分割

  • python 標準庫模塊
  • python 第三方模塊
  • 自定義模塊

有的程序員喜歡這樣導入模塊

from socker import *

這樣寫的好處就是不需要我們一個個列出“socket”需要的方法,但是這樣引入的弊端如下

  • 佔用更多的內存空間,不必要的方法或類可能會進行初始化
  • 代碼可讀性差,模塊內部突然冒出一個沒有見過也沒有歸屬的方法,很是頭疼

 

17. 在給 Py 文件命名的時候需要注意什麼?

在爲Python文件命名時,我們需要注意

  • 不能與Python中的關鍵字命名;
  • 不能以標準庫或常用第三方庫命名,除非你想重寫這些庫;
  • 不能用除字母數字下換線之外的字符命名,注意不要使用中文命名任何路徑和可執行文件;
  • 數字不能作爲開頭。

 

18. 例舉幾個規範 Python 代碼風格的工具

Pylint
安裝:pip install pylint
使用:pylint demo.py
Black
安裝:pip install black
使用:black demo.py
Autopep8
安裝:pip install autopep8
使用:autopep8 --in-place --aggressive demo.py

經評論區 

百萬光年

 提醒,還有flake8:

flake8安裝:
pip install flake8 
使用:flake8 demo.py

個人使用最多的是Autopep8。

 

19. 列舉 Python 中的基本數據類型?

# 數值型--int、float、complex
# 布爾型--bool
# 字符串--str
# 列表--list
# 元組--tuple
# 字典--dict

 

20. 如何區別可變數據類型和不可變數據類型

可變數據類型:在內存id不變的情況下,數據的值可以改變

不可變數據類型:數據的值不能發生改變,如果值發生改變,那麼內存id也會改變,這樣就不是同一個數據了。

demo_list = [1, 2, 3]
print(id(demo_list))        # 4874760
demo_list.append(4)
print(id(demo_list))        # 4874760

demo_tuple = (1, 2, 3)
print(id(demo_tuple))       # 65021344
# demo_tuple.append(4)      # 不可變類型不能對值進行修改
demo_tuple = (1, 2, 3, 4)   # 重新對變量賦值,變量和數據類型需要區分,變量!=數據,變量只是數據的載體
print(id(demo_tuple))       # 42116344

 

21. 將"hello world"轉換爲首字母大寫"Hello World"

#!/usr/bin/python3
# coding:utf-8


def first_capital(change_sentence: str)->str:
    """句子所有單詞首字母大寫

    將句子的首字母大寫並返回
    :param change_sentence: 需要轉換的句子
    :return: 返回轉換後的字符串
    """
    # 將句子使用split切割爲list數組
    split_list = change_sentence.split()
    # 遍歷列表長度值
    for i in range(len(split_list)):
        # 使用capitalize()函數將每個單詞首字母轉爲大寫
        split_list[i] = split_list[i].capitalize()
        # 也可以使用 upper() 方法,upper()可以把所有的小寫字母轉爲大寫,lower()是轉小寫
        # split_list[i] = split_list[i][0].upper() + split_list[i][1:]
    # 使用join將列表轉爲字符串
    split_list = " ".join(split_list)
    return split_list


if __name__ == '__main__':
    change_sentence = "hello world"
    print(first_capital("hello world"))
    # 有需要的同學可以使用匿名函數、列表推導式和map方法一行寫出以上代碼
    print(" ".join(list(map(lambda word: word.capitalize(), change_sentence.split()))))

 

22. 如何檢測字符串中只含有數字?

isdigit() 方法檢測字符串是否只由數字組成。

#!/usr/bin/python3

demo_str = "123456"
print(demo_str.isdigit())                       # 輸出True
demo_str = "this is string example....wow!!!"
print(demo_str.isdigit())                       # 輸出False

 

23. 將字符串"ilovechina"進行反轉

#!/usr/bin/python3
from functools import reduce

# 第一種方法,使用字符串切片
demo_str = "ilovechina"
print(demo_str[::-1])

# 第二種方法,使用列表的reverse方法
list_str = list(demo_str)
list_str.reverse()
print("".join(list_str))

# 第三種方法:reduce累加方法
# 具體步驟是將前兩個字母初始化添加到lambda函數中,得到的結果在與下一個字母做累加直到結束
# 第一次:l + i = li
# 第二次:o + li = oli
# 第三次:v + oli = voli
# ...
# 第九次:a + nihcevoli = anihcevoli
print(reduce(lambda x, y: y+x, demo_str))


# 使用棧,先進後出
def stack_demo(demo_str):
    # 模擬全部入棧
    list_stack = list(demo_str)
    result_str = ""
    while len(list_stack) > 0:
        # 模擬出棧
        result_str += list_stack.pop()
    return result_str

print(stack_demo(demo_str))

# 遍歷循環:略

 

25. 有一個字符串開頭和末尾都有空格,比如“ adabdw ”,要求寫一個函數把這個字符串的前後空格都去掉。

#!/usr/bin/python3

demo_str = " adabdw "

# 去除兩端空格使用:strip()
print(demo_str.strip())

# 去除右端空格使用:rstrip()
print(demo_str.rstrip())

# 去除左端空格使用:lstrip()
print(demo_str.lstrip())

# replace替換
print(demo_str.replace(" ", ""))

# 正則
import re
print(re.sub(" ", "", demo_str))

 

26. 獲取字符串”123456“最後的兩個字符。

#!/usr/bin/python3

demo_str = "123456"
print(demo_str[-2:])

 

27. 一個編碼爲 GBK 的字符串 S,要將其轉成 UTF-8 編碼的字符串,應如何操作?

#!/usr/bin/python3
import chardet

demo_str = "demo string".encode("gbk")
demo_str = demo_str.decode('gbk').encode('utf-8')
print(demo_str)
# chardet.detect()可以檢測編碼格式
print(chardet.detect(demo_str))

28. (1)s="info:xiaoZhang 33 shandong",用正則切分字符串輸出['info', 'xiaoZhang', '33', 'shandong']

#!/usr/bin/python3
import re

demo_str = "info:xiaoZhang 33 shandong"
# compile 函數用於編譯正則表達式,生成一個正則表達式( Pattern )對象
# 由題可知需要去除出字母數字之外的字符,故使用"\W"做匹配
pattern = re.compile(r'\W')
# re.split()方法按照能夠匹配的子串將字符串分割後返回列表
print(pattern.split(demo_str))

(2) a = "你好 中國 ",去除多餘空格只留一個空格。

#!/usr/bin/python3
import re

a = "你好 中國 "
print(a.rstrip())

 

29. (1)怎樣將字符串轉換爲小寫

#!/usr/bin/python3
import re

demo_str = "HELLO WORLD"
# 轉小寫使用lower() 轉大寫使用upper()
print(demo_str.lower())

(2)單引號、雙引號、三引號的區別?

在Python中我們都知道單引號和雙引號都可以用來表示一個字符串,比如

str1 = 'python'
str2 = "python" 

str1和str2是沒有任何區別的。但是如果遇到需要轉義字符的情況,來看單引號和雙引號的版本。

單引號版本:

str3 = 'We all know that \'A\' and \'B\' are two capital letters.'

雙引號版本:

str4 = "We all know that 'A' and 'B' are two capital letters."

單引號需要加 '\' 來讓編譯器判斷目前是轉義字符,而雙引號方便了很多。

反之,如果字符串中有雙引號,爲了避免使用轉義符,可以使用單引號來定義這個字符串。

str5 = 'The teacher said: "Practice makes perfect" is a very famous proverb.'

三個單引號和三個雙引號一般用於多行註釋,也可以用來表示字符串:輸出多行文本

str1 = """List of name:
Hello
World
 """
print(str1)
# 使用三引號定於多行字符類型比使用雙引號要友好許多

 

30. 已知 AList = [1,2,3,1,2],對 AList 列表元素去重,寫出具體過程。

#!/usr/bin/python3
import pandas

# 第一種方法,使用set集合,先轉爲集合再轉回列表
AList = [1, 2, 3, 1, 2]
result_list = list(set(AList))
print(result_list)

# 第二種,使用dict.fromkeys,該函數有兩個參數,第一個是字典的鍵,第二個是對應值(默認爲空str),用於創建一個字典類型
AList = [1, 2, 3, 1, 2]
result_list = list(dict.fromkeys(AList))
print(result_list)

# 第三種,遍歷列表進行判斷
AList = [1, 2, 3, 1, 2]
result_list = []
for i in AList:
    if i not in result_list:
        result_list.append(i)
    else:
        continue
print(result_list)

# 第四種,使用pandas.unique()方法,
AList = [1, 2, 3, 1, 2]
result_list = pandas.unique(AList).tolist()
print(result_list)

 

31. 如何實現 "1,2,3" 變成 ["1","2","3"]

#!/usr/bin/python3


demo_str = "1,2,3"
result_list = demo_str.split(",")
print(result_list)

 

32. 給定兩個 list,A 和 B,找出相同元素和不同元素

#!/usr/bin/python3


list_A = [1, 2, 3, 4, 5]
list_B = [4, 5, 6, 7, 8]

# 找相同
same_list = [i for i in list_A if i in list_B]
print(same_list)

# 找不同
different_list = [i for i in list_A if i not in list_B]
print(different_list)

 

33. [[1,2],[3,4],[5,6]]一行代碼展開該列表,得出[1,2,3,4,5,6]

#!/usr/bin/python3

question_list = [[1, 2], [3, 4], [5, 6]]
# 使用列表推導式嵌套的時候,注意前後的調用關係,前推導式的值需要在後面書寫才能生效
# 需要輸出的值放到推導式的最前面,生成輸出值的推導式在最後面
print([list_int for inside_list in question_list for list_int in inside_list])

 

34. 合併列表[1,5,7,9]和[2,2,6,8]

#!/usr/bin/python3

list_a = [1, 5, 7, 9]
list_b = [2, 2, 6, 8]

# 第一種方法:使用運算符“+”
combine_list = list_a + list_b
print(combine_list)

# 第二種方法:使用運算符extend()方法
list_a.extend(list_b)
print(list_a)

# 第三種方法:使用append
list_a = [1, 5, 7, 9]
for i in list_b:
    list_a.append(i)
print(list_a)

 

35. 如何打亂一個列表的元素?

#!/usr/bin/python3
import random

demo_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# 使用random.shuffle打亂一個list數組
random.shuffle(demo_list)
print(demo_list)

 

36. 字典操作中 del 和 pop 有什麼區別

#!/usr/bin/python3

demo_dic = {"a": 1, "b": 2, "c": 3}
# pop方法刪除指定的鍵值對,並返回刪除的值
pop_str = demo_dic.pop("a")
print(demo_dic)
print(pop_str)

# del不會返回相應的值,只是將其刪除
del demo_dic["b"]
print(demo_dic)

 

37. 按照字典的內的年齡排序

#!/usr/bin/python3

d1 = [
    {'name': 'alice', 'age': 38},
    {'name': 'bob', 'age': 18},
    {'name': 'ctrl', 'age': 28}
]
# 強大的sort方法,滿足大多數排序算法,列表排序優先考慮sort!
"""
list.sort( key=None, reverse=False)
key -- 主要是用來進行比較的元素,只有一個參數,
具體的函數的參數就是取自於可迭代對象中,指定可
迭代對象中的一個元素來進行排序。
reverse -- 排序規則,reverse = True 降序, reverse = False 升序(默認)。
"""
d1.sort(key=lambda x: x['age'])
print(d1)

 

38. 請合併下面兩個字典 a = {"A":1,"B":2},b = {"C":3,"D":4}

#!/usr/bin/python3

a = {"A": 1, "B": 2}
b = {"C": 3, "D": 4}
# 使用update方法
a.update(b)
print(a)

# 補充clear方法,清空字典中的鍵值對
a.clear()
print(a)

 

39. 如何使用生成式的方式生成一個字典,寫一段功能代碼。

#!/usr/bin/python3

# 生成一個以姓名爲鍵的字典,值爲"student"
student_dic = {name: "student" for name in ["Robin", "Bob", "Jams"]}
print(student_dic)   # {'Robin': 'student', 'Bob': 'student', 'Jams': 'student'}  

# 將一個字典中的姓名首字母轉爲大寫
name_dic = {1: "robin", 2: "bob", 3: "jams"}
# items() 以列表返回可遍歷的(鍵, 值) 元組數組。
# dict_items([(1, 'robin'), (2, 'bob'), (3, 'jams')])
result_dic = {k: v.capitalize() for k, v in name_dic.items()}
print(result_dic)    # {1: 'Robin', 2: 'Bob', 3: 'Jams'}

 

40. 如何把元組("a","b")和元組(1,2),變爲字典{"a":1,"b":2}

#!/usr/bin/python3

tuple_key = ("a", "b")
tuple_val = (1, 2)
result_dic = {k: v for k in tuple_key for v in tuple_val}
print(result_dic)

 

41. Python 常用的數據結構的類型及其特性?

字典中的鍵是不可變類型,可變類型list和dict不能作爲字典鍵
一個對象能不能作爲字典的key,就取決於其有沒有__hash__方法

 

42. 如何交換字典 {"A":1,"B":2}的鍵和值?

#!/usr/bin/python3

demo_dic = {"A": 1, "B": 2}
# 使用字典推導式交換位置
result_dic = {v: k for k, v in demo_dic.items()}
print(result_dic)

 

43. Python 裏面如何實現 tuple 和 list 的轉換?

#!/usr/bin/python3

demo_list = [1,2,3]
# 列表轉元祖使用tuple()
result_tup = tuple(demo_list)
print(type(result_tup))             # <class 'tuple'>
print(result_tup)                   # (1, 2, 3)
# 元祖轉列表使用list()
result_list = list(result_tup)  
print(type(result_list))            # <class 'list'>
print(result_list)                  # [1, 2, 3]

 

44. 我們知道對於列表可以使用切片操作進行部分元素的選擇,那麼如何對生成器類型的對象實現相同的功能呢?

#!/usr/bin/python3
import itertools


def fbnq(num):
    """斐波那契生成器

    :param num: 生產數量
    :return: 斐波那契迭代器
    """
    a, b = 1, 1
    for _ in range(num):
        a, b = b, a+b
        yield a


if __name__ == '__main__':
    gener = fbnq(20)
    print(gener)
    # 不能直接對生成器和迭代器進行切片
    # print(fbnq(20)[2])
    # 可以使用itertools.islice()對迭代器進行切片
    # itertools是一個很強大的內置模塊,有需要可以瞭解一下
    gener_clip = itertools.islice(gener, 10, 20)
    for i in gener_clip:
        print(i)

 

45. 請將[i for i in range(3)]改成生成器

(i for i in range(3))    # 方括號改爲尖括號即可

 

46. a="hello"和 b="你好"編碼成 bytes 類型

#!/usr/bin/python3

a.encode()
b.encode()

 

47. 下面的代碼輸出結果是什麼?

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[2] = 2             # TypeError: 'tuple' object does not support item assignment
  • 元祖是不可變類型,因此不能修改元祖內的值
  • a[2]=2 使得元祖中對索引值爲“2”的元素進行了修改,內存id發生了變化

 

48. 下面的代碼輸出的結果是什麼?

a = (1, 2, 3, [4, 5, 6, 7], 8)
a[3][0] = 2               # (1, 2, 3, [2, 5, 6, 7], 8)
  • 列表是可變數據類型,數據的值可以修改的
  • 這裏只是修改了元祖子對象的值,而不是修改了元祖的值
  • 修改可變類型的值不會改變內存id,因此元祖的引用還是沒有發生變化
  • 可以這麼理解,只要不修改元祖中值的內存id,那麼就可以進行“修改元祖”操作
  • 擴展,面試官可能會問到:元祖是否可以被修改?
  • 答:元祖是不可變數據類型,因此不能修改元祖中的值,但是如果元組中有可變數據類型,那麼可以修改可變數據類型中的值,修改可變數據類型的值並不會使其內存id發生變化,所以元祖中元素中的內存id也沒有改變,因此就做到了“修改元祖”操作。

 

49. Python 交換兩個變量的值

a, b = b, a

 

50. 在讀文件操作的時候會使用 read、readline 或者 readlines,簡述它們各自的作用

read:讀取整個文件。
readline:讀取下一行,使用生成器方法。
readlines:讀取整個文件到一個迭代器以供我們遍歷

 

51. json 序列化時,可以處理的數據類型有哪些?如何定製支持 datetime 類型?

  • json序列化時,可以處理列表、字典、字符、數值、布爾和None
  • 定製datetime類型↓
#!/usr/bin/python3
from datetime import datetime
import json
from json import JSONEncoder


class DatetimeEncoder(JSONEncoder):
    """擴展JSONEncoder類中的default方法

    判斷傳入的類型是否是datetime類型,如果
    是則轉爲str字符,否則不是返回父類的值
    """
    def default(self, o):
        if isinstance(o, datetime):
            return o.strftime('%Y-%m-%d %H:%M:%S')
        else:
            return super(DatetimeEncoder, self).default(o)


if __name__ == '__main__':
    dict_demo = {'name': 'alex', 'data': datetime.now()}
    print(json.dumps(dict_demo, cls=DatetimeEncoder))

 

52. json 序列化時,默認遇到中文會轉換成 unicode,如果想要保留中文怎麼辦?

#!/usr/bin/python3
import json

dict_demo = {"name": "旭東"}
# 使用dumps的默認參數ensure_ascii
print(json.dumps(dict_demo, ensure_ascii=False))

 

53. 有兩個磁盤文件 A 和 B,各存放一行字母,要求把這兩個文件中的信息合併(按字母順序排列),輸出到一個新文件 C 中。

#!/usr/bin/python3


def read_file(file_name):
    """讀文件

    讀取文件並返回文件數據
    :param file_name: 文件名
    :return: 文件的所有數據
    """
    with open(file_name, "r") as F:
        return F.read()


def write_filw(file_name, file_data):
    """寫文件

    將數據寫入到指定文件中
    :param file_name: 文件名
    :param file_data: 需要寫入的數據
    :return:
    """
    with open(file_name, "w") as F:
        F.write(file_data)


def letter_sort(letter_str, reverse_flag=False):
    """字母排序

    使用sorted排序算法
    :param letter_str: 排序字母字符串
    :param reverse_flag: 排序順序,False爲正序,True爲反序
    :return: 排序後的新字符串
    """
    return "".join(sorted(letter_str, reverse=reverse_flag))


if __name__ == '__main__':
    test1_data = read_file("test1.txt")
    test2_data = read_file("test2.txt")
    new_data = letter_sort(test1_data + test2_data)
    write_filw("new.txt", new_data)

 

54. 如果當前的日期爲 20190530,要求寫一個函數輸出 N 天后的日期,(比如 N 爲 2,則輸出 20190601)。

#!/usr/bin/python3
from datetime import datetime
from datetime import timedelta


def date_calculation(now_date, offset):
    """獲取日期函數

    獲取幾天前或者幾天後的日期
    :param now_date: 當前日期
    :param offset:  日期偏移量,負數爲前
    :return:  結果日期
    """
    # 格式轉換
    now_date = datetime.strptime(now_date, "%Y%m%d").date()
    # 計算偏移
    offset_date = timedelta(days=offset)
    return (now_date + offset_date).strftime("%Y%m%d")


if __name__ == '__main__':
    result_day = date_calculation("20190918", 30)
    print(result_day)

 

55. 寫一個函數,接收整數參數 n,返回一個函數,函數的功能是把函數的參數和 n 相乘並把結果返回。

  • 閉包是一種特殊的函數,這種函數由多個函數的嵌套組成,且稱之爲外函數和內函數,外函數返回值是內函數的引用,此時就構成了閉包;
  • 閉包函數必須返回一個函數對象;
  • 閉包函數返回的那個函數必須引用外部變量;
  • 閉包可以保存運行環境,即在閉包內的變量是不能被輕易修改的;
  • 閉包的好處:提高代碼的可複用性。
#!/usr/bin/python3


def out_func(n):
    """閉包函數
    
    :param n: 整數參數n
    :return: 內層函數
    """
    def in_func(num):
        return n*num
    return in_func


if __name__ == '__main__':
    demo = out_func(3)
    print(demo(4))

 

56. 下面代碼會存在什麼問題,如何改進?

def strappend(num):        # 函數作用、參數意義不明,需要加註釋
    str='frist'            # 不能使用關鍵字"str"作爲變量名
    for i in range(num):   # 遍歷得到的元素"i"意義不明,無註釋
        str+=str(i)        # 變量名和關鍵字在這個時候重名,必定報錯,沒有了str()方法
    return str

# 修改後
def str_append(append_cound: int) -> str:
    """字符串修改
    
    遍歷append_cound,將遍歷的值轉爲str類型並添加到字符串中
    :param append_cound: 遍歷次數
    :return: 最終修改得到的新字符串
    """
    append_str = "frist"
    # 遍歷獲取到“times”次數int類型
    for times in range(append_cound):
        append_str += str(times)
    return append_str


if __name__ == '__main__':
    print(str_append(4))

 

57. 一行代碼輸出 1-100 之間的所有偶數。

print([num for num in range(1,101) if num % 2 == 0])

 

58. with 語句的作用,寫一段代碼?

with語句:“上下文管理器”,用於資源訪問的場合,作用是資源釋放和異常處理(詳細內容在第3條問題彙總)

import threading

# 來一個用於線程鎖的with使用
num = 0  # 全局變量多個線程可以讀寫,傳遞數據
thread_lock = threading.Lock()  # 創建一個鎖


class Mythread(threading.Thread):
    def run(self):
        global num
        with thread_lock:               # with Lock的作用相當於自動獲取和釋放鎖(資源)
            for i in range(1000000):    # 鎖定期間,其他線程不可以運行
                num += 1
        print(num)

 

59. python 字典和 json 字符串相互轉化方法

#!/usr/bin/python3

import json

dict_demo = {"a": 1, "b": 2}
# 序列化:使用json.dumps()將python類型轉爲json字符串
json_demo = json.dumps(dict_demo)
print(type(json_demo))
# 使用json.dump()將python數據序列化到指定文件中
with open("demo.json", "w") as file_obj:
    json.dump(dict_demo, file_obj)

# 反序列化:使用json.loads()將json字符類型轉爲python類型
dict_demo = json.loads(json_demo)
print(type(dict_demo))
# 使用json.load()將json字符類型從文件中讀出來
with open("demo.json", "r") as file_obj:
    file_date = json.load(file_obj)
    print(file_date)

 

60. 請寫一個 Python 邏輯,計算一個文件中的大寫字母數量

#!/usr/bin/python3
import re


def capital_count(file_name):
    """計算文件中的大寫字母數量
    
    讀取文件並計算文件數據的大寫字母數量,返回大寫字母數量
    :param file_name: 文件名
    :return: 文件中的大寫字母數量
    """
    # 定義大寫字母數量變量
    upper_count = 0
    # 打開文件對象,讀取文件數據
    with open(file_name, "r") as file_obj:
        file_data = file_obj.read()
    # 刪除掉除字母之外的所有字符
    file_data = re.sub("[^a-zA-Z]", "", file_data)
    print(file_data)
    # 遍歷所有字母,使用isupper()判斷是否是大寫字母,並且計數
    for i in file_data:
        if i.isupper():
            upper_count += 1
    return upper_count


if __name__ == '__main__':
    print(capital_count("test1.txt"))
.

 

61. 請寫一段 Python連接 Mongo 數據庫,然後的查詢代碼。

#!/usr/bin/python3

import pymongo

# 連接本地數據庫
db_client = pymongo.MongoClient("mongodb://localhost:27017/")
# 切換到testdb測試數據庫
test_db = db_client["testdb"]
# 切換到“sites”文檔
sites_obj = test_db["sites"]
# find_one() 方法來查詢集合中的一條數據
first_data = sites_obj.find_one()
print(first_data)

 

62. 說一說 Redis 的基本類型。

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

 

63. 請寫一段 Python連接 Redis 數據庫的代碼。

#!/usr/bin/python3

import redis
# 創建連接對象
connec_obj = redis.Redis(host='localhost', port=6379, db=0)
# 設置一個鍵值
connec_obj.set('test', '1')
# 讀取一個鍵值
connec_obj.get('test')   # ->> '1'

 

64. 請寫一段 Python 連接 MySQL 數據庫的代碼。

#!/usr/bin/python3

import pymysql

# 打開數據庫連接
db = pymysql.connect("localhost", "testuser", "test123", "TESTDB", charset='utf8' )

# 使用cursor()方法獲取操作遊標
cursor = db.cursor()

# 使用execute方法執行SQL語句
cursor.execute("SELECT VERSION()")

# 使用 fetchone() 方法獲取一條數據
data = cursor.fetchone()

# 關閉數據庫連接
db.close()

 

65. 瞭解 Redis 的事務麼?

事務提供了一種"將多個命令打包,一次性提交併按順序執行"的機制,提交後在事務執行中不會中斷。只有在執行完所有命令後纔會繼續執行來自其他客戶的消息。
Redis通過multi,exec,discard,watch實現事務功能。
1. multi:開始事務
2. exec:提交事務並執行
3. discard:取消事務
4. watch:事務開始之前監視任意數量的鍵
5. unwatch:取消WATCH命令對多有key的監控,所有監控鎖將會被取消。
關於ACID:
1. 單獨的隔離操作:事務中的所有命令會被序列化、按順序執行,在執行的過程中不會被其他客戶端發送來的命令打斷
2. 沒有隔離級別的概念:隊列中的命令在事務沒有被提交之前不會被實際執行
3. 不保證原子性:redis中的一個事務中如果存在命令執行失敗,那麼其他命令依然會被執行,沒有回滾機制。

 

66. 瞭解數據庫的三範式麼?

通俗解釋
屬性不可分割:字段不能再分割,如“年級班級”可以分割爲“年級”和“班級”兩個字段
唯一主鍵:一張表中需要有一個唯一主鍵用來區分每行數據,如“學生學號 ”
消除冗餘和傳遞依賴:不同表中不能存在重複的字段數據,如“學生”表中的“院系”字段和“班級”表中“院系”字段,我們可以關聯兩張表的字段而無需在“學生”表中再加一個“院系”。
詳細解釋請自行查找。

 

67. 瞭解分佈式鎖麼?

在開發中可能會用到多線程和多進程,如果不同線程或者不同進程搶佔同一個資源,對其行讀寫操作可能會導致數據不一致,導致數據不是在我們預想的情況下改變。這裏爲了保證線程或者進程安全,python中引入了線程鎖和進程鎖,保證了數據的一致性和完整性。

而爲了保證分佈式系統的數據安全,可以使用使用分佈式鎖來解決這一問題(秒殺場景)。分佈式鎖其實可以理解爲:控制分佈式系統有序的去對共享資源進行操作,通過互斥來保持一致性。分佈式鎖的實現有很多種,常見的有redis、zookeeper和數據庫mysql等。

 

68. 用 Python 實現一個 Reids 的分佈式鎖的功能。

引自:junli_chen

#!/usr/bin/python3
# coding=utf-8

import time
import redis


class RedisLock(object):
    def __init__(self, key):
        # 連接數據庫,創建連接對象
        self.rdcon = redis.Redis(host='', port=6379, password="", db=1)
        # 設置鎖的值
        self._lock = 0
        # 分佈式鎖的鍵
        self.lock_key = "%s_dynamic_test" % key

    @staticmethod
    def get_lock(cls, timeout=10):
        """獲取redis分佈式鎖

        設置分佈式鎖,判斷鎖是否超時
        :param cls: 鎖的類對象
        :param timeout: 鎖超時時間
        :return:
        """
        while cls._lock != 1:
            # 設置鎖的過期時間
            timestamp = time.time() + timeout + 1
            # 設置redis分佈式鎖鍵值
            cls._lock = cls.rdcon.setnx(cls.lock_key, timestamp)
            # 判斷鎖的值是否爲1,或者當前時間大於鎖預期釋放的時間,如果成立則退出循環,釋放鎖
            if cls._lock == 1 or (
                    time.time() > cls.rdcon.get(cls.lock_key) and
                    time.time() > cls.rdcon.getset(cls.lock_key, timestamp)):
                print("get lock")
                break
            else:
                time.sleep(0.3)

    @staticmethod
    def release(cls):
        """釋放鎖

        :param cls: 鎖的類對象
        :return:
        """
        # 判斷當前時間是否大於鎖最大釋放時間
        if time.time() < cls.rdcon.get(cls.lock_key):
            print("release lock")
            cls.rdcon.delete(cls.lock_key)


def deco(cls):
    """分佈式鎖裝飾器

    :param cls: 分佈式鎖類對象
    :return: 外層函數
    """
    def _deco(func):
        def __deco(*args, **kwargs):
            print("before %s called [%s]." % (func.__name__, cls))
            cls.get_lock(cls)
            try:
                return func(*args, **kwargs)
            finally:
                cls.release(cls)

        return __deco

    return _deco


@deco(RedisLock("demoLock"))
def myfunc():
    print("myfunc() called.")
    # 設置20s模擬超過鎖釋放時間就自動釋放鎖的操作
    time.sleep(20)


if __name__ == "__main__":
    myfunc()

 

69. 寫一段 Python 使用 Mongo 數據庫創建索引的代碼。

索引通常能夠極大的提高查詢的效率,如果沒有索引,MongoDB在讀取數據時必須掃描集合中的每個文件並選取那些符合查詢條件的記錄。
這種掃描全集合的查詢效率是非常低的,特別在處理大量的數據時,查詢可以要花費幾十秒甚至幾分鐘,這對網站的性能是非常致命的。
索引是特殊的數據結構,索引存儲在一個易於遍歷讀取的數據集合中,索引是對數據庫表中一列或多列的值進行排序的一種結構
#!/usr/bin/python3
# coding=utf-8
import pymongo
from pymongo import ASCENDING, DESCENDING
# 連接數據庫,創建連接對象
myclient = pymongo.MongoClient(mongodbUrl)
# 切換數據庫
mydb = myclient[dbName]
# 創建索引,create_index()創建索引,可以有多個約束條件,值爲1則升序,-1是降序
mydb.create_index([("date", DESCENDING), ("author", ASCENDING)])

 

70. 函數裝飾器有什麼作用?請列舉說明?

裝飾器主要是在不修改代碼前提下進行功能的擴展,滿足面向對象的“開閉原則”。

應用場景:
1,引入日誌
2,函數執行時間統計
3,執行函數前預備處理
4,執行函數後清理功能
5,權限校驗等場景
6,緩存
7,事務處理

 

71. Python 垃圾回收機制?

整數

小整數:Python 對小整數的定義是 [-5, 257) 這些整數對象是提前建立好的,不會被垃圾回收。在一個 Python 的程序中,所有位於這個範圍內的整數使用的都是同一個對象。單個字母同樣也是如此。

大整數:每一個大整數的創建均在內存中會分配一個內存空間,所以大整數的內存空間是需要被回收的。

引用計數爲主,標記清除和分代回收爲輔:

引用計數:

python裏每一個東西都是對象,它們的核心就是一個結構體:PyObject
PyObject是每個對象必有的內容,其中ob_refcnt就是做爲引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少
當引用計數爲0時,該對象生命就結束了。
引用計數機制的優點:
1. 簡單
2. 實時性:一旦沒有引用,內存就直接釋放了。不用像其他機制等到特定時機。實時性還帶來一個好處:處理回收內存的時間分攤到了平時。
引用計數機制的缺點:
1. 維護引用計數消耗資源
2. 循環引用

標記清除

『標記清除(Mark—Sweep)』算法是一種基於追蹤回收(tracing GC)技術實現的垃圾回收算法。它分爲兩個階段:第一階段是標記階段,GC會把所有的『活動對象』打上標記,第二階段是把那些沒有標記的對象『非活動對象』進行回收。從GCROOT出發,標記所有的可達對象,不可達的就清除掉。
標記清除算法作爲Python的輔助垃圾收集技術主要處理的是一些容器對象,比如list、dict、tuple,instance等,因爲對於字符串、數值對象是不可能造成循環引用問題。

分代回收

分代回收是一種以空間換時間的操作方式,Python將內存根據對象的存活時間劃分爲不同的集合,每個集合稱爲一個代,Python將內存分爲了3“代”,分別爲年輕代(第0代)、中年代(第1代)、老年代(第2代),他們對應的是3個鏈表,它們的垃圾收集頻率與對象的存活時間的增大而減小。
每個分代集合中索引值越大的代表存活時間越長,越不容易被回收。
分代回收是建立在標記清除技術基礎之上。分代回收同樣作爲Python的輔助垃圾收集技術處理那些容器對象

 

72. 魔法函數 __call__怎麼使用?

__call__允許一個類的實例像函數一樣被調用
#!/usr/bin/python3
# coding=utf-8


class Entity(object):
    def __init__(self, size, x, y):
        self.x, self.y = x, y
        self.size = size

    def __call__(self, x, y):
        # 改變實例屬性
        self.x, self.y = x, y

        
if __name__ == '__main__':
    # 創建實例
    demo_obj = Entity(1, 2, 3)
    # 實例可以像函數那樣執行,並傳入x y值,修改對象的x y
    demo_obj(4, 5)

 

73. 如何判斷一個對象是函數還是方法?

在類外聲明def爲函數

類中聲明def:使用類調用爲函數,使用實例化對象調用爲方法

可以使用isinstance()判斷

#!/usr/bin/python3
# coding=utf-8
from types import FunctionType
from types import  MethodType


class DemoClass(object):
    def __init__(self):
        pass

    def run_method(self):
        pass


def demo_func():
    pass


if __name__ == '__main__':
    demo_obj = DemoClass()
    print(demo_obj.run_method)
    # out:<bound method DemoClass.run_method of <__main__.DemoClass object at 0x000000000277F588>>
    print(demo_func)
    # out:<function demo_func at 0x000000000248C1E0>
    print(DemoClass.run_method)
    # <function DemoClass.run_method at 0x0000000002924400>
    print(isinstance(demo_obj.run_method, FunctionType))   # False
    print(isinstance(demo_obj.run_method, MethodType))     # True

 

74.@classmethod 和@staticmethod 用法和區別

@classmethod 是類方法:訪問和修改類屬性,進行類相關的操作,通過類或示例對象調用,需要傳遞cls類對象爲參數;
@staticmethod 是靜態方法:不訪問類屬性和實例屬性,通過類或實例調用,相當於一個普通函數。

 

75. Python 中的接口如何實現?

類定義接口、函數定義接口

 

76. Python 中的反射了解麼?

計算機中的反射,是在運行的時候來自我檢查,並對內部成員進行操作。就是說這個變量的類型可以動態的改變,在運行的時候確定它的作用。
在Python中,能夠通過一個對象,找出其type、class、attribute或method的能力,稱爲反射或自省。
具有反射能力的函數有type(),isinstance(),callable().dir().getattr()等

Python的反射是一個很強大的功能,個人認爲每個Python程序員都應該掌握這一用法

這裏我引用 橡皮頭 的博客,非常詳細的解釋。

python的反射機制 - 橡皮頭 - 博客園​www.cnblogs.com圖標

 

77. metaclass 作用?以及應用場景?

有關metaclass請移步廖大教程

使用元類​www.liaoxuefeng.com圖標

 

78. hasattr() getattr() setattr()的用法

這三種方法用於爲對象屬性的存在判斷、獲取和添加修改,簡言之就是對象屬性的“增、改、查”。

hasattr(object, name):判斷對象是否存在name屬性
class A():
    name = 'python'
    def func(self):
        return 'A()類的方法func()'


if __name__ == '__main__':
    print(hasattr(A, 'name'))           # True
    print(hasattr(A, 'age'))            # False
getattr(object, name[, default]):獲取object對象name屬性的值,若沒有name屬性,則返回default值
class A():
    name = 'python'
    def func(self):
        return 'Hello world'


if __name__ == '__main__':
    print(getattr(A, "name"))       # "python"
    print(getattr(A, "age"))        # Error:class A has no attribute 'age'
    print(getattr(A, "age", 18))    # 18
    print(getattr(A, "func")        # <unbound method A.func>
    print(getattr(A(), "func")()    # 'Hello world',獲取到的方法需要實例化後才能調用,類方法則不需要
setattr(object, name, value)給object對象的name屬性賦值value,如果對象原本存在給定的屬性name,則setattr會更改屬性的值爲給定的balue,如果不存在屬性name,會在對象中創建屬性並賦值value
class A():
    name = 'python'
    def func(self):
        return 'Hello world'


if __name__ == '__main__':
    setattr(A, 'name', 'java')
    print(getattr(A, 'name'))             # java
    setattr(A, 'age', 20)
    print(getattr(A, "age")               # age

 

79. 請列舉你知道的 Python 的魔法方法及用途。

個人認爲該題面試時被問到的機率很大,通過該題可以擴展出其他知識,掌握此題將會在面試中擁有主動權哦。

在Python中,所有以 "_ _" 雙下劃包起來的方法稱爲“魔法方法”
魔法方法Python解釋器自動給出默認的,因此除非需要改變其內部功能,其它時刻刻使用默認魔法方法

最常用三個:"__init__"、"__new__"、"__del__"

__new__是用來創建類並返回這個類的實例, 
__init__將傳入的參數來初始化該實例,以及初始化示例屬性,與__new__共同構成了“構造函數”
__del__將實例化後的對象銷燬,即爲析構函數

類調用:__call__

__call__允許一個類像函數一樣被調用 

屬性訪問:__getattr__、__setattr__、__delattr__

__getattr__訪問對象不存在的屬性時,調用該方法,用於定義訪問行爲
__setattr__設置對象屬性時調用
__delattr__刪除對象屬性時調用

上下文管理器:__enter__和__exit__

這兩個方法請看上面第3題。

迭代器方法:__iter__和__next__

__iter__:返回一個容器迭代器,很多情況下會返回迭代器,尤其是當內置的iter()方法被調用的時候,以及當使用for x in container:方式循環的時候。迭代器是它們本身的對象,它們必須定義返回self的__iter__方法。
__next__:返回迭代器的下一個元素

還有很多,能答出三到四個就可以了,需要補充的重要方法請提醒答主。

 

80. 如何知道一個 Python 對象的類型?

demo_obj = range(1,11)    # 創建一個未知類型的對象
print(type(demo_dbj))     # 使用type()判斷對象類型

 

81. Python 的傳參是傳值還是傳址?

結論先行:Python對可變對象(字典或列表)傳址,對不可變對象(數字、字符或元祖)傳值。

#!/usr/bin/python3


def demo_func(parm):
    """輸出整數或者列表改變後的值
    
    :param parm: 整數或者列表
    :return: 
    """
    if isinstance(parm, int):
        parm += 1
    elif isinstance(parm, list):
        parm.append(1)
    print(parm)


if __name__ == '__main__':
    # 定義整數類型
    int_parm = 1
    # 函數內整數值修改(不可變類型不能修改值,其實這裏是變量另外賦值)
    demo_func(int_parm)   # 輸出爲2
    # 輸出整數值,查看對象的值是否被修改
    print(int_parm)       # 輸出爲1,值未改變,說明傳值
    # 定義列表類型
    list_patm = [1,2,3]
    # 函數修改列表
    demo_func(list_patm)   # 輸出[1, 2, 3, 1]
    # 查看函數外部列表是否發生改變
    print(list_patm)       # 輸出[1, 2, 3, 1],列表發生改變,說明傳址

 

82. Python 中的元類(metaclass)使用舉例

與77題重複

 

83. 簡述 any()和 all()方法

any()判斷一個tuple或者list是否全爲空,全空False, 不全爲空返回True,空列表和空元祖爲False;
all()判斷一個tuple或者list是否全爲非空,有一空則False, 全不空True,空列表和空元祖爲True。

 

84. filter 方法求出列表所有奇數並構造新列表,a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

list(filter(lambda x: x % 2 == 1, (i for i in a)))   # py3需要使用list()轉爲list

 

85. 什麼是猴子補丁?

猴子補丁的含義是指在動態語言中,不去改變源碼而對功能進行追加和變更。

爲什麼叫猴子補丁?

1,這個詞原來爲Guerrilla Patch,雜牌軍、游擊隊,說明這部分不是原裝的,在英文裏guerilla發音和gorllia(猩猩)相似,再後來就寫了monkey(猴子)。
2,還有一種解釋是說由於這種方式將原來的代碼弄亂了(messing with it),在英文裏叫monkeying about(頑皮的),所以叫做Monkey Patch。

使用協程時,通常在模塊頭部加入:gevent.monkey.patch_all(),用於將標準庫中的thread/socket等給替換掉,這樣我們在後面使用socket的時候可以跟平常一樣使用,無需修改任何代碼,但是它變成非阻塞的了。

總結:猴子補丁就是程序功能的追加或者變更。

網上還有一個例子

之前做的一個遊戲服務器,很多地方用的import json,後來發現ujson比自帶json快了N倍,於是問題來了,難道幾十個文件要一個個把import json改成import ujson as json嗎?
其實只需要在進程startup的地方monkey patch就行了.是影響整個進程空間的.
同一進程空間中一個module只會被運行一次.
import json
import ujson
def monkey_patch_json():
    json.__name__ = 'ujson'
    json.dumps = ujson.dumps
    json.loads = ujson.loads

monkey_patch_json()
print 'main.py',json.__name__
import sub

 

86. 在 Python 中是如何管理內存的?

Python內存池:內存池的概念就是預先在內存中申請一定數量的,大小相等 的內存塊留作備用,當有新的內存需求時,就先從內存池中分配內存給這個需求,不夠了之後再申請新的內存。這樣做最顯著的優勢就是能夠減少內存碎片,提升效率。
python中的內存管理機制——Pymalloc:python中的內存管理機制都有兩套實現,一套是針對小對象,就是大小小於256bits時,pymalloc會在內存池中申請內存空間;當大於256bits,則會直接執行new/malloc的行爲來申請內存空間。
內存釋放參考垃圾回收

 

87. 當退出 Python 時是否釋放所有內存分配?

循環引用其它對象或引用自全局命名空間的對象的模塊,在 Python 退出時並非完全釋放。

這裏我有疑惑,暫時沒有找到合理的解釋:垃圾回收中的標記清除不是可以解決循環引用的問題嗎?

 

88.使用正則表達式匹配出<html><h1>百度一下,你就知道</html>中的地址 a="張明 98 分",用 re.sub,將 98 替換爲 100

這題我看了好幾遍沒整明白,重新立一個好了

使用正則表達式匹配出 '<html><h1><div>a="張明 98 分"</div></html>' 中的地址 a="張明 98 分",用 re.sub,將 98 替換爲 100

正則一直是我的軟肋,這回一定要好好補補。。。

#!/usr/bin/python3
import re
html_str = '<html><h1><div>a="張明 98 分"</div></html>'
result_str = re.sub(r'\d{1, 2}', "100", html_str)
print(result_str)

 

89. 正則表達式匹配中(.*)和(.*?)匹配區別?

1. 什麼是貪婪匹配:貪婪匹配在匹配字符串時總是嘗試匹配儘可能多的字符。
2. 什麼是非貪婪匹配:與貪婪匹配相反,非貪婪匹配在匹配字符串時總是嘗試匹配儘可能少的字符。
3. Python裏數量詞默認是貪婪模式的,在"*","?","+","{m,n}"後面加上?,可使貪婪模式變成非貪婪模式。
#!/usr/bin/python3
import re

demo_str = "abcdacsdn"
print("原始字符串  " + demo_str)

# 非貪婪匹配
non_greedy = "a.*?d"
print("非貪婪匹配 = " + non_greedy)
pattern = re.compile(non_greedy)
restult_list = re.findall(pattern , demo_str)
print("非貪婪匹配")
print(restult_list)

# 貪婪匹配
greedy = "a.*d"
print("貪婪匹配 = " + greedy)
pattern = re.compile(greedy)
restult_list = re.findall(pattern , demo_str)
print("貪婪匹配結果")
print(restult_list)

 

90.寫一段匹配郵箱的正則表達式

#!/usr/bin/python3
import re
text = input("Please input your Email address:\n")
# 匹配任意的郵箱,@前是19位的字符數字下換線組合
if re.match(r'^[0-9a-zA-Z_]{0,19}@[0-9a-zA-Z]{1,13}\.[com,cn,net]{1,3}$',text):
    print('Email address is Right!')
else:
    print('Please reset your right Email address!')

 

91. 解釋一下Python 中 pass 語句的作用?

Python中的pass是空語句,是爲了保持程序結構的完整性;
pass不做任何事情,一般用做佔位語句;
一般在搭建程序框架的時候或在判斷語句中使用。

 

92. 簡述你對 input()函數的理解

Python3.x中輸入()函數接受一個標準輸入數據,返回爲字符串類型。
Python2.x中輸入()相等於eval(raw_input(prompt)),用來獲取控制檯的輸入。
raw_input()將所有輸入作爲字符串看待,返回字符串類型。而input()在對待純數字輸入時具有自己的特性,它返回所輸入的數字的類型(int,float)。

 

93. python 中的 is 和==

is是身份運算符,判斷兩個對象的內存id是否相等
==是比較運算符,判斷兩個對象的值是否相等
進行值比較的時候使用==,判斷是否是同一對象的時候使用is

 

94. Python 中的作用域(變量的作用域)

L (Local) 局部作用域
E (Enclosing) 閉包函數外的函數中
G (Global) 全局作用域
B (Built-in) 內建作用域
以 L –> E –> G –>B 的規則查找,即:在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內建中找。

 

95. 三元運算寫法和應用場景?

三元運算符就是在賦值變量的時候,可以直接加判斷,然後賦值格式
條件爲真時的結果 if 判段的條件 else 條件爲假時的結果
先定義變量:
a = 1
b = 2
第一種寫法:
erroStr = "More" if a > b else "Less"
print(erroStr) # 運行結果爲:Less
第二種寫法:
print({True: "More", False: "Less"}[a > b]) # 運行結果爲:Less
第三種寫法:
print(("FalseValue", "TrueValue")[a > b]) # 運行結果爲:FalseValue
其中我們比較常見的是第一種。
第二三種是挺簡潔的,但是寫在項目裏怕是接手的同事要抓狂了。

 

96. 瞭解 enumerate 麼?

enumerate() 函數用於將一個可遍歷的數據對象(如列表、元組或字符串)組合爲一個索引序列,同時列出數據和數據下標,一般用在 for 循環當中。

以下是 enumerate() 方法的語法:

enumerate(sequence, [start=0])
參數
sequence -- 一個序列、迭代器或其他支持迭代對象。
start -- 下標起始位置。
#!/usr/bin/python3
seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))       # 下標從 1 開始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

 

97. 列舉 5 個 Python 中的標準模塊

請看第一題

 

98. 如何在函數中設置一個全局變量

使用global

 

99. pathlib 的用法舉例

pathlib 模塊提供了一組面向對象的類,這些類可代表各種操作系統上的路徑,程序可通過這些類操作路徑。
#!/usr/bin/python3
from pathlib import Path

# 1.查看路徑
# 使用cmd()方法輸出當前的工作目錄
now_path = Path.cwd()
# 使用home()輸出用戶的主目錄
home_path = Path.home()
print("當前工作目錄", now_path, type(now_path))
print("home目錄", home_path, type(home_path))

# 2. 路徑拼接
# 將字符串轉爲Pathlib.Path類型
dir_path = Path(r"D:\code\web\flaskweb")
print(dir_path, type(dir_path))
# 使用 "/" 直接拼接路徑
dir_path = Path(r"D:\code\web") / "flaskweb"
print(dir_path, type(dir_path))

# 3.讀寫文件
# 使用基礎的open()函數
demo_file = Path.cwd() / 'test.md'
with open(demo_file, mode='r') as fid:
    file_data = fid.read()
print(file_data)

# 使用pathlib的open()方法
demo_file = Path.cwd() / 'test.md'
# 這樣寫的好處就是open裏面我們不需要再去傳入路徑了,
# 直接指定文件讀寫模式即可。實際上這裏的open方法,
# 底層也是調用了os.open的方法。使用哪種方式看個人的喜好。
with demo_file.open("r") as fid:
    file_data = fid.read()
print(file_data)

# 也可以不使用with open的形式即可以進行讀寫
# .read_text(): 找到對應的路徑然後打開文件,讀成str格式。等同open操作文件的"r"格式。
# .read_bytes(): 讀取字節流的方式。等同open操作文件的"rb"格式。
# .write_text(): 文件的寫的操作,等同open操作文件的"w"格式。
# .write_bytes(): 文件的寫的操作,等同open操作文件的"wb"格式

# 4.使用resolve可以通過傳入文件名,來返回文件的完整路徑
py_path =Path("demo.py")
# 需要注意的是"demo.py"文件要和我當前的程序文件在同一級目錄。
print(py_path.resolve())

更多pathlib的使用介紹請看

文件操作So Easy!來,一起體驗下Python的Pathlib模塊​blog.csdn.net

 

 

100. Python 中的異常處理,寫一個簡單的應用場景

重複:第9題

 

101. Python 中遞歸的最大次數,那如何突破呢?

最大次數爲1000次,如何突破請看:

https://code.activestate.com/recipes/474088/​code.activestate.com

 

答主表示一臉懵逼

 

102. 什麼是面向對象的 mro

MRO:Method Resolution Order(方法解析順序)
MRO就是類的方法解析順序表, 其實也就是繼承父類方法時的順序表。
MRO 是在Python多繼承和鑽石繼承問題上的核心內容,它規定了如何,什麼時候,怎麼樣去 調用父類的方法
# 輸出類的解析繼承關係順序:類名.__mro__
DemoClass.__mro__

案例:

#!/usr/bin/python3
class A(object):
    def f(self):
        print("A.f")


class B(A):
    def f(self):
        print("B.f")


class C(A):
    def f(self):
        print("C.f")


class D(B, C):
    def f(self):
        # super().f()    # 此時按照mro調用順序可知調用的是B類的方法
        # 要想調用C類的方法,查看mro之後使用super調用C的上一類
        super(B, self).f()
        # super(a_type, obj), 其中第一個實參a_type是個類對象,第二個實參obj是個實例對象
        # 再次科普:self是實例對象本身,而不是類本身

 
if __name__ == '__main__':
    print(D.__mro__)
    # (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    d = D()
    d.f()

 

103. isinstance 作用以及應用場景?

isinstance:判斷對象是否是一個已知的類型

isinstance(object, classinfo)
object -- 實例對象。
classinfo -- 可以是直接或間接類名、基本類型或者由它們組成的元組。

使用場景舉例:

判斷對象的數據類型,如參數和返回值判斷,根據不同的數據類型
判斷類的繼承關係,isinstance可以用作判斷是否繼承了某個父類

科普:type和isinstance

type只輸出當前類名,不管繼承關係
isinstance在使用當前類的父類做判斷時,輸出爲True(多重繼承適用)
class A:
    pass
 
class B(A):
    pass
 
isinstance(A(), A)    # returns True
type(A()) == A        # returns True
isinstance(B(), A)    # returns True
type(B()) == A        # returns False

 

104. 什麼是斷言?應用場景?

官方解釋:

Assert statements are a convenient way to insert debugging assertions into a program.
斷言語句是將調試斷言插入程序的便捷方式

assert condition:在condition爲True時不觸發,False觸發AssertionError錯誤

>>> assert 1==1
>>> assert 1==0
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    assert 1==0
AssertionError

如果沒有特別的目的,斷言應該用於如下情況:

  • 防禦性的編程
  • 運行時對程序邏輯的檢測
  • 合約性檢查(比如前置條件,後置條件)
  • 程序中的常量
  • 檢查文檔

 

105. lambda 表達式格式以及應用場景?

lambda表達式,通常是在需要一個函數,但是又不想費神去命名一個函數的場合下使用,也就是指匿名函數。

lambda表達式:lambda 參數1,參數2...: 參數表達式

適用場景:

簡單功能的函數實現
不需要關注函數命名
複用性不高或只用一次的函數

舉例:輸出1到100內的奇數

# 答主很喜歡用filder和lambda的組合
print(list(filter(lambda x: x % 2 == 1, range(1, 101))))

列表的排序:按照絕對值大小排序

list_demo = [3, 5, -4, -1, 0, -2, -6]
# sorted和lambda也是很好的組合,這裏的abs是絕對值函數
print(sorted(list_demo, key=lambda x: abs(x)))

閉包lambda

#!/usr/bin/python3
def get_y(a, b):
    return lambda x: a*x + b

y1 = get_y(3, 1)
print(y1(1))  # 結果爲4

 

106. 新式類和舊式類的區別

在Python 3.x中取消了經典類,默認都是新式類,並且不必顯式的繼承object,也就是說:
class Person(object):pass
class Person():pass 
class Person:pass
三種寫法並無區別,推薦第一種

 

Python2.x中,默認都是經典類,只有顯式繼承了object纔是新式類,即:
class Person(object):pass新式類寫法
class Person():pass經典類寫法
class Person:pass經典類寫法

新式類和經典類的最大的區別:繼承搜索順尋的變化

新式類多繼承搜索順序(廣度優先):先在水平方向查找,然後再向上查找
經典類多繼承搜索順序(深度優先):先深入繼承樹左側查找,然後再返回,開始查找右側

 

107. dir()是幹什麼用的?

dir()函數不帶參數時,返回當前範圍內的變量、方法和定義的類型列表;
帶參數時,返回參數的屬性、方法列表。
如果參數包含方法__dir__(),該方法將被調用。如果參數不包含__dir__(),該方法將最大限度地收集參數信息。
#!/usr/bin/python3
class A(object):
    def f(self):
        print("A.f")


if __name__ == '__main__':
    print(dir())
    # ['A', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
    print(dir(A))
    # ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'f']

 

108.一個包裏有三個模塊,demo1.py,demo2.py,demo3.py,但使用 from tools import *導入模塊時,如何保證只有 demo1、demo3 被導入了。

Python包中使用__init__.py確認導入的包

 

109. 列舉 5 個 Python 中的異常類型以及其含義

BaseException  # 所有異常的基類
 +-- SystemExit  # 解釋器請求退出
 +-- KeyboardInterrupt  # 用戶中斷執行(通常是輸入^C)
 +-- GeneratorExit  # 生成器(generator)發生異常來通知退出
 +-- Exception  # 常規異常的基類
      +-- StopIteration  # 迭代器沒有更多的值
      +-- StopAsyncIteration  # 必須通過異步迭代器對象的__anext__()方法引發以停止迭代
      +-- ArithmeticError  # 各種算術錯誤引發的內置異常的基類
      |    +-- FloatingPointError  # 浮點計算錯誤
      |    +-- OverflowError  # 數值運算結果太大無法表示
      |    +-- ZeroDivisionError  # 除(或取模)零 (所有數據類型)
      +-- AssertionError  # 當assert語句失敗時引發
      +-- AttributeError  # 屬性引用或賦值失敗
      +-- BufferError  # 無法執行與緩衝區相關的操作時引發
      +-- EOFError  # 當input()函數在沒有讀取任何數據的情況下達到文件結束條件(EOF)時引發
      +-- ImportError  # 導入模塊/對象失敗
      |    +-- ModuleNotFoundError  # 無法找到模塊或在在sys.modules中找到None
      +-- LookupError  # 映射或序列上使用的鍵或索引無效時引發的異常的基類
      |    +-- IndexError  # 序列中沒有此索引(index)
      |    +-- KeyError  # 映射中沒有這個鍵
      +-- MemoryError  # 內存溢出錯誤(對於Python 解釋器不是致命的)
      +-- NameError  # 未聲明/初始化對象 (沒有屬性)
      |    +-- UnboundLocalError  # 訪問未初始化的本地變量
      +-- OSError  # 操作系統錯誤,EnvironmentError,IOError,WindowsError,socket.error,select.error和mmap.error已合併到OSError中,構造函數可能返回子類
      |    +-- BlockingIOError  # 操作將阻塞對象(e.g. socket)設置爲非阻塞操作
      |    +-- ChildProcessError  # 在子進程上的操作失敗
      |    +-- ConnectionError  # 與連接相關的異常的基類
      |    |    +-- BrokenPipeError  # 另一端關閉時嘗試寫入管道或試圖在已關閉寫入的套接字上寫入
      |    |    +-- ConnectionAbortedError  # 連接嘗試被對等方中止
      |    |    +-- ConnectionRefusedError  # 連接嘗試被對等方拒絕
      |    |    +-- ConnectionResetError    # 連接由對等方重置
      |    +-- FileExistsError  # 創建已存在的文件或目錄
      |    +-- FileNotFoundError  # 請求不存在的文件或目錄
      |    +-- InterruptedError  # 系統調用被輸入信號中斷
      |    +-- IsADirectoryError  # 在目錄上請求文件操作(例如 os.remove())
      |    +-- NotADirectoryError  # 在不是目錄的事物上請求目錄操作(例如 os.listdir())
      |    +-- PermissionError  # 嘗試在沒有足夠訪問權限的情況下運行操作
      |    +-- ProcessLookupError  # 給定進程不存在
      |    +-- TimeoutError  # 系統函數在系統級別超時
      +-- ReferenceError  # weakref.proxy()函數創建的弱引用試圖訪問已經垃圾回收了的對象
      +-- RuntimeError  # 在檢測到不屬於任何其他類別的錯誤時觸發
      |    +-- NotImplementedError  # 在用戶定義的基類中,抽象方法要求派生類重寫該方法或者正在開發的類指示仍然需要添加實際實現
      |    +-- RecursionError  # 解釋器檢測到超出最大遞歸深度
      +-- SyntaxError  # Python 語法錯誤
      |    +-- IndentationError  # 縮進錯誤
      |         +-- TabError  # Tab和空格混用
      +-- SystemError  # 解釋器發現內部錯誤
      +-- TypeError  # 操作或函數應用於不適當類型的對象
      +-- ValueError  # 操作或函數接收到具有正確類型但值不合適的參數
      |    +-- UnicodeError  # 發生與Unicode相關的編碼或解碼錯誤
      |         +-- UnicodeDecodeError  # Unicode解碼錯誤
      |         +-- UnicodeEncodeError  # Unicode編碼錯誤
      |         +-- UnicodeTranslateError  # Unicode轉碼錯誤
      +-- Warning  # 警告的基類
           +-- DeprecationWarning  # 有關已棄用功能的警告的基類
           +-- PendingDeprecationWarning  # 有關不推薦使用功能的警告的基類
           +-- RuntimeWarning  # 有關可疑的運行時行爲的警告的基類
           +-- SyntaxWarning  # 關於可疑語法警告的基類
           +-- UserWarning  # 用戶代碼生成警告的基類
           +-- FutureWarning  # 有關已棄用功能的警告的基類
           +-- ImportWarning  # 關於模塊導入時可能出錯的警告的基類
           +-- UnicodeWarning  # 與Unicode相關的警告的基類
           +-- BytesWarning  # 與bytes和bytearray相關的警告的基類
           +-- ResourceWarning  # 與資源使用相關的警告的基類。被默認警告過濾器忽略。

 

110. copy 和 deepcopy 的區別是什麼?

  • copy 僅拷貝對象本身,而不拷貝對象中引用的其它對象。
  • deepcopy 除拷貝對象本身,而且拷貝對象中引用的其它對象。(子對象)
copy不會爲子對象額外創建新的內存空間,當子對象被修改之後,這個子對象的引用都會發生改變;
deepcopy是一個新對象的創建,只是用了和被拷貝對象相同的值,子對象改變不會影響被拷貝對象

 

111. 代碼中經常遇到的*args, **kwargs 含義及用法。

args 是 arguments 的縮寫,表示位置參數;
kwargs 是 keyword arguments 的縮寫,表示關鍵字參數
def demo_func(*args, **kwargs):
    # arg是一個元祖類型
    print(args[1])
    # kwargs是一個字典類型
    print(kwargs.keys())


if __name__ == '__main__':
    # 直接傳參,但關鍵字類型必須爲str
    demo_func(1, 2, 3, a=1, b=2)
    # 使用*和**進行解包
    demo_func(*(1, 2, 3), **{"a": 1, "b": 2})

 

112. Python 中會有函數或成員變量包含單下劃線前綴和結尾,和雙下劃線前綴結尾,區別是什麼?

單下劃線
單下劃線開頭的命名方式被常用於模塊中,在一個模塊中以單下劃線開頭的變量和方法會被默認劃入模塊內部範圍。當使用 from my_module import * 導入時,單下劃線開頭的變量和方法是不會被導入的。但使用 import my_module 導入的話,仍然可以用 my_module._var 這樣的形式訪問屬性或方法。
單下劃線結尾的命名方式也存在,但是不常用,其實也不推薦用。這種命名方式的作用就是爲了和 python 的一些內置關鍵詞區分開來,假設我們想給一個變量命名爲 class,但是這會跟 python 的關鍵詞 class 衝突,所以我們只好退一步使用單下劃線結尾命名,也就是 class_。
雙下劃線
雙下劃線開頭和結尾的是一些 python 的“魔術”對象,如類成員的 __init__、__del__、__add__、__getitem__ 等,以及全局的__file__、__name__ 等。 python 官方推薦永遠不要將這樣的命名方式應用於自己的變量或函數,而是按照文檔說明來使用。
雙下劃線開頭的命名方式有實際的作用,採用這種命名的變量或方法無法直接通過 “對象名.變量名(方法名)” 這樣的方式訪問。

 

113. w、a+、wb 文件寫入模式的區別

r : 讀取文件,若文件不存在則會報錯
w: 寫入文件,若文件不存在則會先創建再寫入,會覆蓋原文件
a : 寫入文件,若文件不存在則會先創建再寫入,但不會覆蓋原文件,而是追加在文件末尾
rb,wb:分別於r,w類似,用於讀寫二進制文件
r+ : 可讀、可寫,文件不存在也會報錯,寫操作時會覆蓋
w+ : 可讀,可寫,文件不存在先創建,會覆蓋
a+ :可讀、可寫,文件不存在先創建,不會覆蓋,追加在末尾

 

114. 舉例 sort 和 sorted 的區別

demo_list = [1, 3, 4, 2, 7, 5]
# sorted是一個函數,返回一個新的list
result_list = sorted(demo_list)
print(result_list)
# sort是實例方法,直接作用在list本身,沒有返回新的list
demo_list.sort()
print(demo_list)

 

115. 什麼是負索引?

負索引是指使用負數做爲索引,-1代表數組的最後一位

 

116. pprint 模塊是幹什麼的?

# pprint用於輸出一個整齊美觀Python數據的結構
import pprint

demo_list = [str(i)*20 for i in range(10)]
# indent是指句首縮進
pp_object = pprint.PrettyPrinter(indent=4)
pp_object.pprint(demo_list)  # 整齊輸出
print(demo_list)             # 只輸出一行

 

117. 解釋一下 Python 中的賦值運算符

 

118. 解釋一下 Python 中的邏輯運算符

 

119. 講講 Python 中的位運算符

 

120. 在 Python 中如何使用多進制數字?

1、二進制數字由0和1組成,我們使用0b或0B前綴表示二進制數
print(int(0b1010))  #10
2、使用bin()函數將一個數字轉換爲它的二進制形式
print(bin(0xf))  #0b1111
3、八進制數由數字0-7組成,用前綴0o或0O表示8進制數
print(oct(8))  #0o10
4、十六進數由數字0-15組成,用前綴0x或者0X表示16進制數
print(hex(16))  #0x10

print(hex(15))  #0xf

 

121. 怎樣聲明多個變量並賦值?

a, b, = 1, 2

 

122.已知:Alist = [1, 2, 3] Bset = {1, 2, 3}

(1) 從 AList 和 BSet 中 查找 4,最壞時間複雜度那個大?

(2) 從 AList 和 BSet 。中 插入 4,最壞時間複雜度那個大?

  • python的列表內部實現是數組(具體實現要看解析器, CPython的實現 ),因此就有數組的特點。超過容量會增加更多的容量,set, get 是O(1),但del, insert, in的性能是O(n)。
  • 關於字典需要了解的是hash函數和哈希桶。一個好的hash函數使到哈希桶中的值只有一個,若多個key hash到了同一個哈希桶中,稱之爲哈希衝突。查找值時,會先定位到哈希桶中,再遍歷hash桶。更詳細的信息請點這裏。在hash基本沒有衝突的情況下get, set, delete, in方面都是O(1)。
  • 集合內部實現是dict的。在in操作上是O(1), 這一點比list要強。

由此可知:

(1)查找操作set優於list;

(2)插入操作兩個相同。

 

123. 用 Python 實現一個二分查找的函數

def binary_Search(search_list: list, search_num: int):
    """二分查找

    利用二分法找到list數組中的值
    :param search_list: 目標list
    :param search_num: 待查詢值
    :return:
    """
    # 最小的下標
    min_index = 0
    # 最大的下標
    max_index = len(search_list) - 1
    # 當前索引下標
    now_index = 0
    while True:
        # 中間的下標每次向下取整
        mid_index = (min_index + max_index) // 2
        if search_num > search_list[mid_index]:
            # 小於需要的猜的數,則將最小下標變爲中間的,因爲中間的已經猜過,所以要加1
            min_index = mid_index + 1
        elif search_num == search_list[mid_index]:
            print("找到數據", "索引是{}".format(mid_index))
            print("一共查找{}次".format(now_index))
            break
        else:
            # 大於需要的猜的數,則將最大下標變爲中間的,因爲中間的已經猜過,所以要減1
            max_index = mid_index - 1
        # 索引值加一
        now_index += 1


if __name__ == "__main__":
    list1 = [i for i in range(0, 1000)]
    num = 0
    binary_Search(list1, num)

 

124.python 單例模式的實現方法

class SingleCase(object):
    _instance = None
    
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance


if __name__ == '__main__':
    a = SingleCase()
    b = SingleCase()

 

125. 使用 Python 實現一個斐波那契數列

#!/usr/bin/python3

def fbnq(num):
    """斐波那契生成器

    :param num: 生產數量
    :return: 斐波那契迭代器
    """
    a, b = 1, 1
    for _ in range(num):
        a, b = b, a+b
        yield a


if __name__ == '__main__':
    gener = fbnq(20)
    print(gener)
    for i in gener:
        print(i)

 

126. 找出列表中的重複數字 127. 找出列表中的單個數字

#!/usr/bin/python3
from collections import Counter
result = Counter([1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4])
print(result)
# Counter({2: 4, 4: 4, 3: 3, 1: 1})

 

128. 寫一個冒泡排序

def bubble_sort(parm_list):
    """冒泡排序

    冒泡排序思路:判斷前後兩個值的大小,若前大於後則調換兩個值位置
    每一輪循環都可以將最大值放到末尾,所以需要迭代次數爲數組的大小
    因爲每次都將最大值放到最後,所以內層迭代就不需要全部檢測一遍
    :param parm_list:
    :return:
    """
    for n in range(len(parm_list)):
        for now_index in range(len(parm_list)-n-1):
            if parm_list[now_index] > parm_list[now_index + 1]:
                parm_list[now_index], parm_list[now_index + 1] = \
                    parm_list[now_index + 1], parm_list[now_index]
    print(parm_list)


if __name__ == '__main__':
    bubble_sort([1, 2, 3, 7, 5, 4, 6])

 

129. 寫一個快速排序

def quick_sort(parm_list):
    """快排

    每次選取第一個值爲基準值,再把列表中比基準值大的組成新列表,小的組成另一個新列表
    再次對兩個新列表進行操作,直到新列表爲空
    :param parm_list: 參數列表
    :return:
    """
    if not parm_list:
        return []
    else:
        pivot = parm_list[0]
        # 利用遞歸每次找出大於和小於基準值得兩個新列表
        lesser = quick_sort([x for x in parm_list[1:] if x < pivot])
        greater = quick_sort([x for x in parm_list[1:] if x >= pivot])
        # 最後將排列好的值相加
        return lesser + [pivot] + greater


if __name__ == '__main__':
    demo_list = [4, 23, 5, 6, 43, 14, 9, -23, 2, 6, 123, 12, 3, 3, 3, 3, 1]
    print(quick_sort(demo_list))

 


 

在線編輯好卡啊,csdn能把草稿自動保存關掉嗎o(╥﹏╥)o

持續更新中...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章