1?fromkeys()函數是什么
在 Python 中,fromkeys()
?是字典(dict
)的一個類方法,用于創建一個新字典。
它的作用是:根據指定的可迭代對象(如列表、元組等)中的元素作為鍵(key),并為所有鍵設置一個默認的初始值(value),從而快速創建一個新字典。
語法格式:
python
運行
dict.fromkeys(iterable, value=None)
參數說明:
iterable
:必需參數,一個可迭代對象(如列表、元組、字符串等),其元素將作為新字典的鍵。value
:可選參數,為所有鍵設置的默認值,默認為?None
。
返回值:一個新的字典。
示例:
python
運行
# 用列表作為可迭代對象,創建新字典,默認值為 None
keys = ['name', 'age', 'gender']
new_dict = dict.fromkeys(keys)
print(new_dict) # 輸出: {'name': None, 'age': None, 'gender': None}# 為所有鍵設置相同的初始值
new_dict2 = dict.fromkeys(keys, 'unknown')
print(new_dict2) # 輸出: {'name': 'unknown', 'age': 'unknown', 'gender': 'unknown'}# 用字符串作為可迭代對象
str_keys = 'abc'
new_dict3 = dict.fromkeys(str_keys, 0)
print(new_dict3) # 輸出: {'a': 0, 'b': 0, 'c': 0}
注意事項:
如果?value
?是可變對象(如列表、字典等),則所有鍵會共享同一個對象的引用,修改其中一個鍵的值會影響其他鍵。例如:
python
運行
keys = ['a', 'b', 'c']
value = [] # 可變對象
my_dict = dict.fromkeys(keys, value)my_dict['a'].append(1)
print(my_dict) # 輸出: {'a': [1], 'b': [1], 'c': [1]}
此時所有鍵的值都會被修改,因為它們指向同一個列表對象。如果需要避免這種情況,通常需要用其他方式(如循環)為每個鍵創建獨立的可變對象。
=> 如果希望每個鍵對應不同的初始值(尤其是可變對象)的做法
如果希望每個鍵對應不同的初始值(尤其是可變對象),可以通過循環遍歷鍵列表,為每個鍵單獨賦值。這樣能確保每個值都是獨立的對象,修改其中一個不會影響其他。
方法 1:使用普通 for 循環
python
運行
keys = ['a', 'b', 'c']
my_dict = {}# 為每個鍵設置不同的初始值(這里用索引區分)
for i, key in enumerate(keys):my_dict[key] = [i] # 每個鍵對應一個獨立的列表print(my_dict) # 輸出: {'a': [0], 'b': [1], 'c': [2]}# 修改其中一個值,其他不受影響
my_dict['a'].append(100)
print(my_dict) # 輸出: {'a': [0, 100], 'b': [1], 'c': [2]}
方法 2:使用字典推導式(更簡潔)
字典推導式可以一行代碼實現上述功能,適合簡單場景:
python
運行
keys = ['a', 'b', 'c']# 為每個鍵設置不同的值(例如基于鍵名的處理)
my_dict = {key: [key.upper()] for key in keys}print(my_dict) # 輸出: {'a': ['A'], 'b': ['B'], 'c': ['C']}# 修改一個值,其他不變
my_dict['a'].append('first')
print(my_dict) # 輸出: {'a': ['A', 'first'], 'b': ['B'], 'c': ['C']}
方法 3:根據條件動態生成不同值
如果值的差異更復雜(比如依賴外部數據),可以結合條件判斷:
python
運行
keys = ['a', 'b', 'c']
values = [10, 20, 30] # 提前準備好對應的值列表# 用zip配對鍵和值
my_dict = {k: [v] for k, v in zip(keys, values)}print(my_dict) # 輸出: {'a': [10], 'b': [20], 'c': [30]}
核心原理:
通過循環或推導式,為每個鍵單獨創建新對象(如[i]
、[key.upper()]
),而不是讓所有鍵共享同一個對象引用。這樣無論值是列表、字典等可變對象,修改時都不會相互影響。
2?pop()
?
pop() 函數的作用是默認刪除最后一個元素,并返回刪除的元素的值。
在 Python 中,pop()
?是列表(list
)的一個常用方法,其主要作用是:
- 默認刪除列表中的最后一個元素
- 返回被刪除的元素的值
基本語法:
python
運行
list.pop(index=-1)
index
?是可選參數,默認值為?-1
(即最后一個元素的索引)- 如果指定了有效的索引,就會刪除該索引位置的元素并返回它
示例:
python
運行
# 示例1:默認刪除最后一個元素
fruits = ['apple', 'banana', 'cherry']
last_fruit = fruits.pop()
print(last_fruit) # 輸出: 'cherry'
print(fruits) # 輸出: ['apple', 'banana']# 示例2:指定索引刪除元素
numbers = [10, 20, 30, 40]
removed = numbers.pop(1) # 刪除索引為1的元素(20)
print(removed) # 輸出: 20
print(numbers) # 輸出: [10, 30, 40]
注意:
- 如果列表為空,調用?
pop()
?會拋出?IndexError
?錯誤 - 如果指定的索引超出列表范圍,也會拋出?
IndexError
?錯誤
除了列表,字典(dict
)也有?pop()
?方法,但功能不同:字典的?pop(key)
?是根據指定的鍵刪除鍵值對,并返回對應的值。
3?正則表達式(re
模塊)
正則表達式(re
模塊)是 Python 中非常重要且實用的工具,尤其在文本處理場景中幾乎不可或缺。如果你經常需要處理字符串(比如提取信息、驗證格式、替換內容等),系統學習re
模塊會讓你效率翻倍。
為什么re
模塊重要?
文本處理的 “瑞士軍刀”
無論是爬蟲提取數據、日志分析、表單驗證(如郵箱 / 手機號格式),還是字符串清洗,正則表達式都能通過簡潔的模式完成復雜操作。
例如:用一行正則快速提取文本中所有郵箱地址,比用字符串方法(split
/find
等)簡潔得多。跨語言通用能力
正則表達式的語法在 Python、Java、JavaScript 等主流語言中高度一致,學會后能遷移到其他場景。簡化復雜邏輯
很多字符串處理如果不用正則,可能需要寫十幾行循環判斷,而正則只需一個模式字符串。
如何入門re
模塊?
不需要一開始就掌握所有細節,先學核心功能即可:
基礎匹配規則
.*?
?非貪婪匹配(如題目中的用法)\d
?匹配數字,\w
?匹配字母 / 數字 / 下劃線^
?開頭,$
?結尾(用于嚴格驗證格式)()
?捕獲組(用于提取特定部分,如題目中的group(2)
)
常用方法
re.search()
:查找第一個匹配(如題目中的用法)re.findall()
:查找所有匹配并返回列表re.sub()
:替換匹配的內容re.match()
:從字符串開頭匹配(與search
的區別)
實戰練習
從簡單例子入手,比如:- 提取字符串中的所有數字
- 驗證手機號格式(
^1[3-9]\d{9}$
) - 替換文本中的敏感詞
舉個簡單例子理解 “捕獲組”
題目中的(.*) are (.*?) .*
其實是用()
定義了兩個 “捕獲組”,就像給需要提取的部分 “打標簽”:
- 第一個
(.*)
?標簽 1:匹配 “are” 前面的內容(Cats) - 第二個
(.*?)
?標簽 2:匹配 “are” 后面的內容(smarter)
通過group(1)
、group(2)
就能直接拿到這些 “標簽” 對應的內容,這就是正則提取信息的核心邏輯。
如果后續經常用到文本處理,建議花 1-2 天系統學一下基礎語法,之后遇到具體問題再查文檔補充。入門后你會發現,很多之前覺得復雜的字符串操作會變得非常簡單~
4 Python的多態
【Python的多態可通過鴨子類型(Duck Typing) 實現,即不依賴繼承,這一點是不是和強類型語言c,java是反的?】
你的理解有一定道理,但更準確地說,這是動態類型語言(如 Python)與靜態強類型語言(如 C、Java)在多態實現方式上的差異,而非完全 “相反”。
核心區別在于:
Python(動態類型)的鴨子類型
不要求顯式的繼承關系或接口實現,只關注對象是否具有所需的方法 / 屬性。
例如,只要一個對象有fly()
方法,就可以被當作 “會飛的東西” 來使用,無論它是否繼承自Bird
類:python
運行
class Duck:def fly(self):print("鴨子飛")class Plane:def fly(self):print("飛機飛")def make_fly(obj):obj.fly() # 只要有fly()方法就能調用make_fly(Duck()) # 可行 make_fly(Plane()) # 也可行
Java/C#(靜態強類型)的多態
依賴繼承關系或接口實現,編譯器會嚴格檢查類型兼容性。
例如,要讓make_fly
方法接收不同對象,必須讓它們實現同一個接口:java
運行
// 必須定義接口 interface Flyable {void fly(); }class Duck implements Flyable {public void fly() { ... } }class Plane implements Flyable {public void fly() { ... } }// 方法參數必須聲明為接口類型 void makeFly(Flyable obj) {obj.fly(); }
C 語言
本身沒有面向對象的多態特性,需要通過函數指針、結構體模擬,本質是手動實現類型適配,與上述兩種方式都不同。(想當然的以為也有,都不是opp了......倒是像文盲了哈哈哈)
本質差異:
- 動態類型語言:多態是 “隱式” 的,由運行時對象的能力決定(鴨子類型)。
- 靜態強類型語言:多態是 “顯式” 的,由編譯時的類型聲明 / 繼承關系保證。
這兩種方式沒有絕對優劣:
- 鴨子類型更靈活,代碼簡潔,但可能在運行時才暴露類型錯誤;
- 接口 / 繼承方式更嚴謹,編譯期即可發現問題,但需要寫更多聲明代碼。
5 什么叫可調用對象?關于對內存空間的理解
類似的開辟了幾個內存空間,我也沒有搞得很清楚....../(ㄒoㄒ)/
一、什么是可調用對象?
在 Python 中,可調用對象(Callable)?指的是可以像函數一樣被調用的對象,即可以在對象后面加括號?()
?并傳入參數來執行的對象。
常見的可調用對象類型:
函數(def 定義的普通函數)
python
運行
def add(a, b):return a + b add(1, 2) # 調用函數,返回 3 → add 是可調用對象
lambda 表達式(匿名函數)
python
運行
multiply = lambda x, y: x * y multiply(3, 4) # 調用 lambda,返回 12 → multiply 是可調用對象
類(實例化時會調用?
__init__
?方法)python
運行
class Person:def __init__(self, name):self.name = name p = Person("Alice") # 調用類創建實例 → Person 是可調用對象
類的實例(如果類定義了?
__call__
?方法)python
運行
class Counter:def __init__(self):self.count = 0def __call__(self): # 定義 __call__ 方法后,實例可被調用self.count += 1return self.count c = Counter() print(c()) # 調用實例,返回 1 → c 是可調用對象 print(c()) # 再調用,返回 2
其他(如內置函數?
print
、方法?list.append
?等)python
運行
print("Hello") # print 是可調用對象 [1,2].append(3) # append 方法是可調用對象
判斷是否為可調用對象:
用?callable()
?函數判斷:
python
運行
print(callable(add)) # True(函數可調用)
print(callable(Person)) # True(類可調用)
print(callable(123)) # False(數字不可調用)
二、關于 “內存空間” 的理解
變量、對象、函數等在內存中都會占用空間,不同類型的對象占用的空間和存儲方式不同,這里用簡單例子說明:
1. 變量與對象的內存關系
- 對象:在內存中實際存儲數據的實體(如數字、字符串、列表、函數等)。
- 變量:指向對象的 “標簽”,本身不存儲數據,只記錄對象在內存中的地址。
python
運行
a = 100 # 100 是一個整數對象,在內存中占據一塊空間;a 是變量,指向這塊空間
b = a # b 也指向 100 所在的內存空間(不新開辟空間)
2. 不同對象的內存占用示例
簡單類型(int、str 等):創建時開辟一塊內存。
python
運行
x = "hello" # 開辟一塊內存存 "hello",x 指向它 y = "world" # 再開辟一塊內存存 "world",y 指向它
容器類型(list、dict 等):容器本身和內部元素都占內存。
python
運行
lst = [1, 2, 3] # 1. 開辟一塊內存存列表本身(記錄元素的地址)# 2. 分別開辟內存存 1、2、3 三個整數對象
函數 / 類(可調用對象):定義時就會在內存中創建對應的對象。
python
運行
def func(): # 定義函數時,內存中創建一個函數對象,func 指向它pass
3. 關鍵結論
- 每個獨立的對象(如不同的字符串、列表、函數)都會開辟獨立的內存空間。
- 多個變量可以指向同一個對象(此時共享一塊內存,不重復開辟)。
- 可調用對象(如函數、類)也是對象的一種,定義時會在內存中分配空間,變量指向它們。
總結
- 可調用對象:能加?
()
?調用的對象(函數、類、帶?__call__
?的實例等)。 - 內存空間:每個獨立對象占一塊空間,變量只是指向對象的 “指針”,多個變量可共享同一對象的內存。
通過?id()
?函數可以查看對象的內存地址(唯一標識),幫助理解:
python
運行
a = 10
b = a
print(id(a), id(b)) # 輸出相同的地址(a 和 b 指向同一對象)
6 鏈式比較和邏輯運算or(以及其他Python 中容易混淆的運算符特性)
下列Python3代碼的運行結果是()
print('a' < 'b' < 'c')
A a
B b
C c
D True
E False
正確答案:D
你的答案:C#我之前做or不是返回操作數本身嗎??!!
你混淆了兩種不同的運算符行為:
比較運算符的鏈式判斷:
Python 支持類似?a < b < c
?的鏈式比較,等價于?a < b and b < c
,返回的是布爾值(True
?或?False
)。
題目中?'a' < 'b' < 'c'
?是字符的 ASCII 碼比較(a
?的 ASCII 碼是 97,b
?是 98,c
?是 99),顯然?97 < 98 < 99
?成立,所以結果為?True
。邏輯運算符?
or
?的特性:
你提到的 “返回操作數本身” 是?or
?的行為:or
?會返回第一個為 “真” 的值(如果所有值都為假,則返回最后一個值)。例如:python
運行
print(0 or 'a' or 'b') # 輸出 'a'(第一個真值) print('a' or 'b') # 輸出 'a'
這是兩種完全不同的語法:
- 鏈式比較(
a < b < c
)返回布爾值 - 邏輯運算?
or
?返回第一個真值
所以題目中是比較運算,結果為?True
(選項 D)。
為了避免混淆各類運算符的行為,這里整理了 Python 中容易混淆的運算符特性及對比小結:
一、比較運算符(==
、!=
、>
、<
、>=
、<=
)
基本作用:比較兩個值的關系,返回布爾值(
True
/False
)。python
運行
print(2 == 2) # True print(3 > 5) # False
特殊:鏈式比較
支持?a < b < c
?這樣的連續比較,等價于?a < b and b < c
,最終返回布爾值。python
運行
print(1 < 2 < 3) # 等價于 1<2 and 2<3 → True print('x' > 'y' > 'z') # 字符按ASCII碼比較 → False
二、邏輯運算符(and
、or
、not
)
not
:邏輯非,返回布爾值。python
運行
print(not True) # False print(not 0) # True(0為假值)
and
:邏輯與- 若第一個值為假,返回第一個值;
- 若第一個值為真,返回第二個值(不會返回布爾值,除非操作數本身是布爾值)。
python
運行
print(0 and 5) # 0(第一個值為假,返回0) print(3 and 5) # 5(第一個值為真,返回第二個值) print(True and False) # False(操作數是布爾值,返回布爾值)
or
:邏輯或- 若第一個值為真,返回第一個值;
- 若第一個值為假,返回第二個值(同樣可能返回非布爾值)。
python
運行
print(0 or 5) # 5(第一個值為假,返回第二個值) print(3 or 5) # 3(第一個值為真,返回第一個值) print(False or True) # True(操作數是布爾值,返回布爾值)
三、常見混淆點對比
表達式類型 | 運算邏輯 | 返回值類型 | 示例 | 結果 |
---|---|---|---|---|
單個比較 ( | 判斷關系 | 布爾值(True /False ) | 'a' < 'b' | True |
鏈式比較 ( | 等價于?a < b and b < c | 布爾值 | 1 < 3 < 2 | False |
and ?運算 | 取第一個假值,無假值則取最后一個值 | 操作數本身 (或布爾值) | 'a' and '' and 'b' | '' |
or ?運算 | 取第一個真值,無真值則取最后一個值 | 操作數本身 (或布爾值) | '' or 'a' or 'b' | 'a' |
四、記憶技巧
- 比較運算(包括鏈式):最終都是 “判斷對錯”,所以返回布爾值。
and
/or
:更像 “篩選值”,返回的是參與運算的原始值(除非原始值本身是布爾值)。and
?可以理解為 “找第一個不對勁的值”(假值),找不到就返回最后一個。or
?可以理解為 “找第一個對勁的值”(真值),找不到就返回最后一個。
7 list.extend()
?方法的返回值是?None?
以及 列表擴容與內存地址
執行以下程序,輸出結果為()l1 = [1,2]
l2 = [3,4]
print(id(l1) == id(l1.extend(l2)))A False
B True
C None
D 拋出異常正確答案:A
?id(l1.extend(l2)) 得到 None 對象的內存地址?(官方題解)我以為l1這種list列表內存地址是離散的,從底層理解的話extend以后會占更多的內存空間所以內存地址肯定不一樣(歪打正著,本意就是認為列表擴容一定會更改內存地址。
你的思路中有部分正確(結果確實是 False),但對底層邏輯的理解可以再精確一些。我們來詳細拆解:
核心原因:list.extend()
?方法的返回值是?None
l1.extend(l2)
?的作用:
它會在原列表?l1
?上修改,將?l2
?的元素添加到?l1
?末尾(不會創建新列表)。
執行后?l1
?變成?[1,2,3,4]
,但?l1
?本身的內存地址不變(因為是原地修改)。l1.extend(l2)
?的返回值:
所有列表的 “原地修改” 方法(如?extend
、append
、sort
?等)都返回?None
(這是 Python 的設計規范)。
所以?id(l1.extend(l2))
?等價于?id(None)
,即獲取?None
?對象的內存地址。比較的本質:
id(l1) == id(l1.extend(l2))
?等價于:id(原列表l1的地址) == id(None的地址)
顯然兩者完全不同,因此結果為?False
。
關于 “列表內存地址” 的補充
- 列表是動態數組,當元素增加導致內存不足時,Python 會重新分配更大的內存空間并復制元素(此時?
id(l1)
?會變化)。 - 但本題中?
l1
?從?[1,2]
?擴展為?[1,2,3,4]
,通常不會觸發內存重分配(小列表有預留空間),所以?l1
?的?id
?不變。 - 即使觸發了內存重分配,
l1
?的?id
?變化了,l1.extend(l2)
?返回的依然是?None
,id
?比較結果還是?False
(因為?None
?的地址和任何列表的地址都不同)。
關鍵結論
這道題的核心不是 “列表是否擴容”,而是 **extend
?方法返回?None
**,導致比較的是 “原列表地址” 和 “None
的地址”,必然不等,因此選?A
。
【列表擴容影響內存地址嗎?】
列表擴容可能會影響內存地址,具體取決于擴容時是否需要重新分配新的內存空間。
在 Python 中,列表(list
)是一種動態數組,其底層會預先分配一定的「預留內存空間」。當添加元素時:
- 如果當前預留空間足夠容納新元素(即未超過當前容量),則直接在原有內存空間中添加,列表的內存地址(
id
)不會改變。 - 如果當前預留空間不足(即超過當前容量),則需要重新分配一塊更大的內存空間,并將原列表中的元素復制到新空間中,此時列表的內存地址(
id
)會發生改變。
示例驗證:
可以通過?id()
?函數觀察列表擴容時的內存地址變化:
python
運行
l = [1, 2]
print("初始id:", id(l)) # 記錄初始內存地址# 逐步添加元素,觀察id是否變化
l.append(3)
print("添加第3個元素后id:", id(l)) # 通常不變(預留空間足夠)l.append(4)
print("添加第4個元素后id:", id(l)) # 可能不變l.extend([5, 6, 7, 8, 9]) # 一次性添加多個元素,可能觸發擴容
print("添加多個元素后id:", id(l)) # 此時可能變化(預留空間不足)
運行結果可能類似(具體地址因環境而異):
plaintext
初始id: 140525422476416
添加第3個元素后id: 140525422476416 # 地址不變
添加第4個元素后id: 140525422476416 # 地址不變
添加多個元素后id: 140525422509056 # 地址改變(觸發擴容)
總結:
- 列表擴容時,若無需重新分配內存,地址不變;若需要重新分配內存,地址改變。
- 這種機制是 Python 為了平衡性能設計的:預留空間減少了頻繁分配內存的開銷,而必要時的擴容則保證了列表可以動態增長。
因此,不能認為列表擴容一定會改變內存地址,具體取決于當前容量是否足夠。
【我怎么判斷夠不夠?】
在 Python 中,列表的「當前容量」(即底層已分配的內存空間能容納的元素總數)和「實際長度」(len()
?返回的值)是兩個不同的概念。可以通過一些技巧判斷當前容量是否足夠容納新元素:
方法:借助?sys.getsizeof()
?間接計算容量
sys.getsizeof(list)
?會返回列表對象占用的內存總字節數。由于列表存儲的是元素的引用(指針),可以通過總字節數減去列表自身固定開銷,再除以單個指針的字節數(通常與系統位數有關,64 位系統為 8 字節),得到當前容量。
步驟如下:
- 導入?
sys
?模塊 - 計算列表的固定開銷(空列表的內存大小)
- 用?
(列表總字節數 - 固定開銷) // 單個指針字節數
?得到當前容量
python
運行
import sysdef get_list_capacity(lst):# 空列表的內存大小(固定開銷)empty_size = sys.getsizeof([])# 列表總內存大小total_size = sys.getsizeof(lst)# 單個指針的字節數(64位系統通常為8)pointer_size = sys.getsizeof(id(lst)) # 間接獲取指針大小(id返回的是內存地址,其大小即指針大小)# 計算容量:(總大小 - 固定開銷) / 指針大小capacity = (total_size - empty_size) // pointer_sizereturn capacity# 測試示例
l = [1, 2]
print("長度:", len(l)) # 實際元素個數:2
print("容量:", get_list_capacity(l)) # 輸出當前容量(通常大于等于2,比如可能是4)l.append(3)
print("添加1個元素后,長度:", len(l)) # 3
print("添加1個元素后,容量:", get_list_capacity(l)) # 若容量仍為4,說明還能容納l.append(4)
print("添加第2個元素后,長度:", len(l)) # 4
print("添加第2個元素后,容量:", get_list_capacity(l)) # 仍為4,剛好裝滿l.append(5)
print("添加第3個元素后,長度:", len(l)) # 5
print("添加第3個元素后,容量:", get_list_capacity(l)) # 容量可能變為8(觸發擴容)
結論:如何判斷容量是否足夠?
- 當?
len(列表) < 容量
?時:容量足夠,添加元素不會改變列表的內存地址(id
?不變)。 - 當?
len(列表) == 容量
?時:容量已滿,再添加元素會觸發擴容,此時列表的內存地址(id
?會改變)。
注意:
- 容量是 Python 內部管理的,不同版本 / 環境可能有不同的擴容策略(通常是翻倍或按比例增長),不需要手動干預。
- 實際開發中很少需要關心容量,因為 Python 會自動處理擴容,這個知識點主要用于理解列表的底層機制。
通過上面的方法,你可以直觀地看到列表在添加元素過程中「長度」和「容量」的變化,從而判斷是否會觸發擴容。
【那我怎么做到比較extend以后的id地址?】
要比較?extend()
?操作前后列表的內存地址(id
),可以在調用?extend()
?方法之前和之后分別獲取列表的?id
,再進行比較。
關鍵在于:!!!!!!!extend()
?是原地修改列表(不會返回新列表),所以需要單獨獲取?extend()
?執行前后的?id
。
示例代碼:
python
運行
l1 = [1, 2]
l2 = [3, 4]# 記錄 extend 執行前 l1 的 id
id_before = id(l1)# 執行 extend(原地修改 l1,無返回值)
l1.extend(l2)# 記錄 extend 執行后 l1 的 id
id_after = id(l1)# 比較兩個 id 是否相同
print("extend 前 id:", id_before)
print("extend 后 id:", id_after)
print("id 是否相同:", id_before == id_after)
可能的運行結果:
plaintext
extend 前 id: 140632345869056
extend 后 id: 140632345869056
id 是否相同: True
為什么會這樣?
extend()
?是原地修改列表(直接在?l1
?原有內存空間中添加元素),如果原列表的容量足夠(len(l1) + len(l2) ≤ 容量
),則不需要重新分配內存,id
?保持不變。- 即使容量不足需要擴容(
id
?改變),也能通過這種方式清晰對比?extend()
?前后的?id
。
對比:如果用?+
?拼接列表(會創建新列表)
python
運行
l1 = [1, 2]
l2 = [3, 4]id_before = id(l1)
l1 = l1 + l2 # 用 + 拼接會創建新列表,l1 指向新地址
id_after = id(l1)print("+ 拼接前 id:", id_before)
print("+ 拼接后 id:", id_after)
print("id 是否相同:", id_before == id_after) # 結果為 False
總結:
- 要比較?
extend()
?前后的?id
,只需在調用?extend()
?方法前和后分別獲取列表的?id
?即可。 extend()
?通常不會改變列表的?id
(除非擴容觸發內存重分配),而?+
?拼接一定會改變?id
(因為創建了新列表)。