Re: Behaviour of pop() for dictionaries

2021-06-15 Thread dn via Python-list
On 15/06/2021 21.37, BlindAnagram wrote:
> On 15/06/2021 00:11, dn wrote:
>> On 15/06/2021 09.18, BlindAnagram wrote:
>>> On 14/06/2021 20:43, Chris Angelico wrote:
 On Tue, Jun 15, 2021 at 5:41 AM BlindAnagram
>> ...
> I think the difference here is that I know I am going to have to look at
> the documentation for dequeue when I want to use it. For lists, sets and
> dictionaries, I don't expect to look at the documentation and pop()
> seemed a good bet for what I wanted to do.

"What I expect" (aka 'dim/dusty recollection' or 'gut feel') is a good
way to learn - match it with the Python REPL and a couple of experiments
to 'prove' your expectation/recollection. Thereafter there is a far
greater likelihood of "learning" - and remembering-correctly 'next time'...

The other worthy technique in this field is "deliberate learning". Thus,
spending some time studying the docs for the built-ins' functionality
(and experimentation) to obviate the need to look-up that level of
documentation.

Contrary to the learning-practices of 'our day', these days there is a
far lower reliance on memory, in favor of rapid-access to reference
data, eg the Python docs and REPL's help(), etc. Accordingly, the 'bits'
that we might think of as 'minimum knowledge' may be seen as somewhat
"arbitrary".
(actually it is called Curriculum Design, but given that there is no
single application-area for Python there is no single curriculum either)

No matter: we are completely correct, no question - and what would
'they' know anyway?


PS we were also subject to the idea that intelligence/ability was
largely genetic and thus only available in a fixed quantity - either one
is 'good' at something, or not (full stop). These days we know ourselves
(brains) to be more "plastic", and that with sufficient motivation and
effort we can learn 'new stuff', regardless of whether we were 'good at
it' yesterday!


>> I don't know if you are ComSc student or not, but there isn't even
>> consistency at the theoretical level. I first arrived at the concept of
>> "queuing" and "dequeuing" (NB the latter is not the same as the Python
>> Collections module "deque" library) whilst studying Queue Theory in
>> Operations Research. At about the same time, my ComSc studies took me
>> into "stacks".
> 
> My student days are well over (about 60 years over).

Someone with an even longer beard than mine! We could compare walking
sticks...

Oh wait, aren't we talking about Python.

I'm amazed at how stuff from 'history' is recyclable and becomes
applicable to whichever is described as 'today'. Even 'new' things in
computing!

There's always something new to adapt, if not learn...


...

> Yes, but we can still seek consistency where it is possible

Trouble is, there are different ways of looking at 'stuff', and thus
different dimensions of "consistent".

Thus, dict methods try to be consistent to the way a dict behaves.
Whereas being 'consistent' with other collections: sets, lists, strings,
etc; comes second. Oops if not OOPs!


>> Having entered the queue-of-life a long time ago, and shuffling ever
>> closer to 'the end of the line', this memory-challenged 'silver-surfer'
>> prefers to reserve pop() for straightforward stacks and lists, which
>> implies quite enough inconsistency, without trying to convolute my mind
>> to pop()-ing dicts, sets (or 'worse'!).

I still haven't recalled a single occasion when I've used set.pop() or
dict.pop(). Was that because I didn't recall their availability at the
right time, or does my mind simply not see that 'consistency' yours
recognises?
(not that it matters particularly)


>> That said, whether I actually use dict.pop() or not, these 'features'
>> which are consistent in name but not necessarily in arguments or
>> effects, contribute to Python's great flexibility and power! Thank
>> goodness for help() and the Python docs...
> 
> I don't like 'pop's at all since it meant that a valve had exploded on
> the Ferranti Pegasus that was my first encounter with computers.

That does pre-date the prehistoric computers I managed to play-with!

There's apparently one in LON's Science Museum. Don't know if it was
there when I last visited (c.15 years ago) - I do recall their Babbage
implementations and an analog computer (looked like some torture rack)
similar to one that my father used way, way, way-back. Similarly,
considering it/another similar exhibit and talking with our friends
about the 'joys' of using paper tape (and its talent for wrapping itself
around anything and everything, except where you wanted it) and those of
(assembler) programming delays in order to store data on rotating
mag-drums.

Those were the days!
(or maybe not)
-- 
Regards,
=dn
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Behaviour of pop() for dictionaries

2021-06-15 Thread BlindAnagram

On 15/06/2021 00:11, dn wrote:

On 15/06/2021 09.18, BlindAnagram wrote:

On 14/06/2021 20:43, Chris Angelico wrote:

On Tue, Jun 15, 2021 at 5:41 AM BlindAnagram

...


No it isn't hard to use popitem() but it evidently proved hard for me to
remember that it was there.


If that's a problem, you're going to love using deques with their
'popping from the left' and 'popping from the right' concepts!


I think the difference here is that I know I am going to have to look at 
the documentation for dequeue when I want to use it. For lists, sets and 
dictionaries, I don't expect to look at the documentation and pop() 
seemed a good bet for what I wanted to do.



I don't know if you are ComSc student or not, but there isn't even
consistency at the theoretical level. I first arrived at the concept of
"queuing" and "dequeuing" (NB the latter is not the same as the Python
Collections module "deque" library) whilst studying Queue Theory in
Operations Research. At about the same time, my ComSc studies took me
into "stacks".


My student days are well over (about 60 years over).


Queues are known as FIFO constructs - "first-in, first-out".
Stacks are somewhat the opposite: LIFO - "last-in, first-out".

The "pop" operation was defined as taking the "next" item from the queue
or the "last" item from a stack (the opposite of "push"). However,
between queue and stack, the term "next" refers to opposite ends of the
(implementing in Python) list!

In fact, coming from a Burroughs mainframe, which ran on "stack
architecture" (cf IBM's multiplicity of "ALU registers"), it came as
something of a surprise when programming languages started allowing me
to "pop" elements that weren't at the LIFO-end of the 'list', eg
list.pop( 3 ) where len( list ) > 4!


Next consider how the terms "next" and "element" factor into the
thinking. If we consider a (Python) list there is an implied sequence of
elements based upon their relative position. Notice also that the basic
implementation of list.pop() is LIFO! Whereas, the definition of a set
involves no concept of sequence or order - only of membership (and that
the elements are "hashable"). Accordingly, a pop() operation returns an
"arbitrary value", cf 'next please'.

Similarly, a dict's keys are referred-to as hashable, with the idea of
"random access" to an element via its key (cf the "sequential access" of
a list). Thus, we can ask to pop() a dict, but only if we provide a key
- in which case, pop( key ) is the same as dict[ key ] except that the
key-value pair is also removed from the dict!

Recall though, it is possible to use list.pop() without any argument.
So, consistency has been thrown-out-the-window there as well.

Also, with LIFO in-mind, Python v3.7 brought a concept of 'sequence'
(population order) to dicts, and thus we now have this "guarantee" in
popitem() - and thus a memory-confusion for those of us who learned the
original "arbitrary" definition - confusion both of dict behavior and of
dict.popitem() specifically!

Worse confusion awaits (and referring to a conversation 'here' last
week) Python's pop() exhibits elements of both an "expression" and of a
"statement", ie it not only returns a value, but it affects
(?"side-effects") the underlying collection. Thus, no pop() for strings,
tuples, etc, because they are immutable collections!

The action of pop() is clearly inconsistent across types of collection.
It's effect is data-structure dependent because the purposes of those
structures are inherently different. "Consistency" would aid memory, but
"polymorphism" can only deliver functionality according to the
characteristics of the specific data-type!


Yes, but we can still seek consistency where it is possible


Having entered the queue-of-life a long time ago, and shuffling ever
closer to 'the end of the line', this memory-challenged 'silver-surfer'
prefers to reserve pop() for straightforward stacks and lists, which
implies quite enough inconsistency, without trying to convolute my mind
to pop()-ing dicts, sets (or 'worse'!).

That said, whether I actually use dict.pop() or not, these 'features'
which are consistent in name but not necessarily in arguments or
effects, contribute to Python's great flexibility and power! Thank
goodness for help() and the Python docs...


I don't like 'pop's at all since it meant that a valve had exploded on 
the Ferranti Pegasus that was my first encounter with computers.


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


Re: Behaviour of pop() for dictionaries

2021-06-15 Thread BlindAnagram

On 15/06/2021 01:36, Terry Reedy wrote:

On 6/14/2021 5:18 PM, BlindAnagram wrote:

I believe that consistency in how methods common to different types 
work is useful since it adds to the coherence of the language as a 
whole and avoids the need to remember special cases.


Each collection class *is* a special case, and pop has to be adjusted to 
each.  However, you seem to have missed an essential commonality.


Lists and dicts are both subscripted classes.  So the basic API is 
col.pop(sub), which removes and returns the sub item, whereas col[sub] 
leaves and returns.


Lists have a special index, -1, the most commonly used, so that is the 
default.  In fact, when I proposed list.pop(), I only proposed that, as 
I wanted pop to be the inverse of append, so a list could be used as a 
stack.


Bad list subscripts are an error (unless one is slicing), whereas where 
dicts allow a default (when subscripted with the get method).  Hence the 
optional default only for dicts.


At one time, dicts, like sets, were unordered collections (of functional 
item pairs). dict.pop(), with no arg, could have been used to return a 
random 2-ple, but Guido is generally against having return types depend 
on arguments. So a new popxxx name was added.  Note that deques have a 
popleft in addition to pop (right).


Thanks for the interesting history.

Having found that dict.pop() caused an error, I did wonder whether it 
should have returned a (key, value) pair but quickly came to the 
conclusion that this would be awful because it would be inconsistent 
with the normal value returned by pop(x). Sadly this did not result in 
any recollection that there was a popitem() :-(


   Brian

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


Re: Behaviour of pop() for dictionaries

2021-06-15 Thread Cameron Simpson
On 14Jun2021 09:39, BlindAnagram  wrote:
>However, d.pop(key, [default]) returns the value (or the default) and 
>consistency with other pops (a good thing in my view) would suggest 
>that d.pop() could return a random value, which would serve my purpose 
>when there is only one element.

If you don't care what key was popped, maybe you want a set and not a 
dict?

Just a thought.

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


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread Terry Reedy

On 6/14/2021 5:18 PM, BlindAnagram wrote:

I believe that consistency in how methods common to different types work 
is useful since it adds to the coherence of the language as a whole and 
avoids the need to remember special cases.


Each collection class *is* a special case, and pop has to be adjusted to 
each.  However, you seem to have missed an essential commonality.


Lists and dicts are both subscripted classes.  So the basic API is 
col.pop(sub), which removes and returns the sub item, whereas col[sub] 
leaves and returns.


Lists have a special index, -1, the most commonly used, so that is the 
default.  In fact, when I proposed list.pop(), I only proposed that, as 
I wanted pop to be the inverse of append, so a list could be used as a 
stack.


Bad list subscripts are an error (unless one is slicing), whereas where 
dicts allow a default (when subscripted with the get method).  Hence the 
optional default only for dicts.


At one time, dicts, like sets, were unordered collections (of functional 
item pairs). dict.pop(), with no arg, could have been used to return a 
random 2-ple, but Guido is generally against having return types depend 
on arguments. So a new popxxx name was added.  Note that deques have a 
popleft in addition to pop (right).


--
Terry Jan Reedy

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


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread dn via Python-list
On 15/06/2021 09.18, BlindAnagram wrote:
> On 14/06/2021 20:43, Chris Angelico wrote:
>> On Tue, Jun 15, 2021 at 5:41 AM BlindAnagram
...

> No it isn't hard to use popitem() but it evidently proved hard for me to
> remember that it was there.

If that's a problem, you're going to love using deques with their
'popping from the left' and 'popping from the right' concepts!


I don't know if you are ComSc student or not, but there isn't even
consistency at the theoretical level. I first arrived at the concept of
"queuing" and "dequeuing" (NB the latter is not the same as the Python
Collections module "deque" library) whilst studying Queue Theory in
Operations Research. At about the same time, my ComSc studies took me
into "stacks".

Queues are known as FIFO constructs - "first-in, first-out".
Stacks are somewhat the opposite: LIFO - "last-in, first-out".

The "pop" operation was defined as taking the "next" item from the queue
or the "last" item from a stack (the opposite of "push"). However,
between queue and stack, the term "next" refers to opposite ends of the
(implementing in Python) list!

In fact, coming from a Burroughs mainframe, which ran on "stack
architecture" (cf IBM's multiplicity of "ALU registers"), it came as
something of a surprise when programming languages started allowing me
to "pop" elements that weren't at the LIFO-end of the 'list', eg
list.pop( 3 ) where len( list ) > 4!


Next consider how the terms "next" and "element" factor into the
thinking. If we consider a (Python) list there is an implied sequence of
elements based upon their relative position. Notice also that the basic
implementation of list.pop() is LIFO! Whereas, the definition of a set
involves no concept of sequence or order - only of membership (and that
the elements are "hashable"). Accordingly, a pop() operation returns an
"arbitrary value", cf 'next please'.

Similarly, a dict's keys are referred-to as hashable, with the idea of
"random access" to an element via its key (cf the "sequential access" of
a list). Thus, we can ask to pop() a dict, but only if we provide a key
- in which case, pop( key ) is the same as dict[ key ] except that the
key-value pair is also removed from the dict!

Recall though, it is possible to use list.pop() without any argument.
So, consistency has been thrown-out-the-window there as well.

Also, with LIFO in-mind, Python v3.7 brought a concept of 'sequence'
(population order) to dicts, and thus we now have this "guarantee" in
popitem() - and thus a memory-confusion for those of us who learned the
original "arbitrary" definition - confusion both of dict behavior and of
dict.popitem() specifically!


Worse confusion awaits (and referring to a conversation 'here' last
week) Python's pop() exhibits elements of both an "expression" and of a
"statement", ie it not only returns a value, but it affects
(?"side-effects") the underlying collection. Thus, no pop() for strings,
tuples, etc, because they are immutable collections!


The action of pop() is clearly inconsistent across types of collection.
It's effect is data-structure dependent because the purposes of those
structures are inherently different. "Consistency" would aid memory, but
"polymorphism" can only deliver functionality according to the
characteristics of the specific data-type!


Having entered the queue-of-life a long time ago, and shuffling ever
closer to 'the end of the line', this memory-challenged 'silver-surfer'
prefers to reserve pop() for straightforward stacks and lists, which
implies quite enough inconsistency, without trying to convolute my mind
to pop()-ing dicts, sets (or 'worse'!).

That said, whether I actually use dict.pop() or not, these 'features'
which are consistent in name but not necessarily in arguments or
effects, contribute to Python's great flexibility and power! Thank
goodness for help() and the Python docs...
-- 
Regards,
=dn
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread BlindAnagram

On 14/06/2021 20:43, Chris Angelico wrote:

On Tue, Jun 15, 2021 at 5:41 AM BlindAnagram  wrote:

However, d.pop(key, [default]) returns the value (or the default) and
consistency with other pops (a good thing in my view) would suggest that
d.pop() could return a random value, which would serve my purpose when
there is only one element.



Is this actually important or are you just looking for a meaningless
"inconsistency"? Dictionaries are fundamentally different from lists.
Is it really that hard to use popitem?


No I am not looking for meaningless inconsistency - just the opposite in 
fact - meaningful consistency.


I believe that consistency in how methods common to different types work 
is useful since it adds to the coherence of the language as a whole and 
avoids the need to remember special cases.


No it isn't hard to use popitem() but it evidently proved hard for me to 
remember that it was there.


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


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread Chris Angelico
On Tue, Jun 15, 2021 at 5:41 AM BlindAnagram  wrote:
> However, d.pop(key, [default]) returns the value (or the default) and
> consistency with other pops (a good thing in my view) would suggest that
> d.pop() could return a random value, which would serve my purpose when
> there is only one element.
>

Is this actually important or are you just looking for a meaningless
"inconsistency"? Dictionaries are fundamentally different from lists.
Is it really that hard to use popitem?

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


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread BlindAnagram

On 14/06/2021 08:29, Greg Ewing wrote:

On 14/06/21 4:19 am, BlindAnagram wrote:
Am I missing the obvious way to obtain the value (or the key) from a 
dictionary that is known to hold only one item?


v = d.popitem()[1]


Thanks, Greg, I missed that.


More importantly, is there a good reason why we don't have d.pop() for 
dictionaries?


My guess is because it's not generally useful to get an returns the value 
arbitrary
value from a dict without its corresponding key. Hence the existence
of popitem().


However, d.pop(key, [default]) returns the value (or the default) and 
consistency with other pops (a good thing in my view) would suggest that 
d.pop() could return a random value, which would serve my purpose when 
there is only one element.



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


Behaviour of pop() for dictionaries

2021-06-14 Thread BlindAnagram
The pop() method exists for five mainstream data items and shows a range 
of different behaviours for each of them.


But, of the five, pop for dictionaries is the only one for which the 
first parameter is required and this makes d.pop() for dictionaries an 
error rather than doing something useful.


I came across this in trying to use this sequence for a dictionary :

  if len(d.keys()) == 1:
v = d.pop()

I found it surprising that this failed given how the pops for the other 
types work.


So I then tried:

  v = d.values()[0]

and this doesn't work either since dict_keys items don't accept 
indexing. So I was driven to use:


  v = list(d.values())[0]

which seems to me a lot less intuitive (and messier) than d.pop().

These:

  v = next(iter(d.values()))
  v, = d.values()

also seem poor substitutes for giving d.pop() for dictionaries a useful 
and intuitive purpose.


Am I missing the obvious way to obtain the value (or the key) from a 
dictionary that is known to hold only one item?


More importantly, is there a good reason why we don't have d.pop() for 
dictionaries?


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


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread Stestagg
You can do the following:

_,v = d.popitem()

Or:

key, value = d.popitem()

Steve

On Mon, 14 Jun 2021 at 20:10, Greg Ewing 
wrote:

> On 14/06/21 4:19 am, BlindAnagram wrote:
> > Am I missing the obvious way to obtain the value (or the key) from a
> > dictionary that is known to hold only one item?
>
> v = d.popitem()[1]
>
> > More importantly, is there a good reason why we don't have d.pop() for
> > dictionaries?
>
> My guess is because it's not generally useful to get an arbitrary
> value from a dict without its corresponding key. Hence the existence
> of popitem().
>
> --
> Greg
> --
> https://mail.python.org/mailman/listinfo/python-list
>
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Behaviour of pop() for dictionaries

2021-06-14 Thread Greg Ewing

On 14/06/21 4:19 am, BlindAnagram wrote:
Am I missing the obvious way to obtain the value (or the key) from a 
dictionary that is known to hold only one item?


v = d.popitem()[1]

More importantly, is there a good reason why we don't have d.pop() for 
dictionaries?


My guess is because it's not generally useful to get an arbitrary
value from a dict without its corresponding key. Hence the existence
of popitem().

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