扩展 SWIG 内置类
Posted
技术标签:
【中文标题】扩展 SWIG 内置类【英文标题】:Extending SWIG builtin classes 【发布时间】:2011-06-24 15:19:38 【问题描述】:SWIG 的 -builtin 选项的优点是速度更快,并且可以避免多重继承的错误。 挫折是我无法在生成的类或任何子类上设置任何属性: -我可以通过继承它来扩展一个像列表这样的python内置类型,没有麻烦:
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'
我该如何解决这个问题?
【问题讨论】:
我认为应该指出MONK对这个问题的解决方案是完全足够的,但它可能首先否定了使用-builtin
的所有好处。当然,就我而言,我有一个需要速度的类,我没有子类;但我还有另一门课是我的子类。所以这是一个非常有用的技术。
【参考方案1】:
我很偶然地找到了一个解决方案。我正在试验元类,认为我可以设法覆盖子类中内置类型的 setattr 和 getattr 函数。
这样做我发现内置函数已经有一个元类(SwigPyObjectType),所以我的元类必须继承它。
就是这样。仅此一项就解决了问题。如果有人能解释原因,我会很高兴:
SwigPyObjectType = type(SWIGBuiltinClass)
class Meta(SwigPyObjectType):
pass
class Thing(SWIGBuiltinClass):
__metaclass__ = Meta
Thing.myattr = 'anything' # Works fine this time
【讨论】:
【参考方案2】:问题来自于 swig 如何将“-builtin”中的类实现为就像内置类一样(因此得名)。
内置类不可扩展 - 尝试添加或修改 "str" 的成员,python 不会让您修改属性字典。
我确实有一个我已经使用了几年的解决方案。
我不确定我是否可以推荐它,因为:
-
可以说是邪恶的 - 道德上等同于在 C/C++ 中抛弃 const-ness
它不受支持,可能会在未来的 python 版本中中断
我没用python3试过
在生产代码中使用像这样的“黑魔法”我会有点不舒服 - 它可能会破坏而且肯定是晦涩难懂的 - 但至少有一家大公司正在生产代码中使用它
但是.. 我喜欢它解决我们想要调试的一些晦涩功能的效果。
最初的想法不是我的,我是从: https://gist.github.com/mahmoudimus/295200 by Mahmoud Abdelkader
基本思想是将 swig 创建的类型对象中的 const 字典作为非 const 字典访问并添加/覆盖任何所需的方法。
仅供参考,类的运行时修改技术称为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”中有一个类“myclass”,用 swig -python -builtin 包裹
我想添加一个额外的运行时方法“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
与
# add a "namelen" method to all "myclass" objects
monkeypatch.get_class_dict(mystuff.myclass)['namelen'] = lambda self: return len(self.getName())
避免额外的全局变量
【讨论】:
以上是关于扩展 SWIG 内置类的主要内容,如果未能解决你的问题,请参考以下文章