使用@property

时间:2016-04-18 00:43:42

标签: python class attributes

如果我要创建一个需要存储属性的类,何时使用@property装饰器是合适的,何时应该在__init__中定义它们?

我能想到的原因:

说我有一个像

这样的课程
class Apple:
    def __init__(self):
        self.foodType = "fruit"
        self.edible = True
        self.color = "red"

这很好用。在这种情况下,我很清楚我不应该把课程写成:

class Apple:
    @property
    def foodType(self):
        return "fruit"
    @property
    def edible(self):
        return True
    @property
    def color(self):
        return "red"

但是说我有一个更复杂的类,它有更慢的方法(比如通过互联网获取数据)。

我可以在__init__中实现此分配属性:

class Apple:
    def __init__(self):
        self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"
        self.wikipedia_article_content = requests.get(self.wikipedia_url).text

或者我可以使用@property实现此目的:

class Apple:
    def __init__(self):
        self.wikipedia_url = "https://en.wikipedia.org/wiki/Apple"

    @property
    def wikipedia_article_content(self):
        return requests.get(self.wikipedia_url).text

在这种情况下,后者实例化的速度要快约50,000倍。但是,我可以说,如果我多次提取wikipedia_article_content,前者会更快:

a = Apple()
a.wikipedia_article_content
a.wikipedia_article_content
a.wikipedia_article_content

在这种情况下,前者的速度要快3倍,因为它的请求数量只有三分之一。

我的问题

这两种方式分配属性的唯一区别是我所想到的吗? @property除了节省时间(在某些情况下)之外还允许我做什么?对于需要一些时间来分配的属性,是否有正确的方式"分配它们?

2 个答案:

答案 0 :(得分:2)

使用属性可以实现更复杂的行为。例如,只有在文章内容发生变化且仅在经过一段时间后才获取文章内容。

答案 1 :(得分:1)

是的,我建议使用property作为参数。如果你想使它变得懒惰或缓存,你可以继承属性。

这只是一个懒惰属性的实现。它在属性中执行一些操作并返回结果。此结果将保存在具有其他名称的类中,并且属性上的每个后续调用都将返回保存的结果。

class LazyProperty(property):
    def __init__(self, *args, **kwargs):
        # Let property set everything up
        super(LazyProperty, self).__init__(*args, **kwargs)
        # We need a name to save the cached result. If the property is called
        # "test" save the result as "_test".
        self._key = '_{0}'.format(self.fget.__name__)


    def __get__(self, obj, owner=None):
        # Called on the class not the instance
        if obj is None:
            return self

        # Value is already fetched so just return the stored value
        elif self._key in obj.__dict__:
            return obj.__dict__[self._key]

        # Value is not fetched, so fetch, save and return it
        else:
            val = self.fget(obj)
            obj.__dict__[self._key] = val
            return val

这允许您计算一次值,然后始终返回它:

class Test:
    def __init__(self):
        pass

    @LazyProperty
    def test(self):
        print('Doing some very slow stuff.')
        return 100

这就是它的工作原理(显然你需要根据你的情况调整它):

>>> a = Test()
>>> a._test   # The property hasn't been called so there is no result saved yet.
AttributeError: 'Test' object has no attribute '_test'
>>> a.test    # First property access will evaluate the code you have in your property
Doing some very slow stuff.
100
>>> a.test    # Accessing the property again will give you the saved result
100
>>> a._test   # Or access the saved result directly
100