使用描述符协议

时间:2016-10-04 08:58:54

标签: python

假设我有一个现有的类,例如做一些数学的东西:

class Vector:

    def __init__(self, x, y):
        self.x = y
        self.y = y

    def norm(self):
        return math.sqrt(math.pow(self.x, 2) + math.pow(self.y, 2))

现在,出于某种原因,我想让Python不像任何变量那样存储成员xy。我宁愿希望Python内部将它们存储为字符串。或者它将它们存储在专用缓冲​​区中,可能与某些C代码的互操作性。所以(对于字符串情况)我构建了以下描述符

class MyStringMemory(object):

    def __init__(self, convert):
        self.convert = convert

    def __get__(self, obj, objtype):
        print('Read')
        return self.convert(self.prop)

    def __set__(self, obj, val):
        print('Write')
        self.prop = str(val)

    def __delete__(self, obj):
        print('Delete')

我将现有的vector类包装在一个新的类中,其中成员xy变为MyStringMemory

class StringVector(Vector):

    def __init__(self, x, y):
        self.x = x
        self.y = y

    x = MyStringMemory(float)
    y = MyStringMemory(float)

最后,一些驱动代码:

v = StringVector(1, 2)
print(v.norm())
v.x, v.y = 10, 20
print(v.norm())

毕竟,我将xy的内部表示替换为原始类中没有任何更改的字符串,但仍具有完整功能。

我只是想知道:这个概念会普遍发挥作用还是会遇到严重的陷阱?正如我所说的那样,主要的想法是将数据存储到一个特定的缓冲区位置,稍后由一个C代码。

编辑:我正在做的目的如下。目前,我有一个很好的工作程序,其中一些物理对象,所有类型MyPhysicalObj互相交互。 对象中的代码使用Numpy进行矢量化。现在,我还想在所有对象上呈现一些代码。例如,每个对象都有一个energy,它由每个对象的复杂矢量化代码计算。现在我想总结一切能量。我可以迭代所有对象并总结,但这很慢。所以我宁愿将每个对象的属性energy自动存储到全局预定义的缓冲区中,我只能在该缓冲区上使用numpy.sum

2 个答案:

答案 0 :(得分:1)

关于python描述符有一个缺陷。

使用您的代码,您将引用相同的值,分别存储在StringVector.x.prop和StringVector.y.prop中:

v1 = StringVector(1, 2)
print('current StringVector "x": ', StringVector.__dict__['x'].prop)
v2 = StringVector(3, 4)
print('current StringVector "x": ', StringVector.__dict__['x'].prop)

print(v1.x)
print(v2.x)

将具有以下输出:

Write
Write
current StringVector "x":  1
Write
Write
current StringVector "x":  3
Read
3.0
Read
3.0

我想这不是你想要的=)。要在对象内存储每个对象的唯一值,请进行以下更改:

class MyNewStringMemory(object):
    def __init__(self, convert, name):
        self.convert = convert
        self.name = '_' + name

    def __get__(self, obj, objtype):
        print('Read')
        return self.convert(getattr(obj, self.name))

    def __set__(self, obj, val):
        print('Write')
        setattr(obj, self.name, str(val))

    def __delete__(self, obj):
        print('Delete')


class StringVector(Vector):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    x = MyNewStringMemory(float, 'x')
    y = MyNewStringMemory(float, 'y')


v1 = StringVector(1, 2)
v2 = StringVector(3, 4)

print(v1.x, type(v1.x))
print(v1._x, type(v1._x))
print(v2.x, type(v2.x))
print(v2._x, type(v2._x))

输出:

Write
Write
Write
Write
Read
Read
1.0 <class 'float'>
1 <class 'str'>
Read
Read
3.0 <class 'float'>
3 <class 'str'>

此外,您绝对可以使用描述符的__set__方法将数据保存在集中存储中。

请参阅此文档:https://docs.python.org/3/howto/descriptor.html

答案 1 :(得分:0)

如果你需要一个通用转换器(&#39;转换&#39;)就像你一样,这就是你要走的路。

当您需要创建大量实例时,最大的缺点将是性能(我认为您可能会因为类Vector而这样)。由于python类启动缓慢,因此速度很慢。

在这种情况下,您可以考虑使用namedTuple,您可以看到文档与您拥有的场景类似。

作为旁注:如果可能,为什么不在init方法上创建一个带有x和y字符串表示的dict?然后继续使用x和y作为正常变量而不进行所有转换