python - python SWIG对象比较

标签 python compare swig

我有两个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/

相关文章:

python - 来自按级别分组的多索引 Pandas 数据框的子图

Python:re.compile 和 re.sub

mysql - 如何连接两个表但只返回与特定列不匹配的行,其中也包含 NULL?

javascript - 我如何将 csrf_token 包含到 dropzone Post 请求(Django)

python - 发送电子邮件的密码保护

python - 如何将环境变量添加到 Django 项目中

C++ 安全高效地将 std::weak_ordering 转换为 int

c++ - $stdin 与 std::istream 的兼容性,使用 swig、C++ 和 Ruby

c++ - SWIG Python 和 C++ std::string 空终止问题

go - 痛饮 + 去 : C source files not allowed when not using cgo