国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

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

smallStone / 2385人閱讀

摘要:轉(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

我們無需重新定義 refcnttypeid 這兩個字段,通過繼承 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)存的偽字段 _valcreate_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 10000 
10 17 

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

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/37606.html

相關(guān)文章

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

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

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

    摘要:調(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)...

    NickZhou 評論0 收藏0
  • Python 外部函數(shù)調(diào)ctypes簡介

    摘要:最近了解了提供的一個外部函數(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...

    mykurisu 評論0 收藏0
  • SWIG 對 C++ 庫進行 Python 包裝

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

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

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

    Yuanf 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<