Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #33403 > unrolled thread
| Started by | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| First post | 2012-11-15 15:46 -0700 |
| Last post | 2012-11-16 13:46 +0100 |
| Articles | 5 — 4 participants |
Back to article view | Back to comp.lang.python
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: Lazy Attribute Ian Kelly <ian.g.kelly@gmail.com> - 2012-11-15 15:46 -0700
Re: Lazy Attribute Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-11-16 10:29 +0000
Re: Lazy Attribute "Stefan H. Holek" <stefan@epy.co.at> - 2012-11-16 11:45 +0100
RE: Lazy Attribute Andriy Kornatskyy <andriy.kornatskyy@live.com> - 2012-11-16 13:46 +0300
Re: Lazy Attribute "Stefan H. Holek" <stefan@epy.co.at> - 2012-11-16 13:46 +0100
| From | Ian Kelly <ian.g.kelly@gmail.com> |
|---|---|
| Date | 2012-11-15 15:46 -0700 |
| Subject | Re: Lazy Attribute |
| Message-ID | <mailman.3729.1353019611.27098.python-list@python.org> |
On Thu, Nov 15, 2012 at 12:33 PM, Andriy Kornatskyy
<andriy.kornatskyy@live.com> wrote:
>
> A lazy attribute is an attribute that is calculated on demand and only once.
>
> The post below shows how you can use lazy attribute in your Python class:
>
> http://mindref.blogspot.com/2012/11/python-lazy-attribute.html
>
> Comments or suggestions are welcome.
I should add that I like the approach you're taking here. Usually
when I want a lazy property I just make an ordinary property of a
memoized function call:
def memoize(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
kwset = frozenset(kwargs.items())
try:
return cache[args, kwset]
except KeyError:
result = cache[args, kwset] = func(*args, **kwargs)
return result
return wrapper
class Foo:
def __init__(self):
self.times_called = 0
@property
@memoize # Alternatively, use functools.lru_cache
def forty_two(self):
self.times_called += 1
return 6 * 9
>>> foo = Foo()
>>> foo.times_called
0
>>> foo.forty_two
54
>>> foo.times_called
1
>>> foo.forty_two
54
>>> foo.times_called
1
Although you don't go into it in the blog entry, what I like about
your approach of replacing the descriptor with an attribute is that,
in addition to being faster, it makes it easy to force the object to
lazily reevaluate the attribute, just by deleting it. Using the
Person example from your blog post:
>>> p = Person('John', 'Smith')
>>> p.display_name
'John Smith'
>>> p.display_name
'John Smith'
>>> p.calls_count
1
>>> p.first_name = 'Eliza'
>>> del p.display_name
>>> p.display_name
'Eliza Smith'
>>> p.calls_count
2
Although in general it's probably better to use some form of reactive
programming for that.
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve+comp.lang.python@pearwood.info> |
|---|---|
| Date | 2012-11-16 10:29 +0000 |
| Message-ID | <50a6156e$0$29978$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #33403 |
On Thu, 15 Nov 2012 15:46:19 -0700, Ian Kelly wrote: > Although you don't go into it in the blog entry, what I like about your > approach of replacing the descriptor with an attribute is that, in > addition to being faster, it makes it easy to force the object to lazily > reevaluate the attribute, just by deleting it. You just lost me right there. That's a poor UI design -- it violates the principle of least surprise. If I delete something, it should be deleted. Consider your example: >>>> del p.display_name >>>> p.display_name > 'Eliza Smith' That's very surprising. I am not aware of any other name in Python where deleting it does not remove the name from the namespace. (It is possible with properties, but I haven't ever come across someone who does that.) I don't have a good solution for invaliding such lazy attributes. Ideally we could have a new statement: refresh obj.attr # or some other name like "invalidate" but that won't happen. Other alternatives like: obj.attr.refresh() refresh(obj.attr) can't work because the function will see the result of the attribute lookup, not the lazy attribute itself. This won't do: obj.__class__.attr.refresh() because it won't know which instance to invalidate, although this could work: obj.__class__.attr.refresh(obj) # but it's ugly I'm very vaguely leaning towards this as the least-worst solution to invalidating the cached value: refresh(obj, 'attr') # pass the instance and the name -- Steven
[toc] | [prev] | [next] | [standalone]
| From | "Stefan H. Holek" <stefan@epy.co.at> |
|---|---|
| Date | 2012-11-16 11:45 +0100 |
| Message-ID | <mailman.3745.1353062733.27098.python-list@python.org> |
| In reply to | #33425 |
On 16.11.2012, at 11:29, Steven D'Aprano wrote: > I'm very vaguely leaning towards this as the least-worst solution to > invalidating the cached value: > > refresh(obj, 'attr') # pass the instance and the name This it exactly how lazy handles invalidation. http://lazy.readthedocs.org/en/latest/ Stefan -- Stefan H. Holek stefan@epy.co.at
[toc] | [prev] | [next] | [standalone]
| From | Andriy Kornatskyy <andriy.kornatskyy@live.com> |
|---|---|
| Date | 2012-11-16 13:46 +0300 |
| Message-ID | <mailman.3746.1353062869.27098.python-list@python.org> |
| In reply to | #33425 |
I believe it is not valid relate a lazy attribute as something `cached` since it cause confusion (e.g. delete of attribute cause cached item to be re-evaluated...), `cached` and `lazy` have completely different semantic meaning... however might overlap, as we see. Andriy ---------------------------------------- > From: steve+comp.lang.python@pearwood.info > Subject: Re: Lazy Attribute > Date: Fri, 16 Nov 2012 10:29:03 +0000 > To: python-list@python.org > > On Thu, 15 Nov 2012 15:46:19 -0700, Ian Kelly wrote: > > > Although you don't go into it in the blog entry, what I like about your > > approach of replacing the descriptor with an attribute is that, in > > addition to being faster, it makes it easy to force the object to lazily > > reevaluate the attribute, just by deleting it. > > You just lost me right there. That's a poor UI design -- it violates the > principle of least surprise. If I delete something, it should be deleted. > Consider your example: > > >>>> del p.display_name > >>>> p.display_name > > 'Eliza Smith' > > That's very surprising. I am not aware of any other name in Python where > deleting it does not remove the name from the namespace. (It is possible > with properties, but I haven't ever come across someone who does that.) > > I don't have a good solution for invaliding such lazy attributes. Ideally > we could have a new statement: > > refresh obj.attr # or some other name like "invalidate" > > but that won't happen. Other alternatives like: > > obj.attr.refresh() > refresh(obj.attr) > > can't work because the function will see the result of the attribute > lookup, not the lazy attribute itself. This won't do: > > obj.__class__.attr.refresh() > > because it won't know which instance to invalidate, although this could > work: > > obj.__class__.attr.refresh(obj) # but it's ugly > > I'm very vaguely leaning towards this as the least-worst solution to > invalidating the cached value: > > refresh(obj, 'attr') # pass the instance and the name > > > -- > Steven > -- > http://mail.python.org/mailman/listinfo/python-list
[toc] | [prev] | [next] | [standalone]
| From | "Stefan H. Holek" <stefan@epy.co.at> |
|---|---|
| Date | 2012-11-16 13:46 +0100 |
| Message-ID | <mailman.3748.1353069963.27098.python-list@python.org> |
| In reply to | #33425 |
On 16.11.2012, at 11:54, Andriy Kornatskyy wrote: >> Subject: Re: Lazy Attribute >> From: stefan@epy.co.at >> Date: Fri, 16 Nov 2012 11:45:32 +0100 >> To: python-list@python.org >> >> On 16.11.2012, at 11:29, Steven D'Aprano wrote: >> >>> I'm very vaguely leaning towards this as the least-worst solution to >>> invalidating the cached value: >>> >>> refresh(obj, 'attr') # pass the instance and the name >> >> This it exactly how lazy handles invalidation. http://lazy.readthedocs.org/en/latest/ > > @property is a solution to evaluate something that is dynamic. @attribute is good for immutable objects. Even if we assume refresh is a good idea... how I would know when it is valid to `refresh`? What is criteria? > > Andriy I had to implement invalidation anyway in order to write tests. I decided to expose the mechanism to keep users from having to invent their own SHOULD the need arise. I was not advocating invalidation in any way with my reply. All I wanted was to confirm the "least bad" solution. ;-) Stefan -- Stefan H. Holek stefan@epy.co.at
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web