python的底层是c_python基本数据类型底层实现
一、前言
我們知道,python是一種動態(tài)語言,可以將任何類型的數(shù)據(jù)賦給任何變量,譬如:
#Python代碼
x = 4x= "four"
這里已經(jīng)將 x 變量的內(nèi)容由整型轉(zhuǎn)變成了字符串,而同樣的操作在 C 語言中將會導(dǎo)致(取決于編譯器設(shè)置)編譯錯誤或其他未知的后果。
這種靈活性是使 Python 和其他動態(tài)類型的語言更易用的原因之一。理解這一特性如何工作是學(xué)習(xí)用 Python 有效且高效地分析數(shù)據(jù)的重要因素。但是這種類型靈活性也指出了一個事實:
Python 變量不僅是它們的值,還包括了關(guān)于值的類型的一些額外信息。
二、整型
標(biāo)準(zhǔn)的 Python 實現(xiàn)是用 C 語言編寫的。這意味著每一個 Python 對象都是一個偽 C 語言結(jié)構(gòu)體,該結(jié)構(gòu)體不僅包含其值,還有其他信息。例如,當(dāng)我們在 Python 中定義一個整型,例如 x = 10000 時,x 并不是一個“原生”整型,而是一個指針,指向一個 C 語言的復(fù)合結(jié)構(gòu)體,結(jié)構(gòu)體里包含了一些值。查看 Python 3.4 的源代碼,可以發(fā)現(xiàn)整型(長整型)的定義,如下所示(C 語言的宏經(jīng)過擴展之后):
struct_longobject {longob_refcnt;
PyTypeObject*ob_type;
size_t ob_size;long ob_digit[1];
};
Python 3.4 中的一個整型實際上包括 4 個部分。
ob_refcnt 是一個引用計數(shù),它幫助 Python 默默地處理內(nèi)存的分配和回收。
ob_type 將變量的類型編碼。
ob_size 指定接下來的數(shù)據(jù)成員的大小。
ob_digit 包含我們希望 Python 變量表示的實際整型值。
這意味著與 C 語言這樣的編譯語言中的整型相比,在 Python 中存儲一個整型會有一些開銷,正如下圖所示:
這里 PyObject_HEAD 是結(jié)構(gòu)體中包含引用計數(shù)、類型編碼和其他之前提到的內(nèi)容的部分。
兩者的差異在于,C 語言整型本質(zhì)上是對應(yīng)某個內(nèi)存位置的標(biāo)簽,里面存儲的字節(jié)會編碼成整型。而 Python 的整型其實是一個指針,指向包含這個 Python 對象所有信息的某個內(nèi)存位置,其中包括可以轉(zhuǎn)換成整型的字節(jié)。由于 Python 的整型結(jié)構(gòu)體里面還包含了大量額外的信息,所以 Python 可以自由、動態(tài)地編碼。但是,Python 類型中的這些額外信息也會成為負擔(dān),在多個對象組合的結(jié)構(gòu)體中尤其明顯。
三、列表
設(shè)想如果使用一個包含很多 Python 對象的 Python 數(shù)據(jù)結(jié)構(gòu),會發(fā)生什么? Python 中的標(biāo)準(zhǔn)可變多元素容器是列表。可以用如下方式創(chuàng)建一個整型值列表:
In[1]: L = list(range(10))
L
Out[1]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In[2]: type(L[0])
Out[2]: int
或者創(chuàng)建一個字符串列表:
In[3]: L2 = [str(c) for c inL]
L2
Out[3]: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
In[4]: type(L2[0])
Out[4]: str
因為 Python 的動態(tài)類型特性,甚至可以創(chuàng)建一個異構(gòu)的列表:
In[5]: L3 = [True, "2", 3.0, 4]
[type(item)for item inL3]
Out[5]: [bool, str, float, int]
但是想擁有這種靈活性也是要付出一定代價的:為了獲得這些靈活的類型,列表中的每一項必須包含各自的類型信息、引用計數(shù)和其他信息;也就是說,每一項都是一個完整的 Python 對象。來看一個特殊的例子,如果列表中的所有變量都是同一類型的,那么很多信息都會顯得多余——將數(shù)據(jù)存儲在固定類型的數(shù)組中應(yīng)該會更高效。動態(tài)類型的列表和固定類型的(NumPy 式)數(shù)組間的區(qū)別如下圖所示。
在實現(xiàn)層面,數(shù)組基本上包含一個指向連續(xù)數(shù)據(jù)塊的指針。另一方面,Python 列表包含一個指向指針塊的指針,這其中的每一個指針對應(yīng)一個完整的 Python 對象(如前面看到的 Python 整型)。另外,列表的優(yōu)勢是靈活,因為每個列表元素是一個包含數(shù)據(jù)和類型信息的完整結(jié)構(gòu)體,而且列表可以用任意類型的數(shù)據(jù)填充。固定類型的 NumPy 式數(shù)組缺乏這種靈活性,但是能更有效地存儲和操作數(shù)據(jù)。
四、固定類型數(shù)組
1. 使用array模塊創(chuàng)建數(shù)組
Python 提供了幾種將數(shù)據(jù)存儲在有效的、固定類型的數(shù)據(jù)緩存中的選項。內(nèi)置的數(shù)組(array)模塊(在 Python 3.3 之后可用)可以用于創(chuàng)建統(tǒng)一類型的密集數(shù)組:
In[6]: importarray
L= list(range(10))
A= array.array('i', L)
A
Out[6]: array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
這里的 'i' 是一個數(shù)據(jù)類型碼,表示數(shù)據(jù)為整型。
更實用的是 NumPy 包中的 ndarray 對象。Python 的數(shù)組對象提供了數(shù)組型數(shù)據(jù)的有效存儲,而 NumPy 為該數(shù)據(jù)加上了高效的操作。稍后將介紹這些操作,這里先展示幾種創(chuàng)建 NumPy 數(shù)組的方法。
從用 np 別名導(dǎo)入 NumPy 的標(biāo)準(zhǔn)做法開始:
In[7]: import numpy as np
2. 從python列表創(chuàng)建NumPy 數(shù)組
首先,可以用 np.array 從 Python 列表創(chuàng)建數(shù)組:
In[8]: #整型數(shù)組:
np.array([1, 4, 2, 5, 3])
Out[8]: array([1, 4, 2, 5, 3])
請記住,不同于 Python 列表,NumPy 要求數(shù)組必須包含同一類型的數(shù)據(jù)。如果類型不匹配,NumPy 將會向上轉(zhuǎn)換(如果可行)。這里整型被轉(zhuǎn)換為浮點型:
In[9]: np.array([3.14, 4, 2, 3])
Out[9]: array([ 3.14, 4. , 2. , 3. ])
如果希望明確設(shè)置數(shù)組的數(shù)據(jù)類型,可以用 dtype 關(guān)鍵字:
In[10]: np.array([1, 2, 3, 4], dtype='float32')
Out[10]: array([ 1., 2., 3., 4.], dtype=float32)
最后,不同于 Python 列表,NumPy 數(shù)組可以被指定為多維的。以下是用列表的列表初始化多維數(shù)組的一種方法:
In[11]: #嵌套列表構(gòu)成的多維數(shù)組
np.array([range(i, i + 3) for i in [2, 4, 6]])
Out[11]: array([[2, 3, 4],
[4, 5, 6],
[6, 7, 8]])
內(nèi)層的列表被當(dāng)作二維數(shù)組的行。
3. 從頭創(chuàng)建NumPy 數(shù)組
面對大型數(shù)組的時候,用 NumPy 內(nèi)置的方法從頭創(chuàng)建數(shù)組是一種更高效的方法。以下是幾個示例:
In[12]: #創(chuàng)建一個長度為10的數(shù)組,數(shù)組的值都是0
np.zeros(10, dtype=int)
Out[12]: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
In[13]: #創(chuàng)建一個3×5的浮點型數(shù)組,數(shù)組的值都是1
np.ones((3, 5), dtype=float)
Out[13]: array([[ 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1.]])
In[14]: #創(chuàng)建一個3×5的浮點型數(shù)組,數(shù)組的值都是3.14
np.full((3, 5), 3.14)
Out[14]: array([[ 3.14, 3.14, 3.14, 3.14, 3.14],
[3.14, 3.14, 3.14, 3.14, 3.14],
[3.14, 3.14, 3.14, 3.14, 3.14]])
In[15]: #創(chuàng)建一個3×5的浮點型數(shù)組,數(shù)組的值是一個線性序列
#從0開始,到20結(jié)束,步長為2
#(它和內(nèi)置的range()函數(shù)類似)
np.arange(0, 20, 2)
Out[15]: array([ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18])
In[16]: #創(chuàng)建一個5個元素的數(shù)組,這5個數(shù)均勻地分配到0~1
np.linspace(0, 1, 5)
Out[16]: array([ 0. , 0.25, 0.5 , 0.75, 1. ])
In[17]: #創(chuàng)建一個3×3的、在0~1均勻分布的隨機數(shù)組成的數(shù)組
np.random.random((3, 3))
Out[17]: array([[ 0.99844933, 0.52183819, 0.22421193],
[0.08007488, 0.45429293, 0.20941444],
[0.14360941, 0.96910973, 0.946117]])
In[18]: #創(chuàng)建一個3×3的、均值為0、方差為1的
#正態(tài)分布的隨機數(shù)數(shù)組
np.random.normal(0, 1, (3, 3))
Out[18]: array([[ 1.51772646, 0.39614948, -0.10634696],
[0.25671348, 0.00732722, 0.37783601],
[0.68446945, 0.15926039, -0.70744073]])
In[19]: #創(chuàng)建一個3×3的、[0, 10)區(qū)間的隨機整型數(shù)組
np.random.randint(0, 10, (3, 3))
Out[19]: array([[2, 3, 4],
[5, 7, 8],
[0,5, 0]])
In[20]: #創(chuàng)建一個3×3的單位矩陣
np.eye(3)
Out[20]: array([[ 1., 0., 0.],
[ 0.,1., 0.],
[ 0., 0.,1.]])
In[21]: #創(chuàng)建一個由3個整型數(shù)組成的未初始化的數(shù)組
#數(shù)組的值是內(nèi)存空間中的任意值
np.empty(3)
Out[21]: array([ 1., 1., 1.])
五、NumPy標(biāo)準(zhǔn)數(shù)據(jù)類型
NumPy 數(shù)組包含同一類型的值,因此詳細了解這些數(shù)據(jù)類型及其限制是非常重要的。因為 NumPy 是在 C 語言的基礎(chǔ)上開發(fā)的,所以 C、Fortran 和其他類似語言的用戶會比較熟悉這些數(shù)據(jù)類型。
數(shù)據(jù)類型
描述
bool_
布爾值(真、True 或假、False),用一個字節(jié)存儲
int_
默認整型(類似于 C 語言中的 long,通常情況下是 int64 或 int32)
intc
同 C 語言的 int 相同(通常是 int32 或 int64)
intp
用作索引的整型(和 C 語言的 ssize_t 相同,通常情況下是 int32 或 int64)
int8
字節(jié)(byte,范圍從–128 到 127)
int16
整型(范圍從–32768 到 32767)
int32
整型(范圍從–2147483648 到 2147483647)
int64
整型(范圍從–9223372036854775808 到 9223372036854775807)
uint8
無符號整型(范圍從 0 到 255)
uint16
無符號整型(范圍從 0 到 65535)
uint32
無符號整型(范圍從 0 到 4294967295)
uint64
無符號整型(范圍從 0 到 18446744073709551615)
float_
float64 的簡化形式
float16
半精度浮點型:符號比特位,5 比特位指數(shù)(exponent),10 比特位尾數(shù)(mantissa)
float32
單精度浮點型:符號比特位,8 比特位指數(shù),23 比特位尾數(shù)
float64
雙精度浮點型:符號比特位,11 比特位指數(shù),52 比特位尾數(shù)
complex_
complex128 的簡化形式
complex64
復(fù)數(shù),由兩個 32 位浮點數(shù)表示
complex128
復(fù)數(shù),由兩個 64 位浮點數(shù)表示
上表列出了標(biāo)準(zhǔn) NumPy 數(shù)據(jù)類型。請注意,當(dāng)構(gòu)建一個數(shù)組時,你可以用一個字符串參數(shù)來指定數(shù)據(jù)類型:
np.zeros(10, dtype='int16')
或者用相關(guān)的 NumPy 對象來指定:
np.zeros(10, dtype=np.int16)
還可以進行更高級的數(shù)據(jù)類型指定,例如指定高位字節(jié)數(shù)或低位字節(jié)數(shù);更多的信息可以在 NumPy 文檔(http://numpy.org/)中查看。
六、參考
1. 《Python數(shù)據(jù)科學(xué)手冊》 [美] Jake VanderPlas [VanderPlas, Jake]
(完)
總結(jié)
以上是生活随笔為你收集整理的python的底层是c_python基本数据类型底层实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言全排列算法_一文学会回溯搜索算法解
- 下一篇: 一个div 上下两行_用 Python