Re: dict.get(key, default) evaluates default even if key exists

2020-12-20 Thread Chris Angelico
On Mon, Dec 21, 2020 at 12:47 AM <2qdxy4rzwzuui...@potatochowder.com> wrote:
>
> On 2020-12-19 at 22:29:34 +0100,
> Roel Schroeven  wrote:
>
> > What could be useful in some use cases, I think, is a wrapper function
> > that evaluates the function lazily:
> >
> > def dict_get_lazily(d, key, fnc, *args, **kwargs):
> > try:
> > return d[key]
> > except KeyError:
> > return fnc(*args, **kwargs)
> >
> > some_var = dict_get_lazily(d, 'spam', some_function, 31, 11)
>
> There's no need to pass all those arguments through dict_get_lazily, and
> the language already has a way to evaluate an expression lazily:
>
> def dict_get_lazily(d, key, f):
> try:
> return d[key]
> except KeyError:
> return f()
>
> some_var = dict_get_lazily(d, 'spam', lambda: some_function(31, 11))
>

class LazyDict(dict):
def __missing__(self, key):
# Pretend this is slow
print("slow function called")
return 31 + 11

d = LazyDict({
'spam': 0,
'ham': 1,
'parrot': 2,
})
print(d['spam'])
print(d['eggs'])

Guaranteed to be called when and only when the key is missing. This
does imply a variant architecture in which the data structure itself
is aware of the calculations necessary to generate the data, so it may
not be appropriate to all situations, but it's clean, easy, and
efficient.

If you can't do this, I'd recommend using the try/except method, maybe
wrapped up in a function if you want to. In the case where the key is
found, the cost is one try block and one lookup - barely more than any
other option (for the record, a try block is pretty cheap in CPython,
and it's only costly when you hit an exception); and where it isn't
found, it's two lookups plus the cost of generating the default (and
we've already established that generating the default is expensive,
otherwise setdefault would be appropriate).

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-20 Thread 2QdxY4RzWzUUiLuE
On 2020-12-19 at 22:29:34 +0100,
Roel Schroeven  wrote:

> What could be useful in some use cases, I think, is a wrapper function
> that evaluates the function lazily:
> 
> def dict_get_lazily(d, key, fnc, *args, **kwargs):
> try:
> return d[key]
> except KeyError:
> return fnc(*args, **kwargs)
> 
> some_var = dict_get_lazily(d, 'spam', some_function, 31, 11)

There's no need to pass all those arguments through dict_get_lazily, and
the language already has a way to evaluate an expression lazily:

def dict_get_lazily(d, key, f):
try:
return d[key]
except KeyError:
return f()

some_var = dict_get_lazily(d, 'spam', lambda: some_function(31, 11))

So, beyond a cache (of which there are many variations and many
implementations for many use cases), what are the use cases for an
entire dictionary of lazily computed values?  And if my application has
to know when to access d['key'] directly and when to call
dict_get_lazily(d, 'key', f), then whom am I fooling by "hiding" the
lazy evaluation?  But if I always call dict_get_lazily when I access d,
then why do those two or three lines belong in the standard library
instead of my application or my private toolbox?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-20 Thread Roel Schroeven

Ethan Furman schreef op 16/12/2020 om 17:59:

Or, if the computationally massively expensive call uses potentially
different arguments for each invocation:

  some_var = d.get('a') or cme(arg1, arg2, ...)


I don't like that because it is very fragile: the expression will 
evaluate to the second part not only when 'a' is not in the dictionary 
but also whenever the value corresponding to key 'a' is false-y.


Example:

def slow_function(a, b):
# Pretend this is slow, or there is any other reason for this
# function to be called only when strictly necessary.
print('slow_function called')
return a + b

d = {
'spam': 0,
'ham': 1,
'parrot': 2,
}

some_var = d.get('spam') or slow_function(31, 11)
print(some_var)

Result: slow_function is called, and some_var equals 42 instead of 0.

You could make it work reliable by using a unique sentinel value and the 
walrus operator:


SENTINEL = object()
some_var = (
r
if (r := d.get('spam', SENTINEL)) is not SENTINEL
else slow_function(31, 11)
)

But I don't like that either because it's way too complicated.

What could be useful in some use cases, I think, is a wrapper function 
that evaluates the function lazily:


def dict_get_lazily(d, key, fnc, *args, **kwargs):
try:
return d[key]
except KeyError:
return fnc(*args, **kwargs)

some_var = dict_get_lazily(d, 'spam', some_function, 31, 11)



--
"Honest criticism is hard to take, particularly from a relative, a
friend, an acquaintance, or a stranger."
-- Franklin P. Jones

Roel Schroeven

--
https://mail.python.org/mailman/listinfo/python-list


RE: dict.get(key, default) evaluates default even if key exists

2020-12-18 Thread Schachner, Joseph
Yes.  In order to call D.get( ) it needs to pass two arguments.  The first is 
'a', simple.  The second is the result of a call to get_default().   So, that 
is called.  From INSIDE get_default() it prints 'Nobody expects this' but you 
should expect it,  get_default() gets executed.   Following that it prints '1', 
because the default value was NOT USED.   If it was used, you would see 'Nobody 
expects this' followed by 0.

--- Joseph S.

-Original Message-
From: Mark Polesky  
Sent: Tuesday, December 15, 2020 12:07 PM
To: python-list@python.org
Subject: dict.get(key, default) evaluates default even if key exists

Hi.

# Running this script

D = {'a':1}
def get_default():
    print('Nobody expects this')
    return 0
print(D.get('a', get_default()))

# ...generates this output:

Nobody expects this
1

###

Since I'm brand new to this community, I thought I'd ask here first... Is this 
worthy of a bug report?  This behavior is definitely unexpected to me, and I 
accidentally coded an endless loop in a mutual recursion situation because of 
it.  Calling dict.get.__doc__ only gives this short sentence: Return the value 
for key if key is in the dictionary, else default.  Nothing in that docstring 
suggests that the default value is evaluated even if the key exists, and I 
can't think of any good reason to do so.

Am I missing something?

Thanks,
Mark

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread jak

Il 17/12/2020 13:33, Chris Angelico ha scritto:

On Thu, Dec 17, 2020 at 11:16 PM jak  wrote:


Il 17/12/2020 12:40, Peter J. Holzer ha scritto:

On 2020-12-17 12:16:29 +0100, jak wrote:

print(_ if d.get('a', None) is not None else get_default())


That doesn't work:


print(_ if d.get('a', None) is not None else get_default())

Traceback (most recent call last):
File "", line 1, in 
NameError: name '_' is not defined

But this works:


print(_ if (_ := d.get('a', None)) is not None else get_default())

1

(I would prefer ChrisA's solution, though.)

  hp


this one?



D['a'] if 'a' in D else get_default()

ChrisA



This solution search two times same key.


Yes, it does, but hash lookups are pretty fast. Unless your key is
some sort of custom object with a very expensive __hash__ function,
the double lookup isn't going to be too costly. But if that does
bother you, you can write it as a try/except instead.

ChrisA



try/except is a very expensive time. if we have a small dictionary in 
which you do little research, you definitely have reason. I would be 
curious to see the differences by taking times with 50 / 60K records by 
repeating the search 1 or 2 million times with and without try/except 
and also searching 1 or 2 times too.


Cheers
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread jak

Il 17/12/2020 12:40, Peter J. Holzer ha scritto:

On 2020-12-17 12:16:29 +0100, jak wrote:

print(_ if d.get('a', None) is not None else get_default())


That doesn't work:


print(_ if d.get('a', None) is not None else get_default())

Traceback (most recent call last):
   File "", line 1, in 
NameError: name '_' is not defined

But this works:


print(_ if (_ := d.get('a', None)) is not None else get_default())

1

(I would prefer ChrisA's solution, though.)

 hp



I had already corrected my oversight (jak 12:32):

> print((_ if d.get('a', None) is not None else get_default()))

while your fix works but not correctly because in that way does not 
collect the return code of the function get_default().

--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread Chris Angelico
On Thu, Dec 17, 2020 at 11:16 PM jak  wrote:
>
> Il 17/12/2020 12:40, Peter J. Holzer ha scritto:
> > On 2020-12-17 12:16:29 +0100, jak wrote:
> >> print(_ if d.get('a', None) is not None else get_default())
> >
> > That doesn't work:
> >
>  print(_ if d.get('a', None) is not None else get_default())
> > Traceback (most recent call last):
> >File "", line 1, in 
> > NameError: name '_' is not defined
> >
> > But this works:
> >
>  print(_ if (_ := d.get('a', None)) is not None else get_default())
> > 1
> >
> > (I would prefer ChrisA's solution, though.)
> >
> >  hp
> >
> this one?
>
> 
>
> D['a'] if 'a' in D else get_default()
>
> ChrisA
>
> 
>
> This solution search two times same key.

Yes, it does, but hash lookups are pretty fast. Unless your key is
some sort of custom object with a very expensive __hash__ function,
the double lookup isn't going to be too costly. But if that does
bother you, you can write it as a try/except instead.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread jak

Il 17/12/2020 12:40, Peter J. Holzer ha scritto:

On 2020-12-17 12:16:29 +0100, jak wrote:

print(_ if d.get('a', None) is not None else get_default())


That doesn't work:


print(_ if d.get('a', None) is not None else get_default())

Traceback (most recent call last):
   File "", line 1, in 
NameError: name '_' is not defined

But this works:


print(_ if (_ := d.get('a', None)) is not None else get_default())

1

(I would prefer ChrisA's solution, though.)

 hp


this one?



D['a'] if 'a' in D else get_default()

ChrisA



This solution search two times same key.
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread Peter J. Holzer
On 2020-12-17 12:16:29 +0100, jak wrote:
> print(_ if d.get('a', None) is not None else get_default())

That doesn't work:

>>> print(_ if d.get('a', None) is not None else get_default())
Traceback (most recent call last):
  File "", line 1, in 
NameError: name '_' is not defined

But this works:

>>> print(_ if (_ := d.get('a', None)) is not None else get_default())
1

(I would prefer ChrisA's solution, though.)

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | h...@hjp.at |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread jak

Il 17/12/2020 12:16, jak ha scritto:

Il 15/12/2020 18:07, Mark Polesky ha scritto:

Hi.

# Running this script

D = {'a':1}
def get_default():
 print('Nobody expects this')
 return 0
print(D.get('a', get_default()))

# ...generates this output:

Nobody expects this
1

###

Since I'm brand new to this community, I thought I'd ask here first... 
Is this worthy of a bug report?  This behavior is definitely 
unexpected to me, and I accidentally coded an endless loop in a mutual 
recursion situation because of it.  Calling dict.get.__doc__ only 
gives this short sentence: Return the value for key if key is in the 
dictionary, else default.  Nothing in that docstring suggests that the 
default value is evaluated even if the key exists, and I can't think 
of any good reason to do so.


Am I missing something?

Thanks,
Mark



print(_ if d.get('a', None) is not None else get_default())




ops...

print((_ if d.get('a', None) is not None else get_default()))

--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread jak

Il 15/12/2020 18:07, Mark Polesky ha scritto:

Hi.

# Running this script

D = {'a':1}
def get_default():
     print('Nobody expects this')
     return 0
print(D.get('a', get_default()))

# ...generates this output:

Nobody expects this
1

###

Since I'm brand new to this community, I thought I'd ask here first... Is this 
worthy of a bug report?  This behavior is definitely unexpected to me, and I 
accidentally coded an endless loop in a mutual recursion situation because of 
it.  Calling dict.get.__doc__ only gives this short sentence: Return the value 
for key if key is in the dictionary, else default.  Nothing in that docstring 
suggests that the default value is evaluated even if the key exists, and I 
can't think of any good reason to do so.

Am I missing something?

Thanks,
Mark



print(_ if d.get('a', None) is not None else get_default())



--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-17 Thread Greg Ewing

On 17/12/20 8:31 am, Grant Edwards wrote:

On 2020-12-16, Dennis Lee Bieber  wrote:

https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name


I vaguely recall some other old
language I ran across in a survey course when in college that used it.
IIRC it was Algol.


Algol 60.

Also, Lisp macros effectively let you do something similar.

--
Greg
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Grant Edwards
On 2020-12-16, Dennis Lee Bieber  wrote:
> On Tue, 15 Dec 2020 20:08:53 + (UTC), Mark Polesky via Python-list
> declaimed the following:
>
>>behavior, and I can't remember any programming language in which it's
>>different.
>
> https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_name 

Most of us only run across call-by-name when using macro processors
(cpp, m4, TeX, various assemblers). I vaguely recall some other old
language I ran across in a survey course when in college that used it.
IIRC it was Algol. Somebody else already mentioned Haskel.

--
Grant







-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Ethan Furman

On 12/16/20 3:08 AM, Peter J. Holzer wrote:

On 2020-12-15 13:07:25 -0800, Ethan Furman wrote:

On 12/15/20 9:07 AM, Mark Polesky via Python-list wrote:


D = {'a':1}

def get_default():
  print('Nobody expects this')
  return 0

print(D.get('a', get_default()))


Python has short-circuiting logical operations, so one way to get the behavior 
you're looking for is:

 D.get('a') or get_default()


That will call get_default for any falsey value (0, "", an empty list ...),
not just for missing values.

Which may or may not be what the OP wants.


Good point, thanks for clarifying.

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Ethan Furman

On 12/16/20 1:44 AM, Chris Angelico wrote:

On Wed, Dec 16, 2020 at 8:43 PM Loris Bennett
 wrote:


Paul Bryan  writes:


On Wed, 2020-12-16 at 10:01 +0100, Loris Bennett wrote:


OK, I get the point about when the default value is generated and
that
potentially being surprising, but in the example originally given,
the
key 'a' exists and has a value of '1', so the default value is not
needed.


But the function is still called. The get method just doesn't use (or
return) the value it generates because the key exists. Nevertheless,
you're passing the return value of the get_default function as an
argument.


Thus, I am still unsurprised when dict.get returns the value of an
existing key.


As am I.


What am I missing?


You'll need to tell me at this point.


I was just slow and confused, but you helped me get there in the end.  I
now realise that issue is not about the result of the dict.get(), but
rather about the fact that the method which generates the default value
is called, whether or not it is needed.

I shall remember that next time I think it might be a good idea to
define a computationally massively expensive value as a rarely needed
default :-)



Yep! That's what defaultdict can do - or if you need more flexibility,
subclass dict and add a __missing__ method.


Or, if the computationally massively expensive call uses potentially
different arguments for each invocation:

some_var = d.get('a') or cme(arg1, arg2, ...)

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Chris Angelico
On Wed, Dec 16, 2020 at 10:17 PM Peter J. Holzer  wrote:
>
> On 2020-12-15 13:07:25 -0800, Ethan Furman wrote:
> > On 12/15/20 9:07 AM, Mark Polesky via Python-list wrote:
> >
> > > D = {'a':1}
> > >
> > > def get_default():
> > >  print('Nobody expects this')
> > >  return 0
> > >
> > > print(D.get('a', get_default()))
> >
> > Python has short-circuiting logical operations, so one way to get the 
> > behavior you're looking for is:
> >
> > D.get('a') or get_default()
>
> That will call get_default for any falsey value (0, "", an empty list ...),
> not just for missing values.
>
> Which may or may not be what the OP wants.
>

D['a'] if 'a' in D else get_default()

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Peter J. Holzer
On 2020-12-15 13:07:25 -0800, Ethan Furman wrote:
> On 12/15/20 9:07 AM, Mark Polesky via Python-list wrote:
> 
> > D = {'a':1}
> >
> > def get_default():
> >  print('Nobody expects this')
> >  return 0
> >
> > print(D.get('a', get_default()))
> 
> Python has short-circuiting logical operations, so one way to get the 
> behavior you're looking for is:
> 
> D.get('a') or get_default()

That will call get_default for any falsey value (0, "", an empty list ...),
not just for missing values.

Which may or may not be what the OP wants.

hp

-- 
   _  | Peter J. Holzer| Story must make more sense than reality.
|_|_) ||
| |   | h...@hjp.at |-- Charles Stross, "Creative writing
__/   | http://www.hjp.at/ |   challenge!"


signature.asc
Description: PGP signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Loris Bennett
Paul Bryan  writes:

> Maybe this will help:
>
 def get(key, default):
> ...   print("entering get")
> ...   print(f"{key=} {default=}")
> ...   print("exiting get")
> ... 
 def generate_default():
> ...   print("entering generate_default")
> ...   print("exiting generate_default")
> ...   return 1
> ... 
 get("a", generate_default())
> entering generate_default
> exiting generate_default
> entering get
> key='a' default=1
> exiting get
 

OK, so it even has nothing to do with dict.get().  It is just about the
way function arguments are evaluated.  Your example above illustrates
nicely that Python does what I would expect, although from the examples
others have given, I might have to adjust my expectations when using
other languages.

Cheers,

Loris

-- 
This signature is currently under construction.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Chris Angelico
On Wed, Dec 16, 2020 at 8:43 PM Loris Bennett
 wrote:
>
> Paul Bryan  writes:
>
> > On Wed, 2020-12-16 at 10:01 +0100, Loris Bennett wrote:
> >
> >> OK, I get the point about when the default value is generated and
> >> that
> >> potentially being surprising, but in the example originally given,
> >> the
> >> key 'a' exists and has a value of '1', so the default value is not
> >> needed.
> >
> > But the function is still called. The get method just doesn't use (or
> > return) the value it generates because the key exists. Nevertheless,
> > you're passing the return value of the get_default function as an
> > argument.
> >
> >> Thus, I am still unsurprised when dict.get returns the value of an
> >> existing key.
> >
> > As am I.
> >
> >> What am I missing?
> >
> > You'll need to tell me at this point.
>
> I was just slow and confused, but you helped me get there in the end.  I
> now realise that issue is not about the result of the dict.get(), but
> rather about the fact that the method which generates the default value
> is called, whether or not it is needed.
>
> I shall remember that next time I think it might be a good idea to
> define a computationally massively expensive value as a rarely needed
> default :-)
>

Yep! That's what defaultdict can do - or if you need more flexibility,
subclass dict and add a __missing__ method.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Loris Bennett
Paul Bryan  writes:

> On Wed, 2020-12-16 at 10:01 +0100, Loris Bennett wrote:
>
>> OK, I get the point about when the default value is generated and
>> that
>> potentially being surprising, but in the example originally given,
>> the
>> key 'a' exists and has a value of '1', so the default value is not
>> needed.
>
> But the function is still called. The get method just doesn't use (or
> return) the value it generates because the key exists. Nevertheless,
> you're passing the return value of the get_default function as an
> argument.
>
>> Thus, I am still unsurprised when dict.get returns the value of an
>> existing key.
>
> As am I.
>
>> What am I missing?
>
> You'll need to tell me at this point.

I was just slow and confused, but you helped me get there in the end.  I
now realise that issue is not about the result of the dict.get(), but
rather about the fact that the method which generates the default value
is called, whether or not it is needed.

I shall remember that next time I think it might be a good idea to
define a computationally massively expensive value as a rarely needed
default :-)

Cheers,

Loris

-- 
This signature is currently under construction.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Paul Bryan
Maybe this will help:

>>> def get(key, default):
...   print("entering get")
...   print(f"{key=} {default=}")
...   print("exiting get")
... 
>>> def generate_default():
...   print("entering generate_default")
...   print("exiting generate_default")
...   return 1
... 
>>> get("a", generate_default())
entering generate_default
exiting generate_default
entering get
key='a' default=1
exiting get
>>> 

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Paul Bryan
On Wed, 2020-12-16 at 10:01 +0100, Loris Bennett wrote:

> OK, I get the point about when the default value is generated and
> that
> potentially being surprising, but in the example originally given,
> the
> key 'a' exists and has a value of '1', so the default value is not
> needed.

But the function is still called. The get method just doesn't use (or
return) the value it generates because the key exists. Nevertheless,
you're passing the return value of the get_default function as an
argument.

> Thus, I am still unsurprised when dict.get returns the value of an
> existing key.

As am I.

> What am I missing?

You'll need to tell me at this point.

Paul

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Loris Bennett
Paul Bryan  writes:

> On Wed, 2020-12-16 at 08:59 +0100, Loris Bennett wrote:
>
>> Isn't the second argument to D.get() the value to be return if the
>> first
>> argument is not a valid key?  In that case, why does it make any
>> difference here what the second argument of D.get() is since the key
>> 'a'
>> does exist?
>> 
>> Thus, I would indeed expect the code above to print '1'.  I am
>> obviously
>> missing something here.
>
> Yes, the second argument is what to return if there is no valid key.
> The second argument is evaluated before the call to the get function.
> It's return value is being supplied as an argument to the get function.
>
> Let's write a couple of quick functions to demonstrate...
>
 def get(key, default):
> ...   print(f"{key=} {default=}")
> ... 
 def generate_a_default_value():
> ...   return 1
> ... 
 get("a", generate_a_default_value())
> key='a' default=1
 
>
> The generate_a_default_value function was called before the call to
> get. It was called so it could produce a value that is actually passed
> in as an argument to the get function.

OK, I get the point about when the default value is generated and that
potentially being surprising, but in the example originally given, the
key 'a' exists and has a value of '1', so the default value is not
needed.

Thus, I am still unsurprised when dict.get returns the value of an
existing key.

What am I missing?

Cheers,

Loris

-- 
This signature is currently under construction.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Paul Bryan
On Wed, 2020-12-16 at 08:59 +0100, Loris Bennett wrote:

> Isn't the second argument to D.get() the value to be return if the
> first
> argument is not a valid key?  In that case, why does it make any
> difference here what the second argument of D.get() is since the key
> 'a'
> does exist?
> 
> Thus, I would indeed expect the code above to print '1'.  I am
> obviously
> missing something here.

Yes, the second argument is what to return if there is no valid key.
The second argument is evaluated before the call to the get function.
It's return value is being supplied as an argument to the get function.

Let's write a couple of quick functions to demonstrate...

>>> def get(key, default):
...   print(f"{key=} {default=}")
... 
>>> def generate_a_default_value():
...   return 1
... 
>>> get("a", generate_a_default_value())
key='a' default=1
>>> 

The generate_a_default_value function was called before the call to
get. It was called so it could produce a value that is actually passed
in as an argument to the get function.

Paul

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-16 Thread Loris Bennett
Serhiy Storchaka  writes:

> 15.12.20 19:07, Mark Polesky via Python-list пише:
>> # Running this script
>> 
>> D = {'a':1}
>> def get_default():
>>     print('Nobody expects this')
>>     return 0
>> print(D.get('a', get_default()))
>> 
>> # ...generates this output:
>> 
>> Nobody expects this
>> 1
>> 
>> ###
>> 
>> Since I'm brand new to this community, I thought I'd ask here first... Is 
>> this
>> worthy of a bug report?  This behavior is definitely unexpected to me, and I
>> accidentally coded an endless loop in a mutual recursion situation because of
>> it.  Calling dict.get.__doc__ only gives this short sentence: Return the 
>> value
>> for key if key is in the dictionary, else default.  Nothing in that docstring
>> suggests that the default value is evaluated even if the key exists, and I
>> can't think of any good reason to do so.
>> 
>> Am I missing something?
>
> You are missed that expressions for function arguments are always
> evaluated before passing their values to a function. This is expected
> behavior, and I can't remember any programming language in which it's
> different.
>
> So dict.get() returns the value of argument default, which is computed
> by calling function get_default() before calling dict.get().

Isn't the second argument to D.get() the value to be return if the first
argument is not a valid key?  In that case, why does it make any
difference here what the second argument of D.get() is since the key 'a'
does exist?

Thus, I would indeed expect the code above to print '1'.  I am obviously
missing something here.

Cheers,

Loris

-- 
This signature is currently under construction.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Matt Ruffalo
On 15/12/20 15:26, Grant Edwards wrote:
> On 2020-12-15, Mark Polesky via Python-list  wrote:
>
>> I see. Perhaps counterintuitive,
> I guess that depends on what programming language you normally think
> in. Python's handling of function parameters is exactly what I
> expected, because all of the previous languages I used did the same
> thing.
>
> Purely out of curiosity, what language had you been using that behaved
> otherwise?
>

R comes to mind; arguments can even be defined as functions of other
arguments, and this is evaluated symbolically at call time:

"""
> f = function(x, y = 2 * x) { y }
> f(1)
[1] 2
> f(2)
[1] 4
> f(y = 3)
[1] 3
"""

MMR...
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread 2QdxY4RzWzUUiLuE
On 2020-12-16 at 12:01:01 +1300,
dn via Python-list  wrote:

> > On Tue, Dec 15, 2020 at 9:57 AM Mark Polesky via Python-list <
> > python-list@python.org> wrote:
> >
> >> Hi.
> >>
> >> # Running this script
> >>
> >> D = {'a':1}
> >> def get_default():
> >>  print('Nobody expects this')
> >>  return 0
> >> print(D.get('a', get_default()))
> >>
> >> # ...generates this output:
> >>
> >> Nobody expects this
> >> 1
> >>
> >> ###
> >>
> >> Since I'm brand new to this community, I thought I'd ask here first... Is
> >> this worthy of a bug report?  This behavior is definitely unexpected to
> me,
> >> and I accidentally coded an endless loop in a mutual recursion situation
> >> because of it.  Calling dict.get.__doc__ only gives this short sentence:
> >> Return the value for key if key is in the dictionary, else default.
> >> Nothing in that docstring suggests that the default value is evaluated
> even
> >> if the key exists, and I can't think of any good reason to do so.
> 
> 
> Hey this is fun! A quick search reveals that I've (mea culpa) always thought
> of dict.get() as providing a default *value* and not used a function in
> there to provide said value.
> 
> That said, the function's name says it all: get_default(). Is it 'getting' a
> default value? No, it's (also) reporting-back. Thus a "side-effect".
> Undesirable, as you say.
> 
> Were it appropriate to report that a default value was necessary, maybe
> something like:
> 
> try:
> print( d[ 'a' ] )
> except KeyError:
> print( "Nobody expects this" )
> d[ 'a' ] = 0

Be careful:  that mutates d in the except clause, whereas calling
d.get('a', 0) doesn't, which may or may not be okay.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Chris Angelico
On Wed, Dec 16, 2020 at 10:03 AM dn via Python-list
 wrote:
>
> On 16/12/2020 07:52, Dan Stromberg wrote:
> ...> BTW, I tend to prefer collections.defaultdict over the two argument
> D.get
> > or setdefault.
>
>
> Contrarily, dict.get() seems 'better', unless (a) the dict's values are
> all to be initialised to the same value, eg all None, int 0, or empty
> list []; or (b) my fingers will be tired by the number of dict.get()
> calls to be typed.
>
> This thought partly because it may be some 'distance' between the
> definition of the 'dict' as a defaultdict cf the built-in 'original',
> and therefore a simple boy (like me) finds it easy to forget or 'lose
> track'.
>
> Also, because it is unusual to come-across a default-factory which
> initialises values to the defaultdict other than uniformly.
>
> Have you seen any application of defaultdict with some more-complex and
> cleverly-designed default-factory function?
>
> Other than personal-preference (which should be respected), and a
> uniform default-value, what is the rationale for defaultdict over
> dict.get()?

It depends on use-case. Using dict.get() is appropriate when you want
to know if something's there without prepopulating it; but if your
entire purpose is to then stash something into the dict, and you'll
ALWAYS be following the same pattern, then it's often more convenient
to use __missing__. And if your __missing__ method is, as it commonly
will be, constructing a brand new object, then it's easiest to use
defaultdict. But a defaultdict for integers might be better written as
a Counter instead. There are a lot of similar options, and you take
whatever's easiest for this particular job.

One VERY common use-case is gathering things together into lists based
on some sort of lookup key. This one is perfect for defaultdict:

messages = defaultdict(list)
for msg in get_messages():
messages[msg["author"]].append(msg)

That'll quickly and easily group the messages by their authors. The
first time an author is found, a new list is created, and then you
append them all.

(In digging for examples, I actually found a place where I'd used
defaultdict(collections.Counter) to create a nested structure very
easily. Worked out well. Pretty unusual thing to want though!)

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread dn via Python-list

On 16/12/2020 07:52, Dan Stromberg wrote:
...> BTW, I tend to prefer collections.defaultdict over the two argument 
D.get

or setdefault.



Contrarily, dict.get() seems 'better', unless (a) the dict's values are 
all to be initialised to the same value, eg all None, int 0, or empty 
list []; or (b) my fingers will be tired by the number of dict.get() 
calls to be typed.


This thought partly because it may be some 'distance' between the 
definition of the 'dict' as a defaultdict cf the built-in 'original', 
and therefore a simple boy (like me) finds it easy to forget or 'lose 
track'.


Also, because it is unusual to come-across a default-factory which 
initialises values to the defaultdict other than uniformly.


Have you seen any application of defaultdict with some more-complex and 
cleverly-designed default-factory function?


Other than personal-preference (which should be respected), and a 
uniform default-value, what is the rationale for defaultdict over 
dict.get()?

--
Regards =dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread dn via Python-list

> On Tue, Dec 15, 2020 at 9:57 AM Mark Polesky via Python-list <
> python-list@python.org> wrote:
>
>> Hi.
>>
>> # Running this script
>>
>> D = {'a':1}
>> def get_default():
>>  print('Nobody expects this')
>>  return 0
>> print(D.get('a', get_default()))
>>
>> # ...generates this output:
>>
>> Nobody expects this
>> 1
>>
>> ###
>>
>> Since I'm brand new to this community, I thought I'd ask here 
first... Is
>> this worthy of a bug report?  This behavior is definitely unexpected 
to me,

>> and I accidentally coded an endless loop in a mutual recursion situation
>> because of it.  Calling dict.get.__doc__ only gives this short sentence:
>> Return the value for key if key is in the dictionary, else default.
>> Nothing in that docstring suggests that the default value is 
evaluated even

>> if the key exists, and I can't think of any good reason to do so.


Hey this is fun! A quick search reveals that I've (mea culpa) always 
thought of dict.get() as providing a default *value* and not used a 
function in there to provide said value.


That said, the function's name says it all: get_default(). Is it 
'getting' a default value? No, it's (also) reporting-back. Thus a 
"side-effect". Undesirable, as you say.


Were it appropriate to report that a default value was necessary, maybe 
something like:


try:
print( d[ 'a' ] )
except KeyError:
print( "Nobody expects this" )
d[ 'a' ] = 0
--
Regards =dn
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Dan Stromberg
On Tue, Dec 15, 2020 at 11:05 AM Serhiy Storchaka 
wrote:

> 15.12.20 19:07, Mark Polesky via Python-list пише:
> > # Running this script
> >
> > D = {'a':1}
> > def get_default():
> > print('Nobody expects this')
> > return 0
> > print(D.get('a', get_default()))
> >
> > # ...generates this output:
> >
> > Nobody expects this
> > 1
> >
> > ###
> >
> > Since I'm brand new to this community, I thought I'd ask here first...
> Is this worthy of a bug report?  This behavior is definitely unexpected to
> me, and I accidentally coded an endless loop in a mutual recursion
> situation because of it.  Calling dict.get.__doc__ only gives this short
> sentence: Return the value for key if key is in the dictionary, else
> default.  Nothing in that docstring suggests that the default value is
> evaluated even if the key exists, and I can't think of any good reason to
> do so.
> >
> > Am I missing something?
>
> You are missed that expressions for function arguments are always
> evaluated before passing their values to a function. This is expected
> behavior, and I can't remember any programming language in which it's
> different.
>
I think Haskell might be different. It's a lazy language. So its '&&' and
'||' operators aren't specialcased by the language implementation to be
short-circuit - everything is just lazy naturally.  It's a neat idea, but
it's kind of hard to reason about its performance characteristics.  Python
has laziness in its 'and' and 'or', as well as in its generators - this
gives laziness where you tend to need it the most, without Python's
performance becoming hard to reason about (modulo the garbage collector).

One of the coolest things about Haskell is if you write a sort function in
it, then you can get a "give me the n lowest values" function for free.
That's lazy.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Ethan Furman

On 12/15/20 9:07 AM, Mark Polesky via Python-list wrote:

> D = {'a':1}
>
> def get_default():
>  print('Nobody expects this')
>  return 0
>
> print(D.get('a', get_default()))

Python has short-circuiting logical operations, so one way to get the behavior 
you're looking for is:

D.get('a') or get_default()

--
~Ethan~
--
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Grant Edwards
On 2020-12-15, Mark Polesky via Python-list  wrote:

> I see. Perhaps counterintuitive,

I guess that depends on what programming language you normally think
in. Python's handling of function parameters is exactly what I
expected, because all of the previous languages I used did the same
thing.

Purely out of curiosity, what language had you been using that behaved
otherwise?

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Mark Polesky via Python-list
I see. Perhaps counterintuitive, but implemented consistently. Add it to the 
list of gotchas, I guess.

By the way... four helpful responses in under an hour, very impressive. Nice 
community here. Thanks to all who answered.

Mark






On Tuesday, December 15, 2020, 11:05:10 AM PST, Serhiy Storchaka 
 wrote: 





15.12.20 19:07, Mark Polesky via Python-list пише:

> # Running this script
> 
> D = {'a':1}
> def get_default():
>     print('Nobody expects this')
>     return 0
> print(D.get('a', get_default()))
> 
> # ...generates this output:
> 
> Nobody expects this
> 1
> 
> ###
> 
> Since I'm brand new to this community, I thought I'd ask here first... Is 
> this worthy of a bug report?  This behavior is definitely unexpected to me, 
> and I accidentally coded an endless loop in a mutual recursion situation 
> because of it.  Calling dict.get.__doc__ only gives this short sentence: 
> Return the value for key if key is in the dictionary, else default.  Nothing 
> in that docstring suggests that the default value is evaluated even if the 
> key exists, and I can't think of any good reason to do so.
> 
> Am I missing something?


You are missed that expressions for function arguments are always
evaluated before passing their values to a function. This is expected
behavior, and I can't remember any programming language in which it's
different.

So dict.get() returns the value of argument default, which is computed
by calling function get_default() before calling dict.get().

-- 
https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Serhiy Storchaka
15.12.20 19:07, Mark Polesky via Python-list пише:
> # Running this script
> 
> D = {'a':1}
> def get_default():
>     print('Nobody expects this')
>     return 0
> print(D.get('a', get_default()))
> 
> # ...generates this output:
> 
> Nobody expects this
> 1
> 
> ###
> 
> Since I'm brand new to this community, I thought I'd ask here first... Is 
> this worthy of a bug report?  This behavior is definitely unexpected to me, 
> and I accidentally coded an endless loop in a mutual recursion situation 
> because of it.  Calling dict.get.__doc__ only gives this short sentence: 
> Return the value for key if key is in the dictionary, else default.  Nothing 
> in that docstring suggests that the default value is evaluated even if the 
> key exists, and I can't think of any good reason to do so.
> 
> Am I missing something?

You are missed that expressions for function arguments are always
evaluated before passing their values to a function. This is expected
behavior, and I can't remember any programming language in which it's
different.

So dict.get() returns the value of argument default, which is computed
by calling function get_default() before calling dict.get().

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Chris Angelico
On Wed, Dec 16, 2020 at 4:58 AM Mark Polesky via Python-list
 wrote:
>
> Hi.
>
> # Running this script
>
> D = {'a':1}
> def get_default():
> print('Nobody expects this')
> return 0
> print(D.get('a', get_default()))
>
> # ...generates this output:
>
> Nobody expects this
> 1
>
> ###
>
> Since I'm brand new to this community, I thought I'd ask here first... Is 
> this worthy of a bug report?  This behavior is definitely unexpected to me, 
> and I accidentally coded an endless loop in a mutual recursion situation 
> because of it.  Calling dict.get.__doc__ only gives this short sentence: 
> Return the value for key if key is in the dictionary, else default.  Nothing 
> in that docstring suggests that the default value is evaluated even if the 
> key exists, and I can't think of any good reason to do so.
>
> Am I missing something?
>

Seems what you want is the __missing__ method. Look into it - I think
it will do what you're expecting here.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: dict.get(key, default) evaluates default even if key exists

2020-12-15 Thread Dan Stromberg
On Tue, Dec 15, 2020 at 9:57 AM Mark Polesky via Python-list <
python-list@python.org> wrote:

> Hi.
>
> # Running this script
>
> D = {'a':1}
> def get_default():
> print('Nobody expects this')
> return 0
> print(D.get('a', get_default()))
>
> # ...generates this output:
>
> Nobody expects this
> 1
>
> ###
>
> Since I'm brand new to this community, I thought I'd ask here first... Is
> this worthy of a bug report?  This behavior is definitely unexpected to me,
> and I accidentally coded an endless loop in a mutual recursion situation
> because of it.  Calling dict.get.__doc__ only gives this short sentence:
> Return the value for key if key is in the dictionary, else default.
> Nothing in that docstring suggests that the default value is evaluated even
> if the key exists, and I can't think of any good reason to do so.
>

Python isn't a lazy language.  It's evaluating get_default()
unconditionally, prior to executing the D.get().  I don't think it's a
Python bug.

BTW, I tend to prefer collections.defaultdict over the two argument D.get
or setdefault.
-- 
https://mail.python.org/mailman/listinfo/python-list