我正在使用Python包装的api,我正在使用swig生成接口,我需要找到一种方法来使用函数__getitem__
内置的Python来为类的不同组件。
一个简单的例子是:
obj = module.class()
for i in range(obj.numPart1()):
print (obj.part1[i])
for i in range(obj.numPart2()):
print (obj.part2[i])
如果这是完全错误的并且还有更多的Pythonic'做到这一点的方式,我都是耳朵。基础对象必须有两个部分,因为它是一个三角剖分,可以访问所有与同一个C ++后端相关联的顶点和构面。
我已经在很多地方环顾四周,我尝试在Python中使用metaclasses
和super
函数并且没有运气,主要是因为我的课程是如何布局的,因为痛饮。我也可能不完全理解它们......
如果需要,我甚至可以向C ++端添加项目,如果我需要能够提供所需的东西,但我想最终得到一个可以访问对象具有的两个属性之一的类基本上是列表。添加到C ++方面不太理想,因为代码在其他地方被重用,所以我希望尽可能减少与C ++代码的混乱。
使用SWIG会引起一些我应该提到的问题
__init__
无法修改,因为它是在SWIG中生成的。我只能添加到班级以下是我希望通过具有代表性的代码示例访问的内容:
注意:此示例中的所有函数都是包装的C ++函数,但包装的内置函数除外(__getitem__
,__enter__
,__exit__
)
import ModTest as m
with m.multiAttrObj() as obj:
print ("Attribute A:")
for i in range(obj.nAttrA()):
#print (obj.getAttrA(i)) # This version works
print (obj.AttrA[i]) # This version fails
print ("\nAttribute B:")
for i in range(obj.nAttrB()):
#print (obj.getAttrB(i)) # This version works
print (obj.AttrB[i]) # This version fails
我的模块看起来大致如下:
#ModTest.py
class multiAttrObj():
# Init method cannot be changed...generated by SWIG (in real case)
def __init__(self):
# Real module attributes are actually an instance of a C++ object
self._attrA = "a b c d e f".split()
self._attrB = "1 2 3 4 5".split()
def __enter__(self):
self.__init__
return self
def __exit__(self, a,b,c):
self._attrA = None
self._attrB = None
def nAttrA(self):
return len(self._attrA)
def nAttrB(self):
return len(self._attrB)
# Only way to get to AttrA from Python, calls C++ accessor
def getAttrA(self,i):
# real return is as below:
# return _multiAttrObj.get_attr_a(i)
return self._attrA[i] # Example for testing without .pyd
# Only way to get to AttrB from Python, calls C++ accessor
def getAttrB(self,i):
# real return is as below:
# return _multiAttrObj.get_attr_b(i)
return self._attrB[i] # Example for testing without .pyd
# Function can be created, but throws
# TypeError: 'method' object is not subscriptable
# when called like __getitem__
#def AttrA(self,i):
# return self._attrA[i]
#def AttrB(self,i):
# return self._attrB[i]
def __getitem__(self,i):
# This can't distiguish which attribute I want.
pass
编辑: 的 ANSWER
修改了martineau的解决方案,我使用了提供的Sequence代理,并且为了SWIG而坚持使用属性,因为我无法重新映射__getattr__
或__setattr__
。回答下面的代码:
def _getAttrAWrap(self):
return SequenceProxy(self.getAttrA, self.nAttrA)
def _getAttrBWrap(self):
return SequenceProxy(self.getAttrB, self.nAttrB)
# Missing setter functions are for future use.
AttrA = property(_getAttrAWrap, "")
AttrB = property(_getAttrBWrap, "")
答案 0 :(得分:0)
编辑#2.1 (稍微调整编辑#2后)
解决此问题的关键是要意识到需要的是一种覆盖 属性 的索引(通过__getitem__()
方法)的方法类,而不是最外层容器类本身。
要做到这一点,这里更加精简,因为它不再使用属性,因此可能是我原来答案第一次修订的更快版本(又名编辑#1 )。而不是它们实现__getattr__()
方法,当引用属性AttrA
或AttrB
但尚未存在时,该方法通常只被调用一次。从那时起,它就不会被再次召唤。这解决了无法更改SWIG生成的类__init__()
方法的问题(但不限制该类用作上下文管理器(尽管但仍支持用作一个)。
这两个属性在创建时将是从SequenceProxy
抽象基类派生的名为collections.Sequence
的新具体子类的实例。此类与常规list
类似,不同之处在于其自定义__getitem__()
方法调用在创建时为其构造函数提供的函数。类似地,将调用一个不同的提供函数来确定并在请求时返回其长度。
使用SequenceProxy
实例可以将任何索引和长度查询调用转发给您选择的任何函数或绑定方法 - 从而提供"钩子"或者"瓶颈"需要实现它。
import collections
class SequenceProxy(collections.Sequence):
"""Proxy class to make something appear to be an (immutable) sized iterable
container based on just the two functions (or bound methods) provided to
the constructor.
"""
def __init__(self, item_getter, length_getter):
self._item_getter = item_getter
self._get_length = length_getter
def __getitem__(self, index):
return self._item_getter(index)
def __len__(self):
return self._get_length()
class multiAttrObj():
# Init method cannot be changed...generated by SWIG (in real case)
def __init__(self):
self._attrA = "a b c d e f".split()
self._attrB = "1 2 3 4 5".split()
def __getattr__(self, name):
"""This will create AttrA or AttrB when they are first referenced."""
if name == 'AttrA':
self.AttrA = SequenceProxy(self.getAttrA, self.nAttrA)
return self.AttrA
elif name == 'AttrB':
self.AttrB = SequenceProxy(self.getAttrB, self.nAttrB)
return self.AttrB
else:
raise AttributeError('{!r} is not an attribute of {}'.format(
name, self.__class__.__name__))
def __enter__(self):
return self
def __exit__(self, *args):
pass # will implicitly return None which mean handle exceptions normally
def nAttrA(self):
return len(self._attrA) # do whatever is needed here...
def nAttrB(self):
return len(self._attrB) # do whatever is needed here...
def getAttrA(self, i):
return self._attrA[i] # do whatever is needed here...
def getAttrB(self, i):
return self._attrB[i] # do whatever is needed here...