当前位置: 首页 > 知识库问答 >
问题:

在PyGetSetDef中使用闭包重用属性getter setter

尉迟卓
2023-03-14

正如标题所示,我试图创建一堆属性,但是代码变得重复和混乱。我想使用< code>closure参数使代码更紧凑。

根据C API参考,闭包是一个函数指针,它为getter/setter提供附加信息。我还没有找到一个使用中的例子。

这是我目前使用它的方式:

static void closure_1() {};
static void closure_2() {};
...
static PyObject *
FOO_getter(FOO* self, void *closure) {
    if (closure == &closure_1) {
        return self->bar_1;
    } else if (closure == &closure_2) {
        return self->bar_2;
    }
}
static int
FOO_setter(FOO* self, PyObject *value, void *closure) {
    if (closure == &closure_1) {
        if (somehow value is invalid) {
            PyErr_SetString(PyExc_ValueError, "invalid value for bar_1.");
            return -1;
        }
    } else if (closure == closure_2) {
        if (somehow value is invalid) {
            PyErr_SetString(PyExc_ValueError, "invalid value for bar_2.");
            return -1;
        }
    }
    return 0;
}
static PyGetSetDef FOO_getsetters[] = {
    {"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", closure_1},
    {"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", closure_2},
    {NULL}  /* Sentinel */
};
...

它按照我想要的方式工作,但它看起来更像一个黑客,而不是什么“pythonic式”的东西。有没有更好的处理方式?例如以某种方式调用闭包。

共有2个答案

柯升
2023-03-14

尽管有留档,我还是假设闭包可以是您想要的任何指针。那么传递一个“对象”怎么样,因为C不支持闭包(除了在运行时字面上生成函数)。

在对象中,我们可以将成员的偏移量存储在FOO中,并存储一个指向特定属性验证器的指针。

typedef int (*Validator)(FOO *, const struct Attribute *, void *);

typedef struct Attribute {
   const char *name;
   size_t offset;
   Validator validator;
} Attribute;

static PyObject **resolve_offset(FOO *self, const Attribute *attr) {
   return (PyObject **)( ( (char *)self ) + attr->offset );
}

static PyObject *FOO_getter(FOO *self, void *_attr) {
   const Attribute *attr = (const Attribute *)_attr;
   return *resolve_offset(self, attr);
}

static int FOO_setter(FOO *self, PyObject *val, void *_attr) {
   const Attribute *attr = (const Attribute *)_attr;
   if (attr->validator(self, attr, val)) {
      *resolve_offset(self, attr) = val;
      return 0;
   } else {
      // Building the string to include attr->name is left to you.
      PyErr_SetString(PyExc_ValueError, "invalid value.");
      return -1;
   }
}

static int FOO_bar_1_validator(FOO *self, const Attribute *attr, void *val) { ... }
static int FOO_bar_2_validator(FOO *self, const Attribute *attr, void *val) { ... }

#define ATTRIBUTE(name)                      \
   static Attribute FOO_ ## name ## attr = { \
      #name,                                 \
      offsetof(FOO, name),                   \
      FOO_ ## name ## _validator             \
   };

ATTRIBUTE(bar_1);
ATTRIBUTE(bar_2);

#define PY_ATTR_DEF(name) { \
   #name,                   \
   (getter)FOO_getter,      \
   (setter)FOO_setter,      \
   #name " attribute",      \
   &(FOO_ ## name ## attr)  \
}

static PyGetSetDef FOO_getsetters[] = {
    PY_ATTR_DEF(bar_1),
    PY_ATTR_DEF(bar_2),
    { NULL }
};

我最初写道:

< code>resolve_offset当然依赖于未定义的行为,但它应该工作得很好。另一种选择是在我们的属性对象中有三个函数(get、validate、set ),而不是一个,但是这忽略了问题的关键。

但@tstanisl指出,它看起来不像UB。令人惊叹的

呼延俊良
2023-03-14

我猜这个闭包是用来向Foo_getter传递一个额外的上下文。它应该是简化访问Foo成员的东西。文档可能是错误的。它应该是“可选指针”,而不是“可选函数指针”。

考虑传递成员的偏移量。通过在 stddef.h定义的宏的标准偏移,可以很容易地获得到结构杆件的偏移。它是一个小的无符号整数,适合 void* 类型。

static PyGetSetDef FOO_getsetters[] = {
    {"bar_1", (getter) FOO_getter, (setter) FOO_setter, "bar_1 attribute", (void*)offsetof(FOO, bar_1)},
    {"bar_2", (getter) FOO_getter, (setter) FOO_setter, "bar_2 attribute", (void*)offsetof(FOO, bar_2)},
    {NULL}  /* Sentinel */
};

现在,获取者可以是:

static PyObject *
FOO_getter(FOO* self, void *closure) {
    // pointer to location where the FOO's member is stored
    char *memb_ptr = (char*)self + (size_t)closure;
    // cast to `PyObject**` because `mem_ptr` points to location where a pointer to `PyObject` is stored
    return *(PyObject**)mem_ptr;
}

对setter使用类似的模式。

 类似资料:
  • 我正在使用log4j管理Clojure应用程序中的日志记录。我希望能够使用系统变量为log4j中的单个包设置日志级别。属性文件。例如,下面是我的基本属性文件: 这将日志级别设置为中的DEBUG(特定日志代码所在的位置)。然而,我不希望这样——我希望它在本地是调试的,在生产上是信息的。所以,我使用一个环境变量: 这个实例中的设置为,我可以通过使用环境变量将日志级别设置为DEBUG来确认它是否可以正常

  • 问题内容: 我正在使用PropertiesConfiguration加载和存储属性值。 如果我在属性值中使用字符“ /”,则将其另存为“ \ /”。您能帮我只保存“ /”而不是“ \ /”吗?我在这里想念什么吗? 保存后,我的属性文件的值为 谢谢, 问题答案: 进行转义以满足属性文件格式的要求。的JavaDoc中介绍了这种格式。 如果那 不是 您想要的,那么您实际上就不需要属性文件,而是需要其他(

  • 正如我从thymeleaf文档中了解到的那样,我尝试了:

  • 问题内容: hibernate中的property标签的lazy属性允许按照以下链接延迟加载属性:http : //docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/mapping.html#mapping- declaration -属性 lazy(可选-默认为false):指定在首次访问实例变量时应延迟获取此属性。它需要构建时字节码检测

  • 我希望将JavaFX属性用于UI绑定,但我不希望它们出现在我的模型类中(请参见在模型类中使用JavaFX.Beans属性)。我的模型类有getter和setter,我想基于这些创建属性。例如,假设一个实例具有和方法,我将编写 希望会触发对的调用。但这似乎不起作用。我错过了什么?

  • 问题内容: 我为该标题表示歉意。我找不到更好的方法来解释这种情况。 我使用URL http://www.exampledepot.com/egs/java.util/Props.html中所述的Property类加载属性文件。 我的问题是我可以在该属性文件中使用属性吗? 例: test.properties 其他语法有可能吗? 谢谢 问题答案: 以前从未见过。您当然 可以 制作自己的预处理器。只要