扩展SWIG内置类

问题描述 投票:6回答:2

SWIG的-builtin选项的优点是速度更快,并且可以免除具有多重继承的错误。挫折是我无法在生成的类或任何子类上设置任何属性:-通过子类化,我可以轻松扩展python内置类型(如list):

class Thing(list):
    pass

Thing.myattr = 'anything' # No problem

-但是在SWIG内置类型上使用相同的方法,会发生以下情况:

class Thing(SWIGBuiltinClass):
    pass

Thing.myattr = 'anything'

AttributeError: type object 'Thing' has no attribute 'myattr'

我该如何解决此问题?

c++ python swig
2个回答
4
投票

我很偶然地找到了解决方案。我正在尝试元类,以为我可以设法重写子类中内置类型的setattrgetattr函数。

这样做,我发现内置文件已经具有一个元类(SwigPyObjectType),因此我的元类必须继承它。

就是这样。仅此一项就解决了问题。如果有人可以解释原因,我会很高兴:

SwigPyObjectType = type(SWIGBuiltinClass)

class Meta(SwigPyObjectType):
    pass

class Thing(SWIGBuiltinClass):
    __metaclass__ = Meta

Thing.myattr = 'anything' # Works fine this time

0
投票

问题来自swig如何将“ -builtin”中的类实现为类似于内置类(因此得名)。

内置类不可扩展-尝试添加或修改“ str”的成员,而python将不允许您修改属性字典。

我有一个已经使用了几年的解决方案。

我不确定是否可以推荐,因为:

  1. 可以说是邪恶的-在C / C ++中消除常数性的道德等同物
  2. 不受支持,可能会在将来的python版本中中断
  3. 我还没有用python3尝试过

但是,我喜欢它解决我们想要调试的一些晦涩功能的效果。

最初的想法不是我的,我来自:https://gist.github.com/mahmoudimus/295200by Mahmoud Abdelkader

基本思想是在swig创建的类型对象中作为非const字典访问const字典,并添加/覆盖任何所需的方法。

FYI,类的运行时修改技术称为Monkeypatching,请参见https://en.wikipedia.org/wiki/Monkey_patch

首先-这是“ monkeypatch.py​​”:

''' monkeypatch.py:
I got this from https://gist.github.com/mahmoudimus/295200 by Mahmoud Abdelkader,
his comment: "found this from Armin R. on Twitter, what a beautiful gem ;)"
I made a few changes for coding style preferences
- Rudy Albachten   April 30 2015
'''

import ctypes
from types import DictProxyType, MethodType

# figure out the size of _Py_ssize_t
_Py_ssize_t = ctypes.c_int64 if hasattr(ctypes.pythonapi, 'Py_InitModule4_64') else ctypes.c_int

# python without tracing
class _PyObject(ctypes.Structure):
    pass
_PyObject._fields_ = [
    ('ob_refcnt', _Py_ssize_t),
    ('ob_type', ctypes.POINTER(_PyObject))
]

# fixup for python with tracing
if object.__basicsize__ != ctypes.sizeof(_PyObject):
    class _PyObject(ctypes.Structure):
        pass
    _PyObject._fields_ = [
        ('_ob_next', ctypes.POINTER(_PyObject)),
        ('_ob_prev', ctypes.POINTER(_PyObject)),
        ('ob_refcnt', _Py_ssize_t),
        ('ob_type', ctypes.POINTER(_PyObject))
    ]

class _DictProxy(_PyObject):
    _fields_ = [('dict', ctypes.POINTER(_PyObject))]

def reveal_dict(proxy):
    if not isinstance(proxy, DictProxyType):
        raise TypeError('dictproxy expected')
    dp = _DictProxy.from_address(id(proxy))
    ns = {}
    ctypes.pythonapi.PyDict_SetItem(ctypes.py_object(ns), ctypes.py_object(None), dp.dict)
    return ns[None]

def get_class_dict(cls): 
    d = getattr(cls, '__dict__', None)
    if d is None:
        raise TypeError('given class does not have a dictionary')
    if isinstance(d, DictProxyType):
        return reveal_dict(d)
    return d

def test():
    import random
    d = get_class_dict(str)
    d['foo'] = lambda x: ''.join(random.choice((c.upper, c.lower))() for c in x)
    print "and this is monkey patching str".foo()

if __name__ == '__main__':
    test()

这是一个使用monkeypatch的虚构示例:

我在模块“ mystuff”中用swig -python -builtin包装了一个类“ myclass”

我想添加一个额外的运行时方法“ namelen”,该方法返回由myclass.getName()返回的名称的长度。

import mystuff
import monkeypatch

# add a "namelen" method to all "myclass" objects
def namelen(self):
    return len(self.getName())
d = monkeypatch.get_class_dict(mystuff.myclass)
d['namelen'] = namelen

x = mystuff.myclass("xxxxxxxx")
print "namelen:", x.namelen()

[请注意,这也可以用于扩展或覆盖内置python类上的方法,如monkeypatch.py​​中的测试所示:它向内置str类添加方法“ foo”,该方法返回原始字符串的副本带有随机的大写/小写字母

我可能会替换:

# add a "namelen" method to all "myclass" objects
def namelen(self):
    return len(self.getName())
d = monkeypatch.get_class_dict(mystuff.myclass)
d['namelen'] = namelen

with

# add a "namelen" method to all "myclass" objects
monkeypatch.get_class_dict(mystuff.myclass)['namelen'] = lambda self: return len(self.getName())

为了避免额外的全局变量

© www.soinside.com 2019 - 2024. All rights reserved.