摘要:轉(zhuǎn)載地址在中一切皆是對象,而在實現(xiàn)的語言中,這些對象只不過是一些比較復(fù)雜的結(jié)構(gòu)體而已。由于和引用的是同一個整數(shù)對象,因此和的值同時發(fā)生了變化。用來創(chuàng)建大小不固定的結(jié)構(gòu)體對象,首先搜索名為的字段,并將其類型保存到中。
轉(zhuǎn)載地址:http://hyry.dip.jp/tech/slice/slice.html/10
在 Python 中一切皆是對象,而在實現(xiàn) Python 的 C 語言中,這些對象只不過是一些比較復(fù)雜的結(jié)構(gòu)體而已。本文通過 ctypes 訪問對象對應(yīng)的結(jié)構(gòu)體中的數(shù)據(jù),加深對 Python 對象的理解。
對象的兩個基本屬性Python 所有對象結(jié)構(gòu)體中的頭兩個字段都是相同的:
refcnt:對象的引用次數(shù),若引用次數(shù)為 0 則表示此對象可以被垃圾回收了。
typeid:指向描述對象類型的對象的指針。
通過 ctypes,我們可以很容易定義一個這樣的結(jié)構(gòu)體:PyObject。
注意:本文只描述在 32 位操作系統(tǒng)下的情況,如果讀者使用的是 64 位操作系統(tǒng),需要對程序中的一些字段類型做一些改變。
from ctypes import * class PyObject(Structure): _fields_ = [("refcnt", c_size_t), ("typeid", c_void_p)]
下面讓我們用 PyObject 做一些實驗幫助理解這兩個字段的含義:
>>> a = "this is a string" >>> obj_a = PyObject.from_address(id(a)) ? >>> obj_a.refcnt ? 1L >>> b = [a]*10 >>> obj_a.refcnt ? 11L >>> obj_a.typeid ? 505269056 >>> id(type(a)) 505269056 >>> id(str) 505269056
?通過 id(a) 可以獲得對象 a 的內(nèi)存地址,而 PyObject.from_address()可以將指定的內(nèi)存地址的內(nèi)容轉(zhuǎn)換為一個 PyObject 對象。通過此 PyObject 對象obj_a 可以訪問對象 a 的結(jié)構(gòu)體中的內(nèi)容。
?查看對象 a 的引用次數(shù),由于只有 a 這個名字引用它,因此值為 1。接下來創(chuàng)建一個列表,此列表中的每個元素都是對象 a,因此此列表應(yīng)用了它 10 次,?所以引用次數(shù)變?yōu)榱?11。
?查看對象 a 的類型對象的地址,它和 id(type(a)) 相同,而由于對象a的類型為str,因此也就是 id(str)。
下面查看str類型對象的這兩個字段:
>>> obj_str = PyObject.from_address(id(str)) >>> obj_str.refcnt 252L >>> obj_str.typeid 505208152 >>> id(type) 505208152
可以看到 str 的類型就是type。再看看 type 對象:
>>> type_obj = PyObject.from_address(id(type)) >>> type_obj.typeid 505208152
type 對象的類型指針就指向它自己,因為 type(type) is type。
整數(shù)和浮點數(shù)對象接下來看看整數(shù)和浮點數(shù)對象,這兩個對象除了有 PyObject 中的兩個字段之外,還有一個 val 字段保存實際的值。因此 Python 中一個整數(shù)占用 12 個字節(jié),而一個浮點數(shù)占用 16 個字節(jié):
>>> sys.getsizeof(1) 12 >>> sys.getsizeof(1.0) 16
我們無需重新定義 refcnt 和 typeid 這兩個字段,通過繼承 PyObject,可以很方便地定義整數(shù)和浮點數(shù)對應(yīng)的結(jié)構(gòu)體,它們會繼承父類中定義的字段:
class PyInt(PyObject): _fields_ = [("val", c_long)] class PyFloat(PyObject): _fields_ = [("val", c_double)]
下面是使用 PyInt 查看整數(shù)對象的例子:
>>> i = 2000 >>> i_obj = PyInt.from_address(id(a)) >>> i_obj.refcnt 1L >>> i_obj.val 2000
通過 PyInt 對象,還可以修改整數(shù)對象的內(nèi)容:
修改不可變對象的內(nèi)容會造成嚴(yán)重的程序錯誤,請不要用于實際的程序中。
>>> j = i >>> i_obj.val = 2012 >>> j 2012
由于i和j引用的是同一個整數(shù)對象,因此i和j的值同時發(fā)生了變化。
結(jié)構(gòu)體大小不固定的對象表示字符串和長整型數(shù)的結(jié)構(gòu)體的大小不是固定的,這些結(jié)構(gòu)體在 C 語言中使用了一種特殊的字段定義技巧,使得結(jié)構(gòu)體中最后一個字段的大小可以改變。由于結(jié)構(gòu)體需要知道最后一個字段的長度,因此這種結(jié)構(gòu)中包含了一個 size 字段,保存最后一個字段的長度。在 ctypes 中無法表示這種長度不固定的字段,因此我們使用了動態(tài)創(chuàng)建結(jié)構(gòu)體類的方法。
class PyVarObject(PyObject): _fields_ = [("size", c_size_t)] class PyStr(PyVarObject): _fields_ = [("hash", c_long), ("state", c_int), ("_val", c_char*0)] ? class PyLong(PyVarObject): _fields_ = [("_val", c_uint16*0)] def create_var_object(struct, obj): inner_type = None for name, t in struct._fields_: if name == "_val": ? inner_type = t._type_ if inner_type is not None: tmp = PyVarObject.from_address(id(obj)) ? size = tmp.size class Inner(struct): ? _fields_ = [("val", inner_type*size)] Inner.__name__ = struct.__name__ struct = Inner return struct.from_address(id(obj))
?在定義長度不固定的字段時,使用長度為 0 的數(shù)組定義一個不占內(nèi)存的偽字段 _val。 create_var_object() 用來創(chuàng)建大小不固定的結(jié)構(gòu)體對象,?首先搜索名為 _val 的字段,并將其類型保存到 inner_type 中。?然后創(chuàng)建一個PyVarObject 結(jié)構(gòu)體讀取obj對象中的 size 字段。?再通過 size 字段的大小創(chuàng)建一個對應(yīng)的 Inner 結(jié)構(gòu)體類,它可以從 struct 繼承,因為 struct 中的 _val 字段不占據(jù)內(nèi)存。
下面我們用上面的程序做一些實驗:
>>> s_obj = create_var_object(PyStr, s) >>> s_obj.size 9L >>> s_obj.val "abcdegfgh"
當(dāng)整數(shù)的范圍超過了 0x7fffffff 時,Python 將使用長整型整數(shù):
>>> l = 0x1234567890abcd >>> l_obj = create_var_object(PyLong, l) >>> l_obj.size 4L >>> val = list(l_obj.val) >>> val [11213, 28961, 20825, 145]
可以看到 Python 用了 4 個 16 位的整數(shù)表示 0x1234567890abcd,下面我們看看長整型數(shù)是如何用數(shù)組表示的:
>>> hex((val[3] << 45) + (val[2] << 30) + (val[1] << 15) + val[0]) "0x1234567890abcdL"
即數(shù)組中的后面的元素表示高位,每個 16 為整數(shù)中有 15 位表示數(shù)值。
列表對象列表對象的長度是可變的,因此不能采用字符串那樣的結(jié)構(gòu)體,而是使用了一個指針字段items指向可變長度的數(shù)組,而這個數(shù)組本身是一個指向 PyObject 的指針。 allocated 字段表示這個指針數(shù)組的長度,而 size 字段表示指針數(shù)組中已經(jīng)使用的元素個數(shù),即列表的長度。列表結(jié)構(gòu)體本身的大小是固定的。
class PyList(PyVarObject): _fields_ = [("items", POINTER(POINTER(PyObject))), ("allocated", c_size_t)] def print_field(self): print self.size, self.allocated, byref(self.items[0])
我們用下面的程序查看往列表中添加元素時,列表結(jié)構(gòu)體中的各個字段的變化:
def test_list(): alist = [1,2.3,"abc"] alist_obj = PyList.from_address(id(alist)) for x in xrange(10): alist_obj.print_field() alist.append(x)
運行 test_list() 得到下面的結(jié)果:
>>> test_list() 3 3? 4 7 ? 5 7 6 7 7 7 8 12 9 12 10 12 11 12 12 12
?一開始列表的長度和其指針數(shù)組的長度都是 3,即列表處于飽和狀態(tài)。因此?往列表中添加新元素時,需要重新分配指針數(shù)組,因此指針數(shù)組的長度變?yōu)榱?7,而地址也發(fā)生了變化。這時列表的長度為 4,因此指針數(shù)組中還有 3 個空位保存新的元素。由于每次重新分配指針數(shù)組時,都會預(yù)分配一些額外空間,因此往列表中添加元素的平均時間復(fù)雜度為 O(1)。
下面再看看從列表刪除元素時,各個字段的變化:
def test_list2(): alist = [1] * 10000 alist_obj = PyList.from_address(id(alist)) alist_obj.print_field() del alist[10:] alist_obj.print_field()
運行test_list2()得到下面的結(jié)果:
>>> test_list2() 10000 1000010 17
可以看出大指針數(shù)組的位置沒有發(fā)生變化,但是后面額外的空間被回收了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/37606.html
摘要:上篇文章我許了一個愿,就是想讓大家多多關(guān)注我,然后我的粉絲就蹭蹭的漲了好幾百,謝謝大家的厚愛。可是我發(fā)現(xiàn)粉絲是漲了,三連變少了,謝謝大家這次給我三連,我一定再接再厲。地址的尋找陽光總值,種植一個豌豆需要,非常不夠用。 目錄 前言 游戲的安裝 思路 ? ? ? 一句話總結(jié) ? ? ? 大概的思...
摘要:調(diào)用以回調(diào)函數(shù)地址為參數(shù)的函數(shù)這個主題就稍微繞一些了,也就是說在接口中,需要傳入回調(diào)函數(shù)作為參數(shù)。這個問題在中也可以解決,并且回調(diào)函數(shù)可以用定義。代碼代碼很簡單回調(diào)函數(shù)的傳入?yún)?shù)為,返回參數(shù)也是。 項目中要對一個用 C 編寫的 .so 庫進行邏輯自測。這項工作,考慮到靈活性,我首先考慮用 Python 來完成。 研究了一些資料,采用 python 的 ctypes 來完成這項工作。已經(jīng)...
摘要:最近了解了提供的一個外部函數(shù)庫它提供了語言兼容的幾種數(shù)據(jù)類型,并且可以允許調(diào)用編譯好的庫。這里是閱讀相關(guān)資料的一個記錄,內(nèi)容大部分來自官方文檔。注意,提供的接口會在不同系統(tǒng)上有出入,比如為了加載動態(tài)鏈接庫,在上提供的是而在上提供的是和。 參考資料 https://docs.python.org/2.7/l... http://www.ibm.com/developerw... c...
摘要:可以在接口文件中直接引用庫里的內(nèi)容,大大方便接口文件的編寫。使用庫里的這里先介紹方式通過創(chuàng)建出來的數(shù)組是數(shù)組的直接代理,非常底層和高效,但是,它也和數(shù)組一樣不安全,一樣沒有邊界檢查。對由于這種情況,可以使用庫里的。 如果你也像我們一樣,同時使用Python和C++,以獲得兩種語言的優(yōu)勢,一定也會希望尋找一種好的方式集成這兩種語言,相比而言,讓Python能夠方便使用C++的庫更加重要,...
摘要:可以使用標(biāo)準(zhǔn)的索引切片迭代操作訪問它,其中每項操作均鎖進程同步,對于字節(jié)字符串,還具有屬性,可以把整個數(shù)組當(dāng)做一個字符串進行訪問。當(dāng)所編寫的程序必須一次性操作大量的數(shù)組項時,如果同時使用這種數(shù)據(jù)類型和用于同步的單獨大的鎖,性能將極大提升。 上一篇文章:Python進程專題5:進程間通信下一篇文章:Python進程專題7:托管對象 我們現(xiàn)在知道,進程之間彼此是孤立的,唯一通信的方式是隊...
閱讀 2461·2023-04-26 02:18
閱讀 1262·2021-10-14 09:43
閱讀 3822·2021-09-26 10:00
閱讀 6945·2021-09-22 15:28
閱讀 2535·2019-08-30 15:54
閱讀 2600·2019-08-30 15:52
閱讀 474·2019-08-29 11:30
閱讀 3465·2019-08-29 11:05