动态python属性

时间:2014-12-30 22:06:38

标签: python properties

好吧,我正在重述我的问题,因为我的动机不够明确。

我们有一个类,用户将使用更复杂的存储/检索机制实际实现的属性填充。 我在下面对其进行了简化。 假设存储机制现在只是一个简单的字典:

class MyClass:
    def __init__(self, d):
        self.mydict = d

    def getterX(self):
        return self.mydict['X']
    def setterX(self, val):
        self.mydict['X'] = val
    X = property(getterX, setterX)

    def getterY(self):
        return self.mydict['Y']
    def setterY(self, val):
        self.mydict['Y'] = val
    Y = property(getterY, setterY)

da = {}
Ma = MyClass(da)

Ma.X = 5
Ma.Y = 6
print(Ma.X, Ma.Y)   #outputs 5 6
print(da)           #outputs {'Y': 6, 'X': 5}

这很好用,除了它非常容易出错和冗长。 它需要五行不少于 7 X 的实例,需要为每个新属性剪切+粘贴。 其中一些类可能有许多属性。

代替使用宏(我不认为Python支持并且效率低下),我试图用一些辅助函数来简化它:

da = {}

class MyHelper:
    def __init__(self, dict, label):
        self.mydict = dict
        self.mylabel = label

    def getter2(self, other):
         return self.mydict[self.mylabel]

    def setter2(self, other, value):
        self.mydict[self.mylabel] = value

def makeProperty(d, label):
    H = MyHelper(d, label)
    return property(H.getter2, H.setter2)

class MyClass:
    def __init__(self, d):
        self.mydict = d

    X = makeProperty(da, 'X')  #da is global, but should be self.mydict
    Y = makeProperty(da, 'Y')  #  " " "

Ma = MyClass(da)

Ma.X = 5
Ma.Y = 6
print(Ma.X, Ma.Y)   #outputs 5 6
print(da)           #outputs {'Y': 6, 'X': 5}

现在这个几乎有效,除了对 makeProperty()的调用需要访问全局变量 da ,而不是使用自己的成员数据。

这是我遇到的问题,但我倾向于相信它的可能性,因为第一个代码示例在其getter / setter中访问自己的成员数据。

希望这会让事情变得更加清晰。 谢谢, 罗布。

更多信息:

我认为这个解决方案不起作用,因为看起来makeProperty调用只被调用一次,而不是像我假设的那样每个类实例化。

那么有没有什么方法可以让这项工作成功,或者用户是否会被降级为大量切割+粘贴?

1 个答案:

答案 0 :(得分:4)

给出问题" MyClass的实例每个都有属性y?"的唯一getter / setter函数,字面答案是" no" - 在类上查找描述符(包括property),而不是在实例上查找。

但是,类级描述符可以委托给实例持有的callables,从而产生类似的效果。你的例子对我来说并不完全清楚,但是,例如考虑:

class Whatever(object):

    def __init__(self, scale, getter=lambda x: 42):
        self.scale = scale
        self.getter = getter

    @property
    def y(self):
        return self.getter(self.scale)

plain = Whatever(33)
fancy = Whatever(47, lambda x: x*2)

现在plain.y42,忽略plain.scale,即使这已经改变了;虽然fancy.y最初为94,但fancy.scale更改时更改。那是你想要做的事情吗?看到所需的用例肯定会有所帮助......

补充:鉴于我们在Q的大型编辑中看到了一个简化的用例,以下是我将如何解决它:

class MyHelper:
    def __init__(self, label):
        self.mylabel = label

    def getter2(self, other):
        return other.mydict[self.mylabel]

    def setter2(self, other, value):
        other.mydict[self.mylabel] = value

def makeProperty(label):
    H = MyHelper(label)
    return property(H.getter2, H.setter2)

class MyClass:
    def __init__(self, d):
        self.mydict = d

    X = makeProperty('X')
    Y = makeProperty('Y')

可以通过将makeProperty方法的名称更改为MyHelper__get__来消除工厂函数__set__(即,{{1}本身进入描述符类型)并直接调用MyHelper来构建要分配的实例,但这种稍微不那么直接的方法可能更容易理解,因为它依赖于众所周知的内置类型{{ 1}}。