在逐个对象的基础上将属性转换为属性?

时间:2014-09-18 00:49:00

标签: python python-3.x properties

我有一类对象,其中大多数具有这一属性,在95%的情况下可以将其作为简单属性实现。但是,有一些重要的边缘情况,必须根据另一个对象的数据计算该属性。

我希望能够设置myobj.gnarlyattribute = property(lambda self: self.container.x*self.k)

但是,这似乎不起作用:

>>> myfoo=foo()
>>> myfoo.spam
10
>>> import random
>>> myfoo.spam=property(lambda self: random.randint(0,20))
>>> myfoo.spam
<property object at 0x02A57420>
>>> 

我想我可以gnarlyattribute永远是一个通常只有lambda self: self._gnarlyattribute作为吸气剂的财产,但这似乎有点臭。有什么想法吗?

2 个答案:

答案 0 :(得分:1)

正如已经指出的那样,属性只能在类级别工作,并且不能在实例上设置它们。 (好吧,他们可以,但他们不做你想做的事。)

因此,我建议使用类继承来解决您的问题:

class NoProps(object):
    def __init__(self, spam=None):
        if spam is None:
            spam = 0  # Pick a sensible default
        self.spam = spam

class Props(NoProps):
    @property
    def spam(self):
        """Docstring for the spam property"""
        return self._spam

    @spam.setter
    def spam(self, value):
        # Do whatever calculations are needed here
        import random
        self._spam = value + random.randint(0,20)

    @spam.deleter
    def spam(self):
        del self._spam

然后,当您发现某个特定对象需要将其spam属性作为计算属性时,请将该对象设为Props而不是NoProps的实例:

a = NoProps(3)
b = NoProps(4)
c = Props(5)

print a.spam, b.spam, c.spam
# Prints 3, 4, (something between 5 and 25)

如果您可以提前告知您在给定实例中需要计算值,则应该执行您正在寻找的内容。


或者,如果您在创建实例之前无法告诉您需要计算的值,那么这一点也非常简单:只需在您的类中添加一个工厂方法,该方法将复制&#34; old&#34;反对&#34;新&#34;一。例如:

class NoProps(object):
    def __init__(self, spam=None):
        if spam is None:
            spam = 0  # Pick a sensible default
        self.spam = spam

    @classmethod
    def from_other_obj(cls, other_obj):
        """Factory method to copy other_obj's values"""
        # The call to cls() is where the "magic" happens
        obj = cls()
        obj.spam = other_obj.spam
        # Copy any other properties here
        return obj

class Props(NoProps):
    @property
    def spam(self):
        """Docstring for the spam property"""
        return self._spam

    @spam.setter
    def spam(self, value):
        # Do whatever calculations are needed here
        import random
        self._spam = value + random.randint(0,20)

    @spam.deleter
    def spam(self):
        del self._spam

因为我们在工厂方法中调用cls(),所以它将创建一个调用它的类的实例。因此,以下是可能的:

a = NoProps(3)
b = NoProps.from_other_obj(a)
c = NoProps.from_other_obj(b)
print(a.spam, b.spam, c.spam)
# Prints 3, 3, 3

# I just discovered that c.spam should be calculated
# So convert it into a Props object
c = Props.from_other_obj(c)
print(a.spam, b.spam, c.spam)
# Prints 3, 3, (something between 3 and 23)

这两种解决方案中的一种或另一种应该是您正在寻找的。

答案 1 :(得分:0)

The magic to make properties work仅存在于班级。没有办法让属性按对象工作。