簡體   English   中英

python SWIG對象比較

[英]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 ++代碼並查看“修復”問題。 我們有幾種方法可以做到這一點。

  1. 我們可以要求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,我不希望它是必需的)

  2. 我們也可以通過在我們的界面中編寫更多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膠水中。

  3. 最后,如果您使用-builtin運行SWIG Python, -builtin這些解決方案都不適用於非成員運算符情況。 值得注意的是,默認的SWIG輸出包括tp_richcompare函數。 仍然要使用我們的operator==而不是底層對象的地址比較,您需要使用槽機制來注冊我們自己的函數,類似於上面的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM