【问题标题】:Why "__getattr__" does not work in python "ctypes"?为什么“__getattr__”在python“ctypes”中不起作用?
【发布时间】:2013-04-25 07:30:24
【问题描述】:

我把它停在“datetime”的例子中,改写成一个真实的lxml例子。
(这可能很奇怪,因为英语是在谷歌翻译中翻译的,我很抱歉。)

据说我喜欢lxml的性能很好,但是源码很难看。
如果你正在积极使用XML,我也可以经常修改python的代码。
时间过去了就忘记了,源码因为很难理解,
我已经花时间调试和修复。
例如,我认为通常当您搜索如下:深层 XML 层次结构。

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)

(Ex.1) 的例子成功了。
但是(Ex.2) 的例子变成了属性错误__getattr__ is not present in the return of etree._Element depth1.

虽然不是(补充)实用,但我使用了一个示例,在“易懂”的第一个问题中添加“日期时间”的“毫秒”。

当时认为这是一种使用 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__(self, k):
=> ret = self.xpath("//" + k)
=> setattr(self, k, ret)
=> return getattr(self, k)
来源已添加..

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'

【问题讨论】:

    标签: python ctypes getattr


    【解决方案1】:

    PyObject_GetAttr (Objects/object.c) 使用类型的 tp_getattro 槽,如果未定义前者,则使用 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'
    

    【讨论】:

    • 感谢您的回答。如果您修改我的示例,它将是任何特定来源吗?请告诉我初学者。对不起
    • 谢谢你的建议。
      虽然我只看了一点“在lxml中使用自定义元素类”,
      我可能只是不明白。
      我会再读一遍更多时间。因为英语不好,所以需要时间。
    • 谢谢!!!!!!!!!!!!非常酷的代码!!我不明白“使用自定义”的意思lxml 中的元素类”非常感谢!
    【解决方案2】:

    我认为您不能以这种方式覆盖__getattr__。基本上,您正在破解对象的__dict__ 以包含一个新方法。如果你调用now.millisecond,原始的“属性获取器”会被调用,查看字典,并返回你的新方法。我不确定这个属性 getter 驻留在哪里(可能在 C 代码中),但它不能在它查找内容的 dict 中 - 所以你不能以这种方式覆盖它。

    可能试试__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
    

    这样,我可以主要为现代版本的库进行开发,但如果只缺少一两个函数,我仍然支持旧版本,而无需更改我的代码。

    【讨论】:

    • 感谢您的回答。我以日期时间为例。我真的很想扩展lxml的c模块。我想上传以后做个例子
    猜你喜欢
    • 2012-09-19
    • 2021-07-01
    • 2023-03-24
    • 1970-01-01
    • 1970-01-01
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-06-17
    相关资源
    最近更新 更多