Python 數(shù)據(jù)類型從基礎到幕后(實例代碼詳解)
Python 是一門動態(tài)類型、強類型的編程語言。這意味著變量無需聲明類型,但對象本身始終有明確的類型,不同類型之間的操作通常不會隱式轉(zhuǎn)換。真正理解 Python 的數(shù)據(jù)類型,不僅要知道有哪些類型可用,更要明白它們的內(nèi)部結(jié)構(gòu)、內(nèi)存行為以及由此衍生出的“可變性”、“哈希”等核心概念。本文將全面梳理 Python 的數(shù)據(jù)類型,穿插底層實現(xiàn)細節(jié)與代碼示例,帶你看到表象之下的真實。
1. 萬物皆對象,類型在對象上
在 Python 中,一切數(shù)據(jù)都是對象,每個對象都有三個基本屬性:
- 標識(identity):即內(nèi)存地址,可通過
id()獲取。 - 類型(type):決定對象能參與哪些操作,可通過
type()查看。 - 值(value):對象中存儲的數(shù)據(jù)。
變量名只是貼在對象上的“標簽”,賦值本質(zhì)上是將標簽貼到某個對象上。
a = 10 b = a print(id(a), id(b)) # 相同,指向同一個整數(shù)對象 print(type(a)) # <class 'int'>
2. 數(shù)字類型:不僅僅是“數(shù)字”
2.1 int — 任意精度的整數(shù)
Python 的 int 是任意精度的,只受內(nèi)存限制,不像 C/Java 有固定位寬溢出。底層使用變長數(shù)組存儲數(shù)字的絕對值(通常是 30 位或 15 位的“數(shù)字”片段),并搭配符號位。
big = 2**1000 print(big.bit_length()) # 1001 位二進制
小整數(shù)緩存:CPython 在啟動時會預先創(chuàng)建 -5 到 256 之間所有整數(shù)對象。這些整數(shù)在整個解釋器生命周期中復用。
a = 256
b = 256
print(a is b) # True (同一對象)
c = 257
d = 257
print(c is d) # 在腳本環(huán)境下可能是 True(因為同一代碼塊的常量合并),
# 但在交互環(huán)境或函數(shù)動態(tài)創(chuàng)建時通常是 False實用技巧:不要用 is 比較數(shù)值相等,始終使用 ==。is 僅用于判斷是否為同一對象(如 is None)。
2.2 float — IEEE 754 雙精度浮點數(shù)
Python float 對應 C 的 double,遵循 IEEE 754 標準。因此存在精度誤差:
print(0.1 + 0.2) # 0.30000000000000004
進行精確十進制運算應使用 decimal.Decimal,有理數(shù)運算可用 fractions.Fraction。
2.3 complex — 內(nèi)置復數(shù)
c = 3 + 4j print(c.real, c.imag) # 3.0 4.0 print(abs(c)) # 5.0
2.4 bool — 整數(shù)子類
bool 類型只有 True 和 False 兩個實例,且是 int 的子類:
print(issubclass(bool, int)) # True print(True == 1) # True print(True is 1) # False print(True + 3) # 4
這種設計繼承了歷史習慣,但也可能導致一些“陷阱”,比如在字典中 True 和 1 會被視為相同鍵:
d = {True: 'yes', 1: 'no'}
print(d) # {True: 'no'}
3. 序列類型:有序世界的不同選擇
Python 序列類型包括 list、tuple、str、bytes、bytearray、range 等。它們的核心共性是:支持索引、切片、長度、包含檢測,并可迭代。
3.1 列表 list — 動態(tài)數(shù)組
list 底層是一個指向元素對象的指針數(shù)組,并采用預分配(over-allocation)策略來讓 append 達到均攤 O(1)。
import sys
lst = []
prev = sys.getsizeof(lst)
for i in range(20):
lst.append(i)
size = sys.getsizeof(lst)
if size != prev:
print(f"len={len(lst)}, size={size}, extra slots={(size-prev)//8}")
prev = size
輸出的分配模式大致是 0, 4, 8, 16, 25...,增長速度約為原大小的 1/8,避免每次追加都重新分配內(nèi)存。
可變性影響:因為列表元素存的是對象引用,修改列表不會改變被引用的對象本身,但會改變列表內(nèi)部的指針數(shù)組。這也是深淺拷貝問題的根源。
3.2 元組 tuple — 不可變序列
tuple 一旦創(chuàng)建,結(jié)構(gòu)就固定了,無法添加、刪除或修改元素。這帶來兩個好處:
- 哈希穩(wěn)定,可作為字典的鍵(內(nèi)部元素也必須是可哈希的)。
- 內(nèi)存開銷比列表更小,沒有額外預分配空間。
但需注意:元組的“不可變”指的是它儲存的引用不可變,若引用指向可變對象,則該對象的狀態(tài)依然可以改變。
t = ([1, 2], 3) t[0].append(99) print(t) # ([1, 2, 99], 3)
字符串駐留(interning):與整數(shù)緩存類似,某些字符串也會被解釋器駐留,以復用對象。標識符類似的短字符串會自動駐留;可通過 sys.intern() 手動駐留。
a = "python" b = "python" print(a is b) # True (自動駐留) c = "python!" d = "python!" print(c is d) # 通常 False (包含特殊字符的短字符串不一定駐留)
3.3 字符串 str — 不可變 Unicode 序列
Python 3 的 str 是 Unicode 字符序列,內(nèi)部表示會根據(jù)字符串內(nèi)容動態(tài)選擇緊湊編碼(1/2/4 字節(jié)每字符),稱為“靈活字符串表示”。字符串不可變,因此所有操作都會返回新對象,可使用 join 而不是循環(huán) += 來提高性能。
# 拼接大量字符串的反例:
result = ""
for s in many_strings:
result += s # 每次創(chuàng)建新字符串,O(n2)
# 正例:
result = "".join(many_strings) # O(n)3.4 range — 惰性數(shù)值序列
range 不是列表,它只存儲 start、stop、step 三個值,按需生成整數(shù),內(nèi)存占用極小。
r = range(10**10) # 不會創(chuàng)建 100 億個整數(shù) print(r[100]) # 即時計算
3.5 二進制序列類型:bytes 與 bytearray
bytes:不可變字節(jié)串(0-255 整數(shù)序列),常用于處理二進制數(shù)據(jù)。bytearray:可變字節(jié)串,可原地修改。
b = b'hello' print(b[0]) # 104 (ASCII 碼) ba = bytearray(b) ba[0] = 106 print(ba) # bytearray(b'jello')
memoryview 則允許在不復制的情況下訪問不同類型二進制數(shù)據(jù)的內(nèi)存,進行高效操作。
4. 字典 dict — 哈希表的魔力
dict 是 Python 的核心,底層使用散列表(hash table)。從 Python 3.6 開始,字典保留插入順序,這得益于使用了緊湊存儲結(jié)構(gòu):一個稠密數(shù)組存儲鍵值對條目,一個稀疏的哈希表數(shù)組只存儲索引。這樣既節(jié)約內(nèi)存,又保證了順序。
4.1 鍵的要求:可哈希
字典的鍵必須是可哈希(hashable)的對象,即:
- 定義了
__hash__方法,且哈希值在其生命周期內(nèi)不變; - 定義了
__eq__方法,用于比較相等。
所有不可變內(nèi)置類型(int、float、str、tuple、frozenset 等)都是可哈希的,前提是它們內(nèi)部所有元素也可哈希。列表、集合、字典本身不可哈希,因此不能作為字典的鍵。
# 合法的鍵
d = { (1, 2): "point" }
# 非法
# d = { [1, 2]: "line" } # TypeError: unhashable type: 'list'自定義對象默認可哈希(基于 id()),但如果定義了 __eq__ 而未定義 __hash__,則會被設為不可哈希。
4.2 常見操作與性能
字典的取值、賦值、刪除平均時間復雜度都是 O(1)。但若哈希碰撞過多或裝載因子過高,會觸發(fā)重建(rehash),使某些操作變?yōu)?O(n)。
默認值技巧:setdefault、collections.defaultdict 和 dict.get 可簡化邏輯。
from collections import defaultdict
counter = defaultdict(int)
for ch in 'abracadabra':
counter[ch] += 1
print(counter) # defaultdict(<class 'int'>, {'a': 5, 'b': 2, ...})5. 集合類型:不重復元素的容器
set 是可變的無序集合(元素唯一),frozenset 是其不可變版本。它們同樣基于哈希表,元素必須可哈希。支持交集、并集、差集等高效集合運算。
a = {1, 2, 3}
b = frozenset([2, 3, 4])
print(a & b) # {2, 3}
a.add(4)
# b.add(4) # AttributeError: 'frozenset' object has no attribute 'add'
集合的典型應用是去重和成員資格快速判斷(O(1))。
6. None — 唯一的空值類型
None 是 NoneType 的單例對象,表示“沒有值”。它是用 is 檢查的絕佳例子:
if x is None:
# 正確的寫法
7. 可變與不可變:深入理解引用與拷貝
7.1 可變對象作為默認參數(shù)的陷阱
函數(shù)的默認參數(shù)在定義時只評估一次,若默認值是可變對象,多次調(diào)用會共享同一對象。
def add_item(item, target=[]):
target.append(item)
return target
print(add_item(1)) # [1]
print(add_item(2)) # [1, 2] 并不是空列表!正確方式:
def add_item(item, target=None):
if target is None:
target = []
target.append(item)
return target
7.2 淺拷貝與深拷貝
- 淺拷貝(
list.copy(),copy.copy等):構(gòu)造一個新容器,但元素是原容器中對象的引用。 - 深拷貝(
copy.deepcopy):遞歸復制所有嵌套對象,創(chuàng)建全新的獨立對象圖。
import copy orig = [ [1, 2], 3 ] shallow = copy.copy(orig) deep = copy.deepcopy(orig) orig[0].append(99) print(shallow) # [[1, 2, 99], 3] —— 受影響 print(deep) # [[1, 2], 3] —— 不受影響
理解深淺拷貝對避免數(shù)據(jù)被意外修改至關重要。
8. 類型提示:讓代碼更自文檔化
Python 3.5+ 引入了類型提示(Type Hints),雖然不強制類型檢查,但極大地提高了代碼可讀性,并可結(jié)合 mypy、pyright 等工具做靜態(tài)分析。
from typing import List, Dict, Optional
def process(data: List[int], config: Optional[Dict[str, str]] = None) -> str:
if config is None:
config = {}
return ", ".join(str(x) for x in data)
print(process([1, 2, 3])) # "1, 2, 3"更高級的用法包括泛型、聯(lián)合類型、TypedDict 等。類型提示本身也催生了許多強大模式。
9. 數(shù)據(jù)類與命名元組:讓數(shù)據(jù)容器更優(yōu)雅
當我們需要一個僅用于保存數(shù)據(jù)的類時,傳統(tǒng)寫法需要定義 __init__、__repr__ 等樣板代碼。Python 提供了更簡潔的解決方案。
9.1 命名元組
collections.namedtuple 創(chuàng)建輕量、不可變的數(shù)據(jù)容器,通過屬性訪問字段,且支持迭代。
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(10, 20)
print(p.x, p.y) # 10 20
print(p[0]) # 10
# p.x = 30 # AttributeError: can't set attribute9.2 數(shù)據(jù)類
Python 3.7+ 引入的 @dataclass 提供可變或不可變的數(shù)據(jù)容器,自動生成 __init__、__repr__、__eq__ 等方法,并支持默認值、類型注解等。
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int = 0
tags: list = None # 注意默認值問題,推薦使用 field(default_factory=list)
# 自動生成的 __init__ 和 __repr__
p1 = Person("Alice", 30)
p2 = Person("Alice", 30)
print(p1 == p2) # True (基于值的比較)若要不可變,只需設置 frozen=True:
@dataclass(frozen=True)
class Point:
x: int
y: int
數(shù)據(jù)類極大減少了樣板代碼,成為數(shù)據(jù)建模的首選。
10. 總結(jié):類型是理解 Python 的關鍵
Python 的數(shù)據(jù)類型系統(tǒng)看似簡單,實則蘊含著深思熟慮的設計。抓住以下主線,就能駕馭它:
- 對象模型:變量是標簽,對象有類型、標識和值。
- 可變與不可變:決定了對象的哈希能力和作為參數(shù)時的行為。
- 內(nèi)部結(jié)構(gòu):列表是動態(tài)數(shù)組,字典是緊湊哈希表,整數(shù)任意精度,float 遵循 IEEE 754。
- 高效慣用法:字符串用
join,字典用get/defaultdict,集合用于去重,數(shù)據(jù)類替代樣板類。 - 類型提示與數(shù)據(jù)類:提升可維護性,構(gòu)建自文檔化代碼。
Python 的類型既是基礎,也是進階的入口。當你從“會使用”走向“懂原理”,就能寫出更高效、更可靠的代碼,并從容解決那些令人頭疼的“坑”。這篇文章只是一個深潛的起點,希望它能激發(fā)你繼續(xù)探索底層實現(xiàn)、C API 甚至 Python 解釋器源碼的興趣。
到此這篇關于Python 數(shù)據(jù)類型從基礎到幕后的文章就介紹到這了,更多相關Python 數(shù)據(jù)類型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- python調(diào)用C++庫實現(xiàn)數(shù)據(jù)類型轉(zhuǎn)換的完整指南
- Python數(shù)據(jù)類型轉(zhuǎn)換(Type Casting)與避坑指南
- Python中操作MySQL數(shù)據(jù)庫時如何處理數(shù)據(jù)類型轉(zhuǎn)換問題
- 一文詳解Python數(shù)據(jù)類型:數(shù)字、布爾與None的語義
- Python 常用數(shù)據(jù)類型詳解之字符串、列表、字典操作方法
- Python 常用數(shù)據(jù)類型相同點、差異與使用指南
- Python標準庫datetime模塊日期和時間數(shù)據(jù)類型解讀
- Python變量與數(shù)據(jù)類型全解析(最新整理)
- 一文教你徹底掌握Python數(shù)據(jù)類型轉(zhuǎn)換
相關文章
五個Pandas?實戰(zhàn)案例帶你分析操作數(shù)據(jù)
pandas是基于NumPy的一種工具,該工具是為了解決數(shù)據(jù)分析任務而創(chuàng)建的。Pandas納入了大量庫和一些標準的數(shù)據(jù)模型,提供了高效操作大型數(shù)據(jù)集的工具。pandas提供大量快速便捷地處理數(shù)據(jù)的函數(shù)和方法。你很快就會發(fā)現(xiàn),它是使Python強大而高效的數(shù)據(jù)分析環(huán)境的重要因素之一2022-01-01
python爬蟲之urllib庫常用方法用法總結(jié)大全
urllib是python自帶的請求庫,各種功能相比較之下也是比較完備的,下面這篇文章主要給大家介紹了關于python爬蟲之urllib庫常用方法用法的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2018-11-11
Python定時從Mysql提取數(shù)據(jù)存入Redis的實現(xiàn)
這篇文章主要介紹了Python定時從Mysql提取數(shù)據(jù)存入Redis的實現(xiàn),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05
利用Python實現(xiàn)眨眼計數(shù)器的示例代碼
這篇文章主要介紹了如何使用Python語言實現(xiàn)對視頻中的人物的眨眼進行計數(shù)并描繪在圖表中,文中的實現(xiàn)步驟講解詳細,感興趣的可以動手試一試2022-02-02

