成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[轉(zhuǎn)載] 用ctypes觀察Python對(duì)象的內(nèi)存結(jié)構(gòu)

smallStone / 2400人閱讀

摘要:轉(zhuǎn)載地址在中一切皆是對(duì)象,而在實(shí)現(xiàn)的語言中,這些對(duì)象只不過是一些比較復(fù)雜的結(jié)構(gòu)體而已。由于和引用的是同一個(gè)整數(shù)對(duì)象,因此和的值同時(shí)發(fā)生了變化。用來創(chuàng)建大小不固定的結(jié)構(gòu)體對(duì)象,首先搜索名為的字段,并將其類型保存到中。

轉(zhuǎn)載地址:http://hyry.dip.jp/tech/slice/slice.html/10

在 Python 中一切皆是對(duì)象,而在實(shí)現(xiàn) Python 的 C 語言中,這些對(duì)象只不過是一些比較復(fù)雜的結(jié)構(gòu)體而已。本文通過 ctypes 訪問對(duì)象對(duì)應(yīng)的結(jié)構(gòu)體中的數(shù)據(jù),加深對(duì) Python 對(duì)象的理解。

對(duì)象的兩個(gè)基本屬性

Python 所有對(duì)象結(jié)構(gòu)體中的頭兩個(gè)字段都是相同的:

refcnt:對(duì)象的引用次數(shù),若引用次數(shù)為 0 則表示此對(duì)象可以被垃圾回收了。

typeid:指向描述對(duì)象類型的對(duì)象的指針。

通過 ctypes,我們可以很容易定義一個(gè)這樣的結(jié)構(gòu)體:PyObject。

注意:本文只描述在 32 位操作系統(tǒng)下的情況,如果讀者使用的是 64 位操作系統(tǒng),需要對(duì)程序中的一些字段類型做一些改變。

from ctypes import *

class PyObject(Structure):
    _fields_ = [("refcnt", c_size_t),
                ("typeid", c_void_p)]

下面讓我們用 PyObject 做一些實(shí)驗(yàn)幫助理解這兩個(gè)字段的含義:

>>> 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) 可以獲得對(duì)象 a 的內(nèi)存地址,而 PyObject.from_address()可以將指定的內(nèi)存地址的內(nèi)容轉(zhuǎn)換為一個(gè) PyObject 對(duì)象。通過此 PyObject 對(duì)象obj_a 可以訪問對(duì)象 a 的結(jié)構(gòu)體中的內(nèi)容。
?查看對(duì)象 a 的引用次數(shù),由于只有 a 這個(gè)名字引用它,因此值為 1。接下來創(chuàng)建一個(gè)列表,此列表中的每個(gè)元素都是對(duì)象 a,因此此列表應(yīng)用了它 10 次,?所以引用次數(shù)變?yōu)榱?11。
?查看對(duì)象 a 的類型對(duì)象的地址,它和 id(type(a)) 相同,而由于對(duì)象a的類型為str,因此也就是 id(str)。

下面查看str類型對(duì)象的這兩個(gè)字段:

>>> obj_str = PyObject.from_address(id(str))
>>> obj_str.refcnt
252L
>>> obj_str.typeid
505208152
>>> id(type)
505208152

可以看到 str 的類型就是type。再看看 type 對(duì)象:

>>> type_obj = PyObject.from_address(id(type))
>>> type_obj.typeid
505208152

type 對(duì)象的類型指針就指向它自己,因?yàn)?type(type) is type。

整數(shù)和浮點(diǎn)數(shù)對(duì)象

接下來看看整數(shù)和浮點(diǎn)數(shù)對(duì)象,這兩個(gè)對(duì)象除了有 PyObject 中的兩個(gè)字段之外,還有一個(gè) val 字段保存實(shí)際的值。因此 Python 中一個(gè)整數(shù)占用 12 個(gè)字節(jié),而一個(gè)浮點(diǎn)數(shù)占用 16 個(gè)字節(jié):

>>> sys.getsizeof(1)
12
>>> sys.getsizeof(1.0)
16

我們無需重新定義 refcnttypeid 這兩個(gè)字段,通過繼承 PyObject,可以很方便地定義整數(shù)和浮點(diǎn)數(shù)對(duì)應(yīng)的結(jié)構(gòu)體,它們會(huì)繼承父類中定義的字段:

class PyInt(PyObject):
    _fields_ = [("val", c_long)]

class PyFloat(PyObject):
    _fields_ = [("val", c_double)]

下面是使用 PyInt 查看整數(shù)對(duì)象的例子:

>>> i = 2000
>>> i_obj = PyInt.from_address(id(a))
>>> i_obj.refcnt
1L
>>> i_obj.val
2000

通過 PyInt 對(duì)象,還可以修改整數(shù)對(duì)象的內(nèi)容:
修改不可變對(duì)象的內(nèi)容會(huì)造成嚴(yán)重的程序錯(cuò)誤,請(qǐng)不要用于實(shí)際的程序中。

>>> j = i
>>> i_obj.val = 2012
>>> j
2012

由于i和j引用的是同一個(gè)整數(shù)對(duì)象,因此i和j的值同時(shí)發(fā)生了變化。

結(jié)構(gòu)體大小不固定的對(duì)象

表示字符串和長(zhǎng)整型數(shù)的結(jié)構(gòu)體的大小不是固定的,這些結(jié)構(gòu)體在 C 語言中使用了一種特殊的字段定義技巧,使得結(jié)構(gòu)體中最后一個(gè)字段的大小可以改變。由于結(jié)構(gòu)體需要知道最后一個(gè)字段的長(zhǎng)度,因此這種結(jié)構(gòu)中包含了一個(gè) size 字段,保存最后一個(gè)字段的長(zhǎng)度。在 ctypes 中無法表示這種長(zhǎng)度不固定的字段,因此我們使用了動(dòng)態(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))

?在定義長(zhǎng)度不固定的字段時(shí),使用長(zhǎng)度為 0 的數(shù)組定義一個(gè)不占內(nèi)存的偽字段 _val。 create_var_object() 用來創(chuàng)建大小不固定的結(jié)構(gòu)體對(duì)象,?首先搜索名為 _val 的字段,并將其類型保存到 inner_type 中。?然后創(chuàng)建一個(gè)PyVarObject 結(jié)構(gòu)體讀取obj對(duì)象中的 size 字段。?再通過 size 字段的大小創(chuàng)建一個(gè)對(duì)應(yīng)的 Inner 結(jié)構(gòu)體類,它可以從 struct 繼承,因?yàn)?struct 中的 _val 字段不占據(jù)內(nèi)存。
下面我們用上面的程序做一些實(shí)驗(yàn):

>>> s_obj = create_var_object(PyStr, s)
>>> s_obj.size
9L
>>> s_obj.val
"abcdegfgh"

當(dāng)整數(shù)的范圍超過了 0x7fffffff 時(shí),Python 將使用長(zhǎng)整型整數(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 個(gè) 16 位的整數(shù)表示 0x1234567890abcd,下面我們看看長(zhǎng)整型數(shù)是如何用數(shù)組表示的:

>>> hex((val[3] << 45) + (val[2] << 30) + (val[1] << 15) + val[0])
"0x1234567890abcdL"

即數(shù)組中的后面的元素表示高位,每個(gè) 16 為整數(shù)中有 15 位表示數(shù)值。

列表對(duì)象

列表對(duì)象的長(zhǎng)度是可變的,因此不能采用字符串那樣的結(jié)構(gòu)體,而是使用了一個(gè)指針字段items指向可變長(zhǎng)度的數(shù)組,而這個(gè)數(shù)組本身是一個(gè)指向 PyObject 的指針。 allocated 字段表示這個(gè)指針數(shù)組的長(zhǎng)度,而 size 字段表示指針數(shù)組中已經(jīng)使用的元素個(gè)數(shù),即列表的長(zhǎng)度。列表結(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])

我們用下面的程序查看往列表中添加元素時(shí),列表結(jié)構(gòu)體中的各個(gè)字段的變化:

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)

運(yùn)行 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 

?一開始列表的長(zhǎng)度和其指針數(shù)組的長(zhǎng)度都是 3,即列表處于飽和狀態(tài)。因此?往列表中添加新元素時(shí),需要重新分配指針數(shù)組,因此指針數(shù)組的長(zhǎng)度變?yōu)榱?7,而地址也發(fā)生了變化。這時(shí)列表的長(zhǎng)度為 4,因此指針數(shù)組中還有 3 個(gè)空位保存新的元素。由于每次重新分配指針數(shù)組時(shí),都會(huì)預(yù)分配一些額外空間,因此往列表中添加元素的平均時(shí)間復(fù)雜度為 O(1)。

下面再看看從列表刪除元素時(shí),各個(gè)字段的變化:

def test_list2():
    alist = [1] * 10000
    alist_obj = PyList.from_address(id(alist))

    alist_obj.print_field()
    del alist[10:]
    alist_obj.print_field()

運(yùn)行test_list2()得到下面的結(jié)果:

>>> test_list2()
10000 10000 
10 17 

可以看出大指針數(shù)組的位置沒有發(fā)生變化,但是后面額外的空間被回收了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37606.html

相關(guān)文章

  • 不要迷戀我,我只是利Python修改了游戲內(nèi)存

    摘要:上篇文章我許了一個(gè)愿,就是想讓大家多多關(guān)注我,然后我的粉絲就蹭蹭的漲了好幾百,謝謝大家的厚愛。可是我發(fā)現(xiàn)粉絲是漲了,三連變少了,謝謝大家這次給我三連,我一定再接再厲。地址的尋找陽光總值,種植一個(gè)豌豆需要,非常不夠用。 目錄 前言 游戲的安裝 思路 ? ? ? 一句話總結(jié) ? ? ? 大概的思...

    ermaoL 評(píng)論0 收藏0
  • Python 調(diào) C 動(dòng)態(tài)鏈接庫,包括結(jié)構(gòu)體參數(shù)、回調(diào)函數(shù)等

    摘要:調(diào)用以回調(diào)函數(shù)地址為參數(shù)的函數(shù)這個(gè)主題就稍微繞一些了,也就是說在接口中,需要傳入回調(diào)函數(shù)作為參數(shù)。這個(gè)問題在中也可以解決,并且回調(diào)函數(shù)可以用定義。代碼代碼很簡(jiǎn)單回調(diào)函數(shù)的傳入?yún)?shù)為,返回參數(shù)也是。 項(xiàng)目中要對(duì)一個(gè)用 C 編寫的 .so 庫進(jìn)行邏輯自測(cè)。這項(xiàng)工作,考慮到靈活性,我首先考慮用 Python 來完成。 研究了一些資料,采用 python 的 ctypes 來完成這項(xiàng)工作。已經(jīng)...

    NickZhou 評(píng)論0 收藏0
  • Python 外部函數(shù)調(diào)ctypes簡(jiǎn)介

    摘要:最近了解了提供的一個(gè)外部函數(shù)庫它提供了語言兼容的幾種數(shù)據(jù)類型,并且可以允許調(diào)用編譯好的庫。這里是閱讀相關(guān)資料的一個(gè)記錄,內(nèi)容大部分來自官方文檔。注意,提供的接口會(huì)在不同系統(tǒng)上有出入,比如為了加載動(dòng)態(tài)鏈接庫,在上提供的是而在上提供的是和。 參考資料 https://docs.python.org/2.7/l... http://www.ibm.com/developerw... c...

    mykurisu 評(píng)論0 收藏0
  • SWIG 對(duì) C++ 庫進(jìn)行 Python 包裝

    摘要:可以在接口文件中直接引用庫里的內(nèi)容,大大方便接口文件的編寫。使用庫里的這里先介紹方式通過創(chuàng)建出來的數(shù)組是數(shù)組的直接代理,非常底層和高效,但是,它也和數(shù)組一樣不安全,一樣沒有邊界檢查。對(duì)由于這種情況,可以使用庫里的。 如果你也像我們一樣,同時(shí)使用Python和C++,以獲得兩種語言的優(yōu)勢(shì),一定也會(huì)希望尋找一種好的方式集成這兩種語言,相比而言,讓Python能夠方便使用C++的庫更加重要,...

    jas0n 評(píng)論0 收藏0
  • Python進(jìn)程專題6:共享數(shù)據(jù)與同步

    摘要:可以使用標(biāo)準(zhǔn)的索引切片迭代操作訪問它,其中每項(xiàng)操作均鎖進(jìn)程同步,對(duì)于字節(jié)字符串,還具有屬性,可以把整個(gè)數(shù)組當(dāng)做一個(gè)字符串進(jìn)行訪問。當(dāng)所編寫的程序必須一次性操作大量的數(shù)組項(xiàng)時(shí),如果同時(shí)使用這種數(shù)據(jù)類型和用于同步的單獨(dú)大的鎖,性能將極大提升。 上一篇文章:Python進(jìn)程專題5:進(jìn)程間通信下一篇文章:Python進(jìn)程專題7:托管對(duì)象 我們現(xiàn)在知道,進(jìn)程之間彼此是孤立的,唯一通信的方式是隊(duì)...

    Yuanf 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<