[英]python SWIG object compare
我有兩個SWIG對象列表:a和b。 我需要進行設置或比較操作來查找不在b中的項目。 (我還有其他操作,但這是一個很好的開始例子)。
set(a) -set(b)
沒有給出准確的結果所以我試過:
[item for item in a if item not in b]
在這兩種情況下,它都不返回任何項目,即使a和b沒有共同的元素
我有一個項目,其值為:
<Swig Object of type 'OpenAccess_4::oaRect *' at 0x1ad6eea0>
和b中的項目:
<Swig Object of type 'OpenAccess_4::oaRect *' at 0x1ad6eca8>
我比較時被認為是==
'is'操作符可以正常工作,但是對2個列表進行單獨比較將非常耗時,因為它們可能很大,並且操作會重復多次。
我對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輸出也無法證明它始終是返回的同一對象,因此為了安全起見,它始終會創建一個新的代理。
在運行時,我們可以添加一個typemap來檢查和處理這種情況(例如,使用std::map
查找實例)。 這是一個單獨的問題而且是對真實問題的分心,因為它不會產生f1==f3
,因為它們是截然不同但卻相同的對象。
請注意,在C ++中我們遇到了同樣的問題,但由於各種設計原因,我們甚至無法使用operator==
編譯一個簡單的函數:
bool bar() {
static foo f1{2};
static foo f2{2};
return f1==f2;
}
無法編譯:
test.cxx:6 error: no match for ‘operator==’ in ‘f1 == f2’
讓我們來探討一下,以了解生成Python包裝器時SWIG的行為。 如果我們在我們的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> >
這正是我們希望的行為, v
相同的所有實例都是相同的,另一個不是。 盡管每個實例的代理對象不同。
如果operator==
我們寫的是非會員運營商會怎么樣?
%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==
並不是很清楚。 如果您使用-Wall
運行SWIG,您會看到我們現在收到警告:
test.i:15: Warning 503: Can't wrap 'operator ==' unless renamed to a valid identifier.
因此,我們假設我們無法編輯C ++代碼並查看“修復”問題。 我們有幾種方法可以做到這一點。
我們可以要求SWIG重命名operator==
它不知道如何使用%rename
作為函數換行,如警告所示:
%rename(compare_foo) operator==(const foo&, const foo&);
這需要在SWIG看到operator==
的聲明/定義之前的任何地方編寫。
這本身並不足以恢復我們想要的行為,所以我們通過在SWIG的輸出中添加一些額外的Python來修復它。 回想一下,Python函數__eq__
具有以下形式:
object.__eq__(self, other)
這實際上是我們的C ++ operator==
非常好的匹配,所以我們可以簡單地在SWIG接口文件的末尾添加以下內容:
%pythoncode %{ foo.__eq__ = lambda a,b: compare_foo(a,b) %}
這恢復了我們追求的行為。 (注意:我不確定為什么這里需要lambda,我不希望它是必需的)
我們也可以通過在我們的界面中編寫更多C ++來實現這一點,但不必修改我們正在包裝的實際代碼。 基本上我們想要做的是在foo
實現__eq__
。 這可以通過%extend
來完成,它擴展了一個類,但只能從目標語言的角度來看。 為了完整性,我們使用%ignore
來抑制我們在處理問題時收到警告的功能。
%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 av== bv; } bool bar() { static foo f1{2}; static foo f2{2}; return f1==f2; } %}
這再次恢復了我們追求的行為,不同之處在於膠水被包含在C ++膠水而不是Python膠水中。
最后,如果您使用-builtin
運行SWIG Python, -builtin
這些解決方案都不適用於非成員運算符情況。 值得注意的是,默認的SWIG輸出包括tp_richcompare
函數。 仍然要使用我們的operator==
而不是底層對象的地址比較,您需要使用槽機制來注冊我們自己的函數,類似於上面的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.