如果我要创建一个需要存储属性的类,何时使用@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
除了节省时间(在某些情况下)之外还允许我做什么?对于需要一些时间来分配的属性,是否有正确的方式"分配它们?
答案 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