我有两个swig对象列表:a和b。我需要执行set或comparison操作来查找a中不在b中的项(我还有其他操作要做,但这是一个很好的开始示例)。
set(a) -set(b)
不能给出准确的结果
所以我试着:
[item for item in a if item not in b]
在这两种情况下,它都不返回任何项,即使a和b没有共同的元素
我在A中有一个项目的值为:
<Swig Object of type 'OpenAccess_4::oaRect *' at 0x1ad6eea0>
以及b中的一项:
<Swig Object of type 'OpenAccess_4::oaRect *' at 0x1ad6eca8>
当我比较时,这被认为是。
“is”运算符工作正常,但将非常耗时
对这两个列表进行单独比较,因为它们可能很大,并且
手术重复了很多次。
Python中的SWIG对象不允许我
是否执行“=”和“设置”操作?
最佳答案
如果您的对象实现了python对象协议,那么您关心的高级容器操作将自动工作。为了展示它们是如何实现的,我们将关注operator==
/__eq__
。
我们可以设置一个测试用例来研究比较在python和swig中是如何工作的:
import test
f1 = test.foo(1)
f2 = test.foo(2)
f3 = test.foo(1)
f4 = test.static_foo()
f5 = test.static_foo()
a = (f1,f2,f3,f4,f5)
compared = "\n".join((",".join(str(int(y==x)) for y in a) for x in a))
print compared
print "\n".join((str(x) for x in a))
以我们天真的实现为出发点:
%module test
%inline %{
struct foo {
foo(const int v) : v(v) {}
int v;
};
foo *static_foo() {
static foo f{1};
return &f;
}
%}
运行时,这将提供:
1,0,0,0,0
0,1,0,0,0
0,0,1,0,0
0,0,0,1,0
0,0,0,0,1
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb71e79f8> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb71e7ad0> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb71e7b78> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb71e7b90> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb71e7b60> >
这不是我们所希望的,它只是身份矩阵。
为什么会这样?从SWIG开始,默认情况下,为从C++返回到Python的所有内容构造一个新的代理对象。即使在静态情况下,swig在编译时的输出也不能证明它总是返回同一个对象,因此为了安全起见,它总是生成一个新的代理。
在运行时,我们可以添加一个类型映射来检查和处理该情况(例如,使用在中查找实例的
std::map
)。不过,这是一个单独的问题,而且会分散人们对实际问题的注意力,因为它们是不同但等价的对象,因此不会产生f1==f3
。请注意,在C++中,我们有同样的问题,但是由于各种设计原因,我们甚至不能用< cc>编译一个简单的函数:
bool bar() {
static foo f1{2};
static foo f2{2};
return f1==f2;
}
无法编译:
test.cxx:6 error: no match for ‘operator==’ in ‘f1 == f2’
让我们对此进行一些探讨,了解SWIG在生成Python包装器时的行为如果向C++类添加一个
operator==
:%module test
%inline %{
struct foo {
foo(const int v) : v(v) {}
int v;
bool operator==(const foo& o) const { return v == o.v; }
};
foo *static_foo() {
static foo f{1};
return &f;
}
突然,swig做了正确的事情并将其传递给python,因此我们的测试用例现在产生:
1,0,1,1,1
0,1,0,0,0
1,0,1,1,1
1,0,1,1,1
1,0,1,1,1
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb72869f8> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb7286ad0> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb7286a70> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb7286bc0> >
<test.foo; proxy of <Swig Object of type 'foo *' at 0xb7286bd8> >
这正是我们所希望的行为,所有
operator==
相同的实例都是相等的,另一个则不是。尽管代理对象在每个实例中都不同。如果我们编写的
v
是非成员运算符,会发生什么情况?%module test
%inline %{
struct foo {
foo(const int v) : v(v) {}
int v;
};
foo *static_foo() {
static foo f{1};
return &f;
}
bool operator==(const foo& a, const foo& b) { return a.v== b.v; }
突然我们失去了行为,我们又回来了
1,0,0,0,0
0,1,0,0,0
0,0,1,0,0
0,0,0,1,0
0,0,0,0,1
为什么?因为在这种情况下,不太清楚如何处理
operator==
。如果您使用operator==
运行swig,您将看到我们现在收到一个警告:test.i:15: Warning 503: Can't wrap 'operator ==' unless renamed to a valid identifier.
让我们假设我们不能编辑C++代码,看“修复”这个问题。不过,我们有几种方法可以做到这一点。
我们可以让swig将它不知道如何使用
-Wall
包装成函数的operator==
重命名,如警告所示:%rename(compare_foo) operator==(const foo&, const foo&);
这需要在swig看到我们的
%rename
声明/定义之前写在任何地方。但这本身不足以恢复我们想要的行为,所以我们通过在swig的输出中添加一些额外的python来修复它。回想一下,Python函数
operator==
具有以下形式:object.__eq__(self, other)
这实际上是我们的C++
__eq__
的一个很好的匹配,所以我们可以简单地在SWIG接口文件的末尾添加以下内容:%pythoncode %{
foo.__eq__ = lambda a,b: compare_foo(a,b)
%}
恢复了我们的行为。(注:我不确定这里为什么需要lambda,我没想到会需要它)
我们还可以在我们的界面中编写更多的C++,但不必修改我们正在打包的实际代码。基本上我们要做的是在
operator==
内部实现__eq__
这可以通过扩展类的foo
完成,但只能从目标语言的角度。为了完整性,我们使用%extend
来抑制函数,因为我们已经处理了这个问题,所以我们得到了一个警告。%module test
%ignore operator==(const foo&, const foo&);
%extend foo {
bool __eq__(const foo& o) const {
return *$self == o;
}
}
%inline %{
struct foo {
foo(const int v) : v(v) {}
int v;
};
foo *static_foo() {
static foo f{1};
return &f;
}
bool operator==(const foo& a, const foo& b) { return a.v== b.v; }
bool bar() {
static foo f1{2};
static foo f2{2};
return f1==f2;
}
%}
这又恢复了我们正在进行的行为,区别在于胶水被包含为C++胶而不是Python胶。
最后,如果您使用
%ignore
运行swig python,那么这些解决方案都不适用于非成员运算符情况。值得注意的是,默认swig输出包含一个-builtin
函数。仍然要使用我们的tp_richcompare
而不是底层对象的地址比较,您需要使用slots机制来注册我们自己的函数,类似于上面的代码。
关于python - python SWIG对象比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25087232/