473,396 Members | 1,996 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

Delayed evaluation and setdefault()

Hi all,

I have a question about Python and delayed evaluation.

Short-circuiting of Boolean expressions implies that in:
if a() and b():
any possible side-effects the call to b() might have will not
happen of a() returns true, because then b() will never be
executed.

However, if I go looking for dictionary keys like this:
d = {}
d.setdefault('foo', b())


then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

If b() has side-effects, this may not be what you want at all. It
would seem to me not too exotic to expect Python to have some
sort of construct or syntax for dealing with this, just as it
supports Boolean short-circuiting.

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?

--
Leo Breebaart <le*@lspace.org>
Jul 18 '05 #1
7 2822
Leo Breebaart wrote:
Hi all,

I have a question about Python and delayed evaluation.

Short-circuiting of Boolean expressions implies that in:
>>> if a() and b():
any possible side-effects the call to b() might have will not
happen of a() returns true, because then b() will never be
executed.

However, if I go looking for dictionary keys like this:
>>> d = {}
>>> d.setdefault('foo', b())


then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

If b() has side-effects, this may not be what you want at all. It
would seem to me not too exotic to expect Python to have some
sort of construct or syntax for dealing with this, just as it
supports Boolean short-circuiting.

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


It's normal---setdefault is just a method and its parameters get
evaluted before the call occur. If you want this kind of lazy
evaluation, I'd suggest something like this ('foo' in d) or
(d.setdefault('foo', b()). If you want your value too, it gets even
trickier.

Actually, if you really need this kind of functionality, it might be
worth defining a function.

def lazysetdefault(d, key, func):
if key not in d:
d[key] = func()()
return d[key]

lazysetdefault(d, 'foo', lambda: b())

However, are you real sure you need it?

regards,
anton.

Jul 18 '05 #2
Leo Breebaart wrote:
>>> d.setdefault('foo', b()) then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


def lazysetdefault(dict, key, ref):
if not dict.has_key(key):
dict[key] = ref()
return dict[key]
d = {}
lazysetdefault(d, 'foo', b)


-Peter
Jul 18 '05 #3
Leo Breebaart wrote:
So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


The setdefault method of dictionaries is just a normal method in Python;
only the builtin `and' and `or' operators have special short-circuiting
behavior. That is to say, there are no builtin functions or methods in
Python which are "special forms."

It's true that the kind of thing you want to do (lazy evaluation in some
incidental context) is common, but it would not be a good idea to start
selectively adding special forms to the language since it would greatly
complicate things. Right now if I see

this.that(something, theOther())

I can immediately tell what gets evaluated or called and when. I don't
need to know that there are special cases in some things that look like
method/function calls but actually might be special forms, and that's a
good thing.

As for your particular solution, you're much better off implementing the
lazy evaluation you want yourself, rather than wishing there were
special language support for it for that particular method (or a very
small subset of methods/functions).

--
__ Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
/ \ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
\__/ The critical period in matrimony is breakfast-time.
-- A.P. Herbert
Jul 18 '05 #4
Leo Breebaart wrote:
Hi all,

I have a question about Python and delayed evaluation.

Short-circuiting of Boolean expressions implies that in:
>>> if a() and b():
any possible side-effects the call to b() might have will not
happen of a() returns true, because then b() will never be
executed.

However, if I go looking for dictionary keys like this:
>>> d = {}
>>> d.setdefault('foo', b())


then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

If b() has side-effects, this may not be what you want at all. It
would seem to me not too exotic to expect Python to have some
sort of construct or syntax for dealing with this, just as it
supports Boolean short-circuiting.

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


Lazy evaluation is dangerous as the state of mutable objects may have
changed in the mean time. In your case

if "foo" not in d:
d["foo"] = b()

should do.

However, if you absolutely want it, here's a slightly more general approach:

<defer.py>
class Defer:
def __init__(self, fun, *args):
self.fun = fun
self.args = args
def __call__(self):
""" Calculate a deferred function the first time it is
invoked, return the stored result for subsequent calls
"""
try:
return self.value
except AttributeError:
self.value = self.fun(*self.args)
return self.value

def defer(fun, *args):
""" Use, e. g., defer(average, 1, 2) to delay the calculation
of the average until the first time undefer() is called
with the Defer instance.
"""
return Defer(fun, *args)

def undefer(value):
""" Check if value is deferred, if so calculate it, otherwise
return it unchanged
"""
if isinstance(value, Defer):
return value()
return value

#example usage:

class Dict(dict):
def setdefault(self, key, value):
try:
return self[key]
except KeyError:
# it might make sense to further delay the
# calculation until __getitem__()
self[key] = value = undefer(value)
return value

def b(value):
print "side effect for", value
return value
dv = defer(b, "used three times")

d = Dict()
d.setdefault("x", defer(b, "a1"))
d.setdefault("x", defer(b, "a2"))
d.setdefault("y", "b")
d.setdefault("a", dv)
d.setdefault("b", dv)
d.setdefault("c", dv)
print ",\n".join(repr(d).split(","))
assert d["a"] is d["b"]

</defer.py>

You will still have to bother with undeferring in *many* places in your
program, so the above would go into the "or other" category. For lazy
evaluation to take off, I think it has to be build into the language. I
doubt, though, that much effort will be saved, as a human is normally lazy
enough to not calculate things until absolutely needed.

Peter

Jul 18 '05 #5
In article <40***************@engcorp.com>,
Peter Hansen <pe***@engcorp.com> wrote:
Leo Breebaart wrote:
>>> d.setdefault('foo', b())

then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


def lazysetdefault(dict, key, ref):
if not dict.has_key(key):
dict[key] = ref()
return dict[key]


First of all, Peter made a boo-boo in naming a parameter ``dict``.

If the key will be in the dict more than 90% (*very* roughly) of the time
you call this, you can do this instead:

def lazysetdefault(d, key, ref):
try:
return d[key]
except KeyError:
d[key] = ref()
return d[key]

That minimizes the bytecode and function calls.
--
Aahz (aa**@pythoncraft.com) <*> http://www.pythoncraft.com/

A: No.
Q: Is top-posting okay?
Jul 18 '05 #6
Aahz wrote:

In article <40***************@engcorp.com>,
Peter Hansen <pe***@engcorp.com> wrote:
Leo Breebaart wrote:

>>> d.setdefault('foo', b())
then b() *does* get executed regardless of whether or not the
value it returns is actually used ('foo' was not found) or not
('foo' was found).

So I guess my question is twofold: one, do people think
differently, and if so why?; and two: is there any elegant (or
other) way in which I can achieve the delayed evaluation I desire
for setdefault, given a side-effect-having b()?


def lazysetdefault(dict, key, ref):
if not dict.has_key(key):
dict[key] = ref()
return dict[key]


First of all, Peter made a boo-boo in naming a parameter ``dict``.


Oops... I was, uh, writing pseudo-code, not Python! Yeah, that's it...

-but-at-least-it-would-have-executed-ly yr's,
Peter
Jul 18 '05 #7
In article <40***************@engcorp.com>,
Peter Hansen <pe***@engcorp.com> wrote:
Aahz wrote:

First of all, Peter made a boo-boo in naming a parameter ``dict``.


Oops... I was, uh, writing pseudo-code, not Python! Yeah, that's it...


Python *is* pseudo-code. That's why we like it, right?
--
Aahz (aa**@pythoncraft.com) <*> http://www.pythoncraft.com/

A: No.
Q: Is top-posting okay?
Jul 18 '05 #8

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

5
by: Tino Lange | last post by:
Hi! I just realized that <dict>.setdefault *always* executes the second argument - even if it's not necessary, because the requested item in the first argument exists. This is not what I...
0
by: Cole Tuininga | last post by:
Quick question for y'all - is a dict's setdefault call threadsafe? In other words, if I have code like the following: tmp = someObj() result = dictname.setdefault( key, tmp ) is it...
13
by: Brian | last post by:
Hi all... This question is more for the GURUs out there. It is not a question on how to do something, but why it happens, and I am trying to figure out if there is a pattern. I am using IE, but...
0
by: subi | last post by:
Hi, I don't know where's the best place to post my question. I hope it suits this group. I have created an assembly with a delay sign attribute set to true in the AssemblyInfo.cs. And the key...
9
by: Ron Garret | last post by:
Is this a bug or a feature? class mydict(dict): def __setitem__(self, key, val): print 'foo' dict.__setitem__(self, key, val) >>> d=mydict() >>> d=2 foo
54
by: Rasjid | last post by:
Hello, I have just joined and this is my first post. I have never been able to resolve the issue of order of evaluation in C/C++ and the related issue of precedence of operators, use of...
8
by: Grorange | last post by:
Writing ASP.NET pages, I have a need for client-side functions to prevent going to the server for every small change. I have a list of server side created checkboxes filled with a lot of...
39
by: Boltar | last post by:
Why does C do lazy evaluation for logical boolean operations but not bitwise ones? Ie: the following program prints "1 2" , not "1 1" under gcc main() { int a = 1; int b = 1; 0 && ++a;
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.