Cython 具体化属性?
Cython reify attributes?
我的 project, which is a FOREX client. My question stems from this code review. The outcome of that review resulted in my class definitions that previously derived from tuple
now derive form object
. This was done to allow for simpler reifying than my suggested idea in the review 中有这个(下面的)代码模式(并且导致性能显着提高)。
实现方式与 class Foo
(如下)类似。
reify = lambda x: x
class Foo(object):
def __init__(self, value):
self._value = value
def __getattr__(self, item):
print('Reifying')
attr = object.__getattribute__(self, '_'+item)
value = reify(attr)
setattr(self, item, value)
return value
示例 reify
ing
>>> foo = Foo(1)
>>> foo.value
Reifying
1
>>> foo.value
1
而且还允许进行属性分配。 (毕竟 python 中没有任何内容是私有的)
>>> foo.value = 2
>>> foo.value
2
我真的很想收回 tuple
个实例的安全性。知道来自服务器的信息不会被意外更改和采取行动。 (对于我自己和可能选择使用我的代码的其他人)
好的,这就是这个 问题的上下文:我如何在 cython 中实现上面的 Foo class 来呈现一个不可变的实例? 启发通过 this 问题。
我天真地试过这个:
cdef class Bar:
cdef readonly int value
cdef int _value
def __init__(self, value):
self._value = value
def __getattr__(self, item):
attr = object.__getattribute__(self, '_'+item)
value = reify(attr)
setattr(self, item, value)
return value
但很快发现 __getattr__
从未被调用,因为 value
是用 0
初始化的
>>> a = Bar(1)
>>> a.value
0
>>>
让我们来看看,为什么我们的方法不起作用。
第一件事:您无法从 python 访问 cdef
-成员。这意味着 python 没有看到 cdef int _value
,所以即使 __getattr__
被调用,__getattribute__(self, '_value')
也会抛出。
其次:cdef readonly int value
不止于此
通过声明一个成员 readonly value
,您定义了一个 属性 value
,它只有一个 getter(没有 setter),您可以看到在 cythonized C 代码中。
在 Cython 创建的 class Bar
的类型描述符(毕竟 Cython 创建了一个 C 扩展)中,您可以找到 class 的 setters/getters :
static PyTypeObject __pyx_type_10prop_class_Bar = {
PyVarObject_HEAD_INIT(0, 0)
"test.Bar", /*tp_name*/
....
__pyx_getsets_10prop_class_Bar, /*tp_getset*/
...
};
您还可以查看属性:
static struct PyGetSetDef __pyx_getsets_10prop_class_Bar[] = {
{(char *)"value", __pyx_getprop_10prop_class_3Bar_value, 0, (char *)0, 0},
{0, 0, 0, 0, 0}
};
如您所见,仅定义了 getter(称为 __pyx_getprop_10prop_class_3Bar_value
),但未定义 setter。
这意味着,在创建 class Bar
的对象之后,已经有一个名为 value
的属性,因此您的 __getattr__
永远不会使用参数 value
.
可以做什么?我会说,您要实现的是只读(缓存)属性。沿线的某个地方(不涉及 Cython):
class Foo:
def __init__(self, value):
self._value = value
self._cached_value = None
@property
def value(self):
if self._cached_value is None:
print("calculating")
self._cached_value=self._value # evaluate
return self._cached_value
现在
>>> f=Foo(2)
>>> f.value
calculating
2
>> f.value # no calculation!
2
好吧,并非一切都很好:
- 与您的原始解决方案相比,您有一些额外的 checks/indirection,这可能会稍微降低性能,但这是控制对您的属性的访问所付出的代价(成本略高或没有控制)。
- 有一些样板代码,但使用 python 的动态特性(例如装饰器)并不难减少。
但这取决于您做出权衡。
PS:它看起来很像您开始进入代码审查的代码。对我来说,属性的使用看起来比在运行时更改对象的 "interface" 更自然(更易于维护,更少混淆)。但是 __getattr__
的提议用法肯定有其优点。您最了解项目的发展方向,因此您可以决定哪种工具最适合。
我的 project, which is a FOREX client. My question stems from this code review. The outcome of that review resulted in my class definitions that previously derived from tuple
now derive form object
. This was done to allow for simpler reifying than my suggested idea in the review 中有这个(下面的)代码模式(并且导致性能显着提高)。
实现方式与 class Foo
(如下)类似。
reify = lambda x: x
class Foo(object):
def __init__(self, value):
self._value = value
def __getattr__(self, item):
print('Reifying')
attr = object.__getattribute__(self, '_'+item)
value = reify(attr)
setattr(self, item, value)
return value
示例 reify
ing
>>> foo = Foo(1)
>>> foo.value
Reifying
1
>>> foo.value
1
而且还允许进行属性分配。 (毕竟 python 中没有任何内容是私有的)
>>> foo.value = 2
>>> foo.value
2
我真的很想收回 tuple
个实例的安全性。知道来自服务器的信息不会被意外更改和采取行动。 (对于我自己和可能选择使用我的代码的其他人)
好的,这就是这个 问题的上下文:我如何在 cython 中实现上面的 Foo class 来呈现一个不可变的实例? 启发通过 this 问题。
我天真地试过这个:
cdef class Bar:
cdef readonly int value
cdef int _value
def __init__(self, value):
self._value = value
def __getattr__(self, item):
attr = object.__getattribute__(self, '_'+item)
value = reify(attr)
setattr(self, item, value)
return value
但很快发现 __getattr__
从未被调用,因为 value
是用 0
>>> a = Bar(1)
>>> a.value
0
>>>
让我们来看看,为什么我们的方法不起作用。
第一件事:您无法从 python 访问 cdef
-成员。这意味着 python 没有看到 cdef int _value
,所以即使 __getattr__
被调用,__getattribute__(self, '_value')
也会抛出。
其次:cdef readonly int value
不止于此
通过声明一个成员 readonly value
,您定义了一个 属性 value
,它只有一个 getter(没有 setter),您可以看到在 cythonized C 代码中。
在 Cython 创建的 class Bar
的类型描述符(毕竟 Cython 创建了一个 C 扩展)中,您可以找到 class 的 setters/getters :
static PyTypeObject __pyx_type_10prop_class_Bar = {
PyVarObject_HEAD_INIT(0, 0)
"test.Bar", /*tp_name*/
....
__pyx_getsets_10prop_class_Bar, /*tp_getset*/
...
};
您还可以查看属性:
static struct PyGetSetDef __pyx_getsets_10prop_class_Bar[] = {
{(char *)"value", __pyx_getprop_10prop_class_3Bar_value, 0, (char *)0, 0},
{0, 0, 0, 0, 0}
};
如您所见,仅定义了 getter(称为 __pyx_getprop_10prop_class_3Bar_value
),但未定义 setter。
这意味着,在创建 class Bar
的对象之后,已经有一个名为 value
的属性,因此您的 __getattr__
永远不会使用参数 value
.
可以做什么?我会说,您要实现的是只读(缓存)属性。沿线的某个地方(不涉及 Cython):
class Foo:
def __init__(self, value):
self._value = value
self._cached_value = None
@property
def value(self):
if self._cached_value is None:
print("calculating")
self._cached_value=self._value # evaluate
return self._cached_value
现在
>>> f=Foo(2)
>>> f.value
calculating
2
>> f.value # no calculation!
2
好吧,并非一切都很好:
- 与您的原始解决方案相比,您有一些额外的 checks/indirection,这可能会稍微降低性能,但这是控制对您的属性的访问所付出的代价(成本略高或没有控制)。
- 有一些样板代码,但使用 python 的动态特性(例如装饰器)并不难减少。
但这取决于您做出权衡。
PS:它看起来很像您开始进入代码审查的代码。对我来说,属性的使用看起来比在运行时更改对象的 "interface" 更自然(更易于维护,更少混淆)。但是 __getattr__
的提议用法肯定有其优点。您最了解项目的发展方向,因此您可以决定哪种工具最适合。