python 基礎知識記錄

@ 談談對 Python 和其他語言的區別

  • Python 是一門語法簡潔優美,功能強大無比,應用領域非常廣泛,具有強大完備的第三方庫,他是一門強類型的可移植、可擴展,可嵌入的解釋型編程語言,屬於動態語言。

  • 拿 C 語言和 Python 比: Python 的第三方類庫比較齊全並且使用簡潔,很少代碼就能實現一些功能,如果用 C 去實現相同的功能可能就比較複雜。但是對於速度來說 Python 的運行速度相較於 C 就比較慢了。所以有利的同時也有弊端,畢竟我們的學習成本降低了。

@ 簡述解釋型和編譯型編程語言

  • 解釋型語言是在運行程序的時候才翻譯,每執行一次,要翻譯一次,效率較低。 編譯型就是直接編譯成機型可以執行的,只翻譯一次,所以效率相對來說較高。

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

  1. CPython c 語言開發的,使用最廣的解釋器

  2. IPython 基於 cPython 之上的一個交互式計時器,交互方式增強功能和 cPython 一樣

  3. PyPy 目標是執行效率,採用 JIT 技術。對 Python 代碼進行動態編譯,提高執行效率

  4. JPython 運行在 Java 上的解釋器,直接把 Python 代碼編譯成 Java 字節碼執行

  5. IronPython 運行在微軟 .NET 平臺上的解釋器,把 Python 編譯成 . NET 的字節碼。

@ Python3 和 Python2 的區別?

  • 這裏例舉 5 條

1、 print 在 Python3 中是函數必須加括號,Python2 中 print 爲 class。

2、 Python2 中使用 xrange,Python3 使用 range。

3、 Python2 中默認的字符串類型默認是 ASCII,

Python3 中默認的字符串類型是Unicode。

4、 Python2 中 / 的結果是整型,Python3 中是浮點類型。

5、 Python2 中聲明元類:_metaclass_ = MetaClass,

Python3 中聲明元類:class newclass(metaclass=MetaClass):pass。

@ Python3 和 Python2 中 int 和 long 區別?

  • Python2 有 int 和 long 類型。int 類型最大值不能超過 sys.maxint,而且這個最大值是平臺相關的。可以通過在數字的末尾附上一個L來定義長整型,顯然,它比 int 類型表示的數字範圍更大。

  • 在 Python3 裏,只有一種整數類型 int,大多數情況下,和 Python2中的長整型類似。

@ xrange 和 range 的區別?

  • xrange 是在 Python2 中的用法,Python3 中只有 range xrange 用法與 range 完全相同,所不同的是生成的不是一個 list 對象,而是一個生成器。

@ 編碼規範

  • 什麼是 PEP8?

  • PEP8 通常會聽別人提到,但是具體的指什麼內容呢,簡單介紹下。 《Python Enhancement Proposal #8》(8 號 Python 增強提案)又叫 PEP8,他針對的 Python 代碼格式而編訂的風格指南。

@ 瞭解 Python 之禪麼?

  • 通過 import this 語句可以獲取其具體的內容。它告訴大家如何寫出高效整潔的代碼。

@ 瞭解 DocStrings 麼?

  • DocStrings 文檔字符串是一個重要工具,用於解釋文檔程序,幫助你的程序文檔更加簡單易懂。主要是解釋代碼作用的。

  • 函數的 __doc__ 變量內容就是這個DocStrings

def doc_string_test():
    ''' 本函數用來測試 DocStrings 
        DocStrings 文檔字符串用於解釋文檔程序,
        主要是解釋代碼作用的。
        類和模塊都能用。
    '''
    pass

print(doc_string_test.__doc__)

@ 瞭解類型註解麼?

答:PEP 484 引入了類型提示,這使得可以對 Python 代碼進行靜態類型檢查。 在使用 Ide 的時候可以獲取到參數的類型,更方便傳入參數。使用格式如下

def foo(num: int) -> None:
    print(f"接收到的數字是:{num}")

介紹下這個簡單例子,我們可以在函數的參數部分使用參數名+:+類型,來指定參數可以接受的類型,這裏的話就是 num 參數爲 int 類型,然後後面->接的是返回值的類型。這裏返回值爲 None,然後通過 fstring 格式化字符串輸出傳入的數字。

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

  • 類:總是使用首字母大寫單詞串,如 MyClass。內部類可以使用額外的前導下劃線。

  • 變量:小寫,由下劃線連接各個單詞。方法名類似

  • 常量:常量名所有字母大寫 等

@ Python 中的註釋有幾種?

  • 總體來說分爲兩種,單行註釋和多行註釋。

  • 單行註釋在行首是 #

  • 多行註釋可以使用三個單引號或三個雙引號,包括要註釋的內容。

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

  • 可以使用 docstring 配合類型註解

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

  • 不允許 tab 鍵和空格鍵混用,這種現象在使用 sublime 的時候尤爲明顯。

  • 一般推薦使用 4 個空格替代 tab 鍵。

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

  • 可以是可以,但是不推薦。因爲一次導入多個模塊可讀性不是很好

  • 一行導入一個模塊會比較好。

  • 同樣的儘量少用 from modulename import *,因爲判斷某個函數或者屬性的來源有些困難,不方便調試,可讀性也降低了。

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

  • 給文件命名的時候不要和標準庫中的一些模塊重複,比如 abc。

  • 另外要名字要有意義,不建議數字開頭或者中文命名。

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

  • pylint

  • flake8

數據類型-字符串

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

  • Python3 中有六個標準的數據類型

  1. 字符串(String)

  2. 數字(Digit)

  3. 列表(List)

  4. 元組(Tuple)

  5. 集合(Sets)

  6. 字典(Dictionary)

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

  • 從對象內存地址方向來說

  1. 可變數據類型:在內存地址不變的情況下,值可改變(列表和字典是可變類型,但是字典中的 key 值必須是不可變類型)

  2. 不可變數據類型:內存改變,值也跟着改變。(數字,字符串,布爾類型,都是不可變類型)可以通過 id() 方法進行內存地址的檢測。

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

  • 這個得看清題目是要求兩個單詞首字母都要大寫

print("hello world".title())
  1. title() 方法返回"標題化"的字符串,就是說所有單詞都是以大寫開始,其餘字母均爲小寫(見 istitle())

  2. 補充: istitle() 方法檢測字符串中所有的單詞拼寫首字母是否爲大寫,且其他字母爲小寫。

  3. 如果字符串中所有的單詞拼寫首字母是否爲大寫,且其他字母爲小寫則返回 True,否則返回 False.

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

  • 可以通過 isdigit 方法,例子如下

s1 = "12223".isdigit()
print(s1)

s2 = "12223a".isdigit()
print(s2)

#結果如下:
#True
#False

@ 將字符串"hello world"進行反轉

s1 = "hello world"[::-1]
print(s1)

@ Python 中的字符串格式化方式你知道哪些?

  • %s,

  • format,

  • fstring(Python3.6 開始才支持,現在推薦的寫法)

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

  • 因爲題目要是寫一個函數所以我們不能直接使用 strip,不過我們可以把它封裝到函數啊

def strip_function(s1):
    return s1.strip()

s1 = " adabdw "
print(strip_function(s1))

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

  • 切片使用的考察,最後兩個即開始索引是 -2,代碼如下

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

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

a= "S".encode("gbk").decode("utf-8",'ignore')
print(a)

@ 怎樣將字符串轉換爲小寫

  • 使用字符串的 lower() 方法。

@ 單引號、雙引號、三引號的區別?

  • 單獨使用單引號和雙引號沒什麼區別,但是如果引號裏面還需要使用引號的時候,就需要這兩個配合使用了,

  • 然後說三引號,同樣的三引號也分爲三單引號和三雙引號,兩個都可以聲名長的字符串時候使用,如果使用

docstring 就需要使用三雙引號


數據類型 - 列表

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

list(set(AList))

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

s = "1,2,3"
print(s.split(","))

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

  • A、B 中相同元素:print(set(A)&set(B))

  • A、B 中不同元素:print(set(A)^set(B))

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

l = [[1,2],[3,4],[5,6]]
x=[j for i in l for j in i]  
print(x)

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

# 使用 extend 和 + 都可以。
a = [1,5,7,9]
b = [2,2,6,8]
a.extend(b)
print(a)

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

import random

a = [1, 2, 3, 4, 5]
random.shuffle(a)
print(a)

數據類型 - 字典

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

  • del 可以根據索引(元素所在位置)來刪除的,沒有返回值。

  • pop 可以根據索引彈出一個值,然後可以接收它的返回值。

@ 按照字典的內的年齡排序

d1 = [
    {'name': 'alice', 'age': 38},
    {'name': 'bob', 'age': 18},
    {'name': 'Carl', 'age': 28},
]
print(sorted(d1, key=lambda x: x["age"]))

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

  • 合併字典方法很多,可以使用 a.update(b) 或者下面字典解包的方式

a = {"A":1,"B":2}
b = {"C":3,"D":4}
print({**a,**b})

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

# 需求 3: 把字典的 key 和 value 值調換;
d = {'a':'1', 'b':'2'}
print({v: k for k, v in d.items()})

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

  • zip 的使用,但是最後記得把 zip 對象再轉換爲字典。

a = ('a', 'b')
b = (1, 2)
print(dict(zip(a, b)))

數據類型 - 綜合

  • 下列字典對象鍵類型不正確的是?

A:{1: 0,2: 0,3: 0}
B:{"a": 0, "b": 0, "c": 0}
C:{(1,2): 0, (2,3): 0}
D:{[1,2]:0, [2,3]:0}

D 因爲只有可 hash 的對象才能做字典的鍵,列表是可變類型不是可 hash 對象,所以不能用列表做爲字典的鍵。

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

s =  {"A": 1,"B": 2}
#方法一:
dict_new = {value:key for key, value in s.items()}
# 方法二:
new_s = dict(zip(s.values(), s.keys()))

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

  • Python 中的類型轉換,一般通過類型強轉即可完成 tuple 轉 list 是 list() 方法 list 轉 tuple 使用 tuple() 方法

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

答: 這個題目考察了 Python 標準庫的 itertools 模快的掌握情況,該模塊提供了操作生成器的一些方法。 對於生成器類型我們使用 islice 方法來實現切片的功能。例子如下

from itertools import islice
gen = iter(range(10)) #iter()函數用來生成迭代器
#第一個參數是迭代器,第二個參數起始索引,第三個參數結束索引,不支持負數索引
for i in islice(gen,0,4): 
    print(i)

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

  • 通過把列表生產式的中括號,改爲小括號我們就實現了生成器的功能即,

(i for i in range(3))

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

  • 這個題目一共三種方式,

  1. 在字符串的前面加一個 b

  2. 可以使用 bytes 方法

  3. 第三種使用字符串 encode 方法

  • 具體代碼如下,abc 代表三種情況

a = b"hello"
b = bytes("你好", "utf-8")
c = "你好".encode("utf-8")
print(a, b, c)

@ {1, 2, 3} 的數據類型

  • set型,無序不重複集合

  • print({5,5,5}) 輸出 {5}

@ 如果創建空的set類型集合,和空字典怎麼區分呢

  • 規定定義空無序集合,只能用a =set()

@ 下面的代碼輸出結果是什麼?]

a = (1,2,3,[4,5,6,7],8)
a[2] = 2
  • 我們知道元組裏的元素是不能改變的所以這個題目的答案是出現異常。

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

a = (1,2,3,[4,5,6,7],8)
a[3][0] = 2
  • 前面我說了元組的裏元素是不能改變的,這句話嚴格來說是不準確的,如果元組裏面元素本身就是可變類型,比如列表,那麼在操作這個元素裏的對象時,其內存地址也是不變的。a[3] 對應的元素是列表,然後對列表第一個元素賦值,所以最後的結果是: (1,2,3,[2,5,6,7],8)

操作類題目

  • Python 交換兩個變量的值

  1. 在 Python 中交換兩個對象的值通過下面的方式即可

a , b = b ,a 
  • 但是需要強調的是這並不是元組解包,

  • 通過 dis 模塊可以發現,這是交換操作的字節碼是 ROT_TWO,意思是在棧的頂端做兩個值的互換操作。

@ 字符轉換

a = ord('A')  # 字串轉數值
c = chr(96)  # 數值轉字符

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

  • read() 每次讀取整個文件,它通常用於將文件內容放到一個字符串變量中。

  • 如果希望一行一行的輸出那麼就可以使用 readline(),該方法會把文件的內容加載到內存,所以對於對於大文件的讀取操作來說非常的消耗內存資源,

  • 此時就可以通過 readlines 方法,將文件的句柄生成一個生產器,然後去讀就可以了。

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

  • 可以處理的數據類型是 str、int、list、tuple、dict、bool、None, 因爲 datetime 類不支持 json 序列化,所以我們對它進行拓展。

# 自定義時間序列化
import json
from datetime import datetime, date

# JSONEncoder 不知道怎麼去把這個數據轉換成 json 字符串的時候
# ,它就會去調 default()函數,所以都是重寫這個函數來處理它本身不支持的數據類型,
# default()函數默#認是直接拋異常的。
class DateToJson(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(obj, date):
            return obj.strftime('%Y-%m-%d')
        else:
            return json.JSONEncoder.default(self, obj)


d = {'name': 'cxa', 'data': datetime.now()}
print(json.dumps(d, cls=DateToJson))

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

  • 可以通過 json.dumps 的 ensure_ascii 參數解決,代碼示例如下:

import json
a=json.dumps({"name":"張三"},ensure_ascii=False)
print(a)

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

#文件 A.txt 內容爲 ASDCF
#文件 B.txt 內容爲 EFGGTG
with open("A.txt") as f1:
    f1_txt = f1.readline()
with open("B.txt") as f2:
    f2_txt = f2.readline()
f3_txt = f1_txt + f2_txt

f3_list = sorted(f3_txt)

with open("C.txt", "a+") as f:
     f.write("".join(f3_list))
  • 輸出的文件 C 的內容爲 ACDEFFGGGST

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

  • 這個題目考察的是 datetime 裏的 timedelta 方法的使用,參數可選、默認值都爲 0:

datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)

通過這個參數可以指定不同的日期類型進行加減操作,這裏我們需要改的是 days,代碼如下

import datetime

def datetime_operate(n: int):
    now = datetime.datetime.now()  # 獲取當前時間
    _new_date = now + datetime.timedelta(days=n)  # 獲取指定天數後的新日期
    new_date = _new_date.strftime("%Y%m%d")  # 轉換爲指定的輸出格式
    return new_date
    
if __name__ == '__main__':
    print(datetime_operate(4))

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

  • 這個題目考查了閉包的使用代碼示例如下,返回函數之類型是函數對象。

    def mul_operate(num):
        def g(val):
            return num * val
        return g
    
    m = mul_operate(8)
    print(m(5))

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

def strappend(num):
    str='first'
    for i in range(num):
        str+=str(i)
    return str
  • 首先不應該使用 Python 的內置類似 str 作爲變量名這裏我把它改爲了 s,

  • 另外在Python,str 是個不可變對象,每次迭代都會生成新的存儲空間

  • num 越大,創建的 str 對象就會越多,內存消耗越大。使用 yield 改成生成器即可,

  • 還有一點就是命名規範的位置,函數名改爲_分割比較好,完整的代碼如下:

    def str_append(num):
        s = 'first'
        for i in range(num):
            s += str(i)
            yield s
    
    if __name__ == '__main__':
        for i in str_append(3):
            print(i)

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

  • 可以通過列表生成式,然後使用與操作如果如 1 與之後結果爲 0 則表明爲偶數,等於 1 則爲奇數。

    # 方法1
    print([i for i in range(1, 101) if i & 0x1 == 0])
    # 方法2:測試發現方法二效率更高
    print(list(range(2, 101, 2)))
    # 方法3:
    print([i for i in range(1, 101) if i % 2 == 0])

@ 對 for 的疑問?

  • in 後邊的代碼是隻執行一次還是每次都會執行?

# 實驗1:如果能正常輸出,就說明range(len)),每次執行,報錯則執行一次
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in range(len(a)):
    if i % 2 is 0:
        del(a[i])
print(a)
# 結果:報錯,說明只執行一次,符合預期
  • 爲了讓上代碼運行過,於是換個方式實現

    # 實驗2
    a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    for i, v in enumerate(a):
        if i % 2 is 0:
            del(a[i])
    print(a)
    # 結果:[1, 2, 4, 5, 7, 8]
  • 思考:實驗2竟然運行過了,那不是說明 enumerate(a) 每次都執行?返回才被更新了?

    # 實驗3:驗證enumerate(a)生成的值是否固定
    a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    del(a[5])
    d = enumerate(a)
    print([k for i, k in d])
    # 結果:[0, 1, 2, 3, 4, 6, 7, 8, 9] - 5沒了
  • 結論:

  1. for 循環 in 後邊的代碼只執行一次

  2. enumerate(a) 返回的值不是固定的,會根據a實際情況變,所以使用的時候要注意這點

@ 對 enumerate(a) 的疑問?

  • 如果每次調用 enumerate(a) 的值會根據 a 變,那變化的規律是什麼?

    # 實驗4:打印看下單步運行結果
    a = [str(i) for i in range(10)]
    for i, v in enumerate(a):
        print("執行前-", a)
        if i % 2 is 0:
            del(a[i])
        print("執行後*", a, i, v, type(v))
  • 運行結果

    執行前- ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    執行後* ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 0 0 <class 'str'>
    執行前- ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    執行後* ['1', '2', '3', '4', '5', '6', '7', '8', '9'] 1 2 <class 'str'>
    執行前- ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    執行後* ['1', '2', '4', '5', '6', '7', '8', '9'] 2 3 <class 'str'>
    執行前- ['1', '2', '4', '5', '6', '7', '8', '9']
    執行後* ['1', '2', '4', '5', '6', '7', '8', '9'] 3 5 <class 'str'>
    執行前- ['1', '2', '4', '5', '6', '7', '8', '9']
    執行後* ['1', '2', '4', '5', '7', '8', '9'] 4 6 <class 'str'>
    執行前- ['1', '2', '4', '5', '7', '8', '9']
    執行後* ['1', '2', '4', '5', '7', '8', '9'] 5 8 <class 'str'>
    執行前- ['1', '2', '4', '5', '7', '8', '9']
    執行後* ['1', '2', '4', '5', '7', '8'] 6 9 <class 'str'>
  • 結論:

  1. 代碼中 enumerate(a) 返回結果是:[(0, a[0]), (1, a[1])......]

  2. i 的值固定 +1,不會根據列表內容變

  3. 每次得迭代值是 i,a[i], 因爲 a 得值變了,所以返回得 a[i] 就跟着變

  4. 猜測迭代結束得條件是:1、列表迭代完成;2、迭代值出錯(a[i]不成立)

  5. 因爲a[i] 的 i 超出列表範圍時迭代結束,所以函數能正常執行不出錯

@ for 迭代的規律是什麼?

  • 爲什麼 range(len(a)) 出來的值不會根據 a 的變化變

    # 實驗5:測試for迭代的規律
    a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    b = range(len(a))
    print(a == b)
    for i in a:
        print(a, i)
        del(a[i])
        print(a)

輸出結果:

False
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 0
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9] 2
[1, 2, 4, 5, 6, 7, 8, 9]
[1, 2, 4, 5, 6, 7, 8, 9] 4
[1, 2, 4, 5, 7, 8, 9]
[1, 2, 4, 5, 7, 8, 9] 5
[1, 2, 4, 5, 7, 9]
[1, 2, 4, 5, 7, 9] 7
Traceback (most recent call last):
  File "d:/code/test/main.py", line 50, in <module>
    del(a[i])
IndexError: list assignment index out of range
  • 結論

  1. range(len(a)) 返回結果不是 a, 所以實驗1中 a 變不會影響到迭代次數

  2. for 迭代類似 a[i++],i 的值每次都會加1,所以 a 的值變化會影響迭代返回值

  3. for 迭代過程一開始就已經設置好迭代次數,不會因爲 a 內容少了就減少

  4. a內容減少後迭代超出範圍報錯,說明 for 的迭代停止只受最開始計算的次數決定。

  5. enumerate(a) 出錯停止迭代,for 出錯直接報錯

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