2013-05-01 68 views
0

我在「datetime」的例子中停止了它,在一個實際的lxml例子中被重寫。
(這可能很奇怪,因爲英文被翻譯成谷歌翻譯是我的聲明我很抱歉。)

據認爲,我喜歡lxml來自非常好的表現,但源代碼很難閱讀。
如果你正在積極使用XML,我還經常可以修改python的代碼。
忘記時間已經過去了,因爲很難理解,
我花時間去調試和修復了。
例如,我認爲通常當您搜索如下:深層XML層次結構。
爲什麼「__getattr__」在python「ctypes」中不起作用?

elem = lxml.etree.parse ("xxx/xxx/sample.xml").getroot() 

elem.xpath("//depth3/text()")[0] 

elem.find("./depth1/depth2/depth3").get("attr1").text 

我想使用如下。
(使用此代碼,這只是我。)

elem.depth3.text (Ex.1) 
OR 
elem.depth1.depth2.depth3.text (Ex.2) 

我試過類繼承首先要實現這一點。
您已經通過參考「在lxml中使用自定義元素類」進行了一些定製。
我使用__getattr__爲了搜索XML元素。

from lxml import etree 
class CustomElement (etree.ElementBase): 
    def __ getattr__ (self, k): 
     ret = self.xpath ("/ /" + k) 
     setattr(self, k, ret) 
     return getattr(self, k) 

(例1)的例子成功。
但是(例2)的例子變成屬性錯誤__getattr__在etree._Element depth1的返回實例中不存在。

儘管沒有(補充)實用,但我使用了一個例子,在易於理解的第一個問題中添加了「毫秒」的「datetime」。

有人認爲這是一種使用ctypes模塊將函數添加到lxml的Element類的方法。

import ctypes 
import lxml.etree 

class PyObject_HEAD(ctypes.Structure): 
    _fields_ = [ 
     ('HEAD', ctypes.c_ubyte * (object.__basicsize__ - 
          ctypes.sizeof(ctypes.c_void_p))), 
     ('ob_type', ctypes.c_void_p) 
    ] 
def __getattr__(self, k): 
    ret = self.xpath("//" + k) 
    setattr(self, k, ret) 
    return getattr(self, k) 

_get_dict   = ctypes.pythonapi._PyObject_GetDictPtr 
_get_dict.restype = ctypes.POINTER(ctypes.py_object) 
_get_dict.argtypes = [ctypes.py_object] 

EE = _get_dict(lxml.etree._Element).contents.value 
EE["__getattr__"] = __getattr__ 

elem = lxml.etree.parse("xxx/xxx/sample.xml").getroot() 
elem.xpath("//depth3")[0] 

=>返回_Element對象

from ispect import getsource 
print getsource(elem.__getattr__) 

=> DEF __getattr__(個體中,k):
=> RET = self.xpath( 「//」 + K)
=> setattr(self,k,ret)
=> return getattr(self,k)
sources added ..

elem.depth3 

=> AttributeError的..沒有屬性 'depth3'

我不知道,或者我應該寫如何使用 「PyObject_GetAttr」。
請告訴我是否。

致以問候


====================前問題=================== ================
我想增強ctypes。 添加功能通常會順利。 但是,如果您添加一個特殊的方法,爲什麼它不起作用?

import ctypes as c 

class PyObject_HEAD(c.Structure): 
    _fields_ = [ 
     ('HEAD', c.c_ubyte * (object.__basicsize__ - 
           c.sizeof(c.c_void_p))), 
     ('ob_type', c.c_void_p) 
    ] 

pgd = c.pythonapi._PyObject_GetDictPtr 
pgd.restype = c.POINTER(c.py_object) 
pgd.argtypes = [c.py_object] 

import datetime 

def millisecond(td): 
    return (td.microsecond/1000) 

d = pgd(datetime.datetime)[0] 
d["millisecond"] = millisecond 

now = datetime.datetime.now() 
print now.millisecond(), now.microsecond 

這打印155 155958,好的!

def __getattr__(self, k): 
    return self, k 

d["__getattr__"] = __getattr__ 

now = datetime.datetime 
print now.hoge 

這不行,爲什麼?

Traceback (most recent call last): 
    File "xxxtmp.py", line 31, in <module> 
    print now.hoge 
AttributeError: type object 'datetime.datetime' has no attribute 'hoge' 

回答

0

PyObject_GetAttr(Objects/object.c)使用該類型的tp_getattro slot或tp_getattr(如果前者未定義)。它不會在該類型的MRO中查找__getattribute__

對於自定義__getattr__,您需要繼承datetime的子類。您的堆類型將使用slot_tp_getattr_hook(Objects/typeobject.c)作爲其tp_getattro。該函數將通過調用_PyType_Lookup(Objects/typeobject.c)在該類型的MRO中尋找__getattribute____getattr__。請參考"using custom Element classes in lxml"。對於多個結果,我已經入侵了一個__getattr__掛鉤,該索引使用了後綴符號。否則,它默認爲索引0。誠然,我沒有多少想過,但如果您始終使用索引,則可以避免與現有名稱發生衝突。

from lxml import etree 

def make_parser(element): 
    lookup = etree.ElementDefaultClassLookup(element=element) 
    parser = etree.XMLParser() 
    parser.setElementClassLookup(lookup) 
    return parser 

class CustomElement(etree.ElementBase): 
    def __getattr__(self, attr): 
     try: 
      name, index = attr.rsplit('_', 1) 
      index = int(index) 
     except ValueError: 
      name = attr 
      index = 0 
     return self.xpath(name)[index] 

parser = make_parser(CustomElement) 

例如:

>>> spam = etree.fromstring(r''' 
... <spam> 
...  <foo> 
...   <bar>eggs00</bar> 
...   <bar>eggs01</bar> 
...  </foo> 
...  <foo> 
...   <bar>eggs10</bar> 
...   <bar>eggs11</bar> 
...  </foo> 
... </spam> 
... ''', parser) 

>>> spam.foo_0.bar_0.text 
'eggs00' 
>>> spam.foo_0.bar_1.text 
'eggs01' 
>>> spam.foo_1.bar_0.text 
'eggs10' 
>>> spam.foo_1.bar_1.text 
'eggs11' 
+0

謝謝您的回答。它會專門來源嗎?如果你修改我的例子?請告訴我初學者。對不起 – user2338888 2013-05-01 11:09:45

+0

感謝您的建議。
雖然我只是讀了一點「在lxml中使用自定義元素類」,但我可能只是不明白。
我會再試一次。這需要時間,因爲它不擅長英語。
user2338888 2013-05-01 15:30:48

+0

** _謝謝!!!!!!!!!!!非常酷的代碼!! _ **我沒有理解「在lxml中使用自定義元素類」的意思,非常感謝! – user2338888 2013-05-01 23:40:05

0

我不認爲你可以用這種方式覆蓋__getattr__。基本上,你正在黑客對象的__dict__包括一個新的方法。如果你打電話給now.millisecond,原來的「屬性getter」被調用,查看字典,並返回你的新方法。我不確定這個屬性getter的位置在哪裏(可能在C代碼中),但它不能在它查找的字典中 - 所以你不能用這種方式重寫它。

可能嘗試__getattribute__取而代之,但我不知道這是否也可以。請注意,正確實施要困難得多(請參閱https://stackoverflow.com/a/3278104/143091)。

這就是說,這樣做可能不是一個好主意。很多Python標準庫代碼可能取決於您更改的行爲,並且您的代碼可能會以難以理解的方式失敗。對於瞭解python並試圖理解你的代碼的人來說,這也是令人困惑的。

我希望你沒有從我這個討厭的把戲。我只有用它來反向移植不在舊版本的Python,或庫提供的功能,例如:

if not hasattr(wnck.Screen, "get_workspaces"): 
    def get_workspaces(screen): 
     return [screen.get_workspace(i) for i in range(screen.get_workspace_count())] 
     _get_dict(wnck.Screen)[0]['get_workspaces'] = get_workspaces 

這樣一來,我可以主要發展爲現代版本的庫,但仍支持古代版本,如果只是一個或兩個功能缺失,而不必更改我的代碼。

+0

感謝您的答案。我用日期時間作爲例子。我想真的想擴展lxml的c模塊。我想稍後上傳以作示例 – user2338888 2013-05-01 11:07:46

相關問題