Python属性和Swig

fuz*_*fle 18 c++ python swig properties

我试图使用swig为某些C++代码创建python绑定.我似乎遇到了一个问题,试图从一些访问器函数创建python属性我有如下方法:

class Player {
public:
  void entity(Entity* entity);
  Entity* entity() const;
};
Run Code Online (Sandbox Code Playgroud)

我尝试使用python属性函数创建一个属性,但似乎swig生成的包装类与它至少对于setter不兼容.

你如何使用swig创建属性?

Maj*_*cRa 35

有一种简单的方法可以使用swig方法创建python属性.
假设C++代码Example.h:

C++标题

class Example{
    public:
      void SetX(int x);
      int  GetX() const;
    };
Run Code Online (Sandbox Code Playgroud)

让我们将这个setter和getter转换为python propery'x'.诀窍在.i文件中.我们添加一些"swiggy"内联python代码(带有%pythoncode),它插入到生成的python类的主体中(在自动生成的python代码中).

Swig包装Example.i

%module example
%{
     #include "example.h"
%}

class Example{
    public:
      void SetX(int x);
      int  GetX() const;

      %pythoncode %{
         __swig_getmethods__["x"] = GetX
         __swig_setmethods__["x"] = SetX
         if _newclass: x = property(GetX, SetX)
      %}
    };
Run Code Online (Sandbox Code Playgroud)

检查python代码:

python测试代码

import example

test = example.Example()
test.x = 5
print "Ha ha ha! It works! X = ", repr(test.x)
Run Code Online (Sandbox Code Playgroud)

这就对了!



让它更简单!

无需重写类定义.感谢Joshua的建议,可以使用SWIG指令%extend ClassName {}.

Swig包装Example.i

%module example
%{
     #include "example.h"
%}

%extend Example{
      %pythoncode %{
         __swig_getmethods__["x"] = GetX
         __swig_setmethods__["x"] = SetX
         if _newclass: x = property(GetX, SetX)
      %}
    };
Run Code Online (Sandbox Code Playgroud)

隐藏二传手和吸气功能

可以看出,转换后test.GetX()和test.SetX()仍然存在.人们可以隐藏它们:

a)使用%rename重命名函数,在开头添加'_',从而使python的方法为"private".在SWIG接口.i Example.i中

...
class Example{
   %rename(_SetX) SetX(int);
   %rename(_GetX) GetX();
...
Run Code Online (Sandbox Code Playgroud)

(%重命名可以放在一些分开的地方,以节省将此类转换为其他语言的可能性,这些语言不需要这些'_')

b)或者可以使用%feature("shadow")

为什么会这样?

为什么我们必须使用这些东西通过使用SWIG将方法转换为属性?正如所说的,SWIG自私地覆盖_ setattr _,因此必须使用_swig_getmethods__swig_setmethods_来注册函数并保持swig方式.

为什么人们更喜欢这种方式?

上面列出的方法,特别是PropertyVoodoo的方法......就像烧房子炒鸡蛋一样.它还打破了类布局,因为必须创建继承类以从C++方法生成python属性.我的意思是如果类Cow返回类Milk并且继承的类是MilkWithProperties(Milk),那么如何使Cow生产MilkWithProperties?

这种方法允许人们:

  1. 显式控制要转换为python属性的C++方法
  2. 转换规则位于swig接口(*.i)文件中,它们应该位于它们的位置
  3. 一个生成自动生成的.py文件
  4. 保持swig生成的.py文件中插入的swig语法
  5. 如果将库包装到其他语言,则忽略%pythoncode

更新 在较新的版本中,SWIG放弃了_swig_property,因此只需使用属性即可.它适用于旧版swig相同.我改变了帖子.

  • 谢谢!这看起来很漂亮.如果其他人发现它有用,请记住,使用swig,您可以使用%extend ClassName {}在单独的头文件中定义类时,从接口文件中向类添加方法.我相信当您使用相同的名称重载C++ getter和setter时,此方法甚至可以工作. (2认同)

cdi*_*ins 20

使用Attributes.i

在SWIG Lib文件夹中有一个名为"attributes.i"的文件,该文件未在文档中讨论,但包含内联文档.

您所要做的就是将以下行添加到您的接口文件中.

%include <attributes.i>
Run Code Online (Sandbox Code Playgroud)

然后,您将收到许多宏(例如%属性),用于定义现有方法的属性.

摘录来自attributes.i文件中的文档:

以下宏将一对set/get方法转换为"native"属性.当你有一对基本类型的get/set方法时,使用%属性,如:

  %attribute(A, int, a, get_a, set_a);

  struct A
  {
    int get_a() const;
    void set_a(int aa);
  };
Run Code Online (Sandbox Code Playgroud)

  • 在最新版本的SWIG中应该是`%include <attribute.i>`. (4认同)

hao*_*hao 4

哦,这很棘手(也很有趣)。SWIG并不认为这是生成 @property 的机会:我想如果做得不仔细,很容易出错并识别出大量误报。但是,由于 SWIG 不会在生成 C++ 时执行此操作,因此仍然完全可以使用小型元类在 Python 中执行此操作。

因此,下面假设我们有一个 Math 类,可以让我们设置和获取名为“pi”的整数变量。然后我们可以使用这段代码:

示例.h

#ifndef EXAMPLE_H
#define EXAMPLE_H

class Math {
 public:
    int pi() const {
        return this->_pi;
    }

    void pi(int pi) {
        this->_pi = pi;
    }

 private:
    int _pi;
};

#endif
Run Code Online (Sandbox Code Playgroud)

示例.i

%module example

%{
    #define SWIG_FILE_WITH_INIT
    #include "example.h"
%}

[essentially example.h repeated again]
Run Code Online (Sandbox Code Playgroud)

示例.cpp

#include "example.h"
Run Code Online (Sandbox Code Playgroud)

实用工具.py

class PropertyVoodoo(type):
    """A metaclass. Initializes when the *class* is initialized, not
    the object. Therefore, we are free to muck around the class
    methods and, specifically, descriptors."""

    def __init__(cls, *a):
        # OK, so the list of C++ properties using the style described
        # in the OP is stored in a __properties__ magic variable on
        # the class.
        for prop in cls.__properties__:

            # Get accessor.
            def fget(self):
                # Get the SWIG class using super. We have to use super
                # because the only information we're working off of is
                # the class object itself (cls). This is not the most
                # robust way of doing things but works when the SWIG
                # class is the only superclass.
                s = super(cls, self)

                # Now get the C++ method and call its operator().
                return getattr(s, prop)()

            # Set accessor.
            def fset(self, value):
                # Same as above.
                s = super(cls, self)

                # Call its overloaded operator(int value) to set it.
                return getattr(s, prop)(value)

            # Properties in Python are descriptors, which are in turn
            # static variables on the class. So, here we create the
            # static variable and set it to the property.
            setattr(cls, prop, property(fget=fget, fset=fset))

        # type() needs the additional arguments we didn't use to do
        # inheritance. (Parent classes are passed in as arguments as
        # part of the metaclass protocol.) Usually a = [<some swig
        # class>] right now.
        super(PropertyVoodoo, cls).__init__(*a)

        # One more piece of work: SWIG selfishly overrides
        # __setattr__. Normal Python classes use object.__setattr__,
        # so that's what we use here. It's not really important whose
        # __setattr__ we use as long as we skip the SWIG class in the
        # inheritance chain because SWIG's __setattr__ will skip the
        # property we just created.
        def __setattr__(self, name, value):
            # Only do this for the properties listed.
            if name in cls.__properties__:
                object.__setattr__(self, name, value)
            else:
                # Same as above.
                s = super(cls, self)

                s.__setattr__(name, value)

        # Note that __setattr__ is supposed to be an instance method,
        # hence the self. Simply assigning it to the class attribute
        # will ensure it's an instance method; that is, it will *not*
        # turn into a static/classmethod magically.
        cls.__setattr__ = __setattr__
Run Code Online (Sandbox Code Playgroud)

一些文件.py

import example
from util import PropertyVoodoo

class Math(example.Math):
    __properties__ = ['pi']
    __metaclass__  = PropertyVoodoo

m = Math()
print m.pi
m.pi = 1024
print m.pi
m.pi = 10000
print m.pi
Run Code Online (Sandbox Code Playgroud)

因此,最终结果就是您必须为每个 SWIG Python 类创建一个包装类,然后键入两行:一行用于标记应在属性中转换哪些方法,另一行用于引入元类。