我的project中有这个(下面)代码模式,这是一个外汇客户端。我的问题源于this代码审查。该审核的结果导致我之前从tuple
派生的类定义现在派生为object
形式。这样做是为了在review中实现比我建议的更简单的 reifying (并且导致大的性能改进)。
实现的方式与类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
>>> foo = Foo(1)
>>> foo.value
Reifying
1
>>> foo.value
1
但也允许属性赋值。 (毕竟python中没有任何东西是私有的)
>>> foo.value = 2
>>> foo.value
2
我真的想收回tuple
个实例的安全性。知道来自服务器的信息不会被意外更改并采取行动。 (对于我和其他可能选择使用我的代码的人来说)
好的,这个问题的上下文:我如何在cython中实现上面提出不可变实例的Foo类? 受到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
>>>
答案 0 :(得分:1)
让我们来看看为什么我们的方法不起作用。
首先:你无法访问cdef
- 来自python的成员。这意味着python看不到cdef int _value
,所以即使调用__getattr__
,__getattribute__(self, '_value')
也会抛出。
第二:cdef readonly int value
不仅仅是眼睛。
通过声明成员readonly value
,您可以定义一个只有getter(而且没有setter)的属性value
,您可以在cythonized C代码中看到它。
在Cython创建的类Bar
的类型描述符(在所有Cython创建C扩展名之后)中,您可以找到类的setter / 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。
这意味着,在创建类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
好吧,并非一切都很棒:
但这取决于你做出权衡。
PS:它看起来很像您在代码审查中开始使用的代码。对我来说,属性的使用看起来比在运行时更改对象的“接口”更自然(更易于维护,更少混淆)。但是__getattr__
的使用建议肯定有用。您最了解项目所处的位置,因此您决定哪种工具最适合。