Sorry, PEP 3106 is not yet accepted. Also removed mutating operations

and added some clarifications.
This commit is contained in:
Guido van Rossum 2007-02-20 23:32:28 +00:00
parent d6a8f11a3a
commit 7c97ac0ea4
2 changed files with 23 additions and 64 deletions

View File

@ -78,7 +78,7 @@ Index by Category
Accepted PEPs (accepted; may not be implemented yet) Accepted PEPs (accepted; may not be implemented yet)
SA 3102 Keyword-Only Arguments Talin SA 3102 Keyword-Only Arguments Talin
SA 3106 Revamping dict.keys(), .values() and .items() GvR S 3106 Revamping dict.keys(), .values() and .items() GvR
SA 3107 Function Annotations Winter, Lownds SA 3107 Function Annotations Winter, Lownds
SA 3109 Raising Exceptions in Python 3000 Winter SA 3109 Raising Exceptions in Python 3000 Winter
SA 3110 Catching Exceptions in Python 3000 Winter SA 3110 Catching Exceptions in Python 3000 Winter
@ -449,7 +449,7 @@ Numerical Index
S 3103 A Switch/Case Statement GvR S 3103 A Switch/Case Statement GvR
S 3104 Access to Names in Outer Scopes Yee S 3104 Access to Names in Outer Scopes Yee
SF 3105 Make print a function Brandl SF 3105 Make print a function Brandl
SA 3106 Revamping dict.keys(), .values() and .items() GvR S 3106 Revamping dict.keys(), .values() and .items() GvR
SA 3107 Function Annotations Winter, Lownds SA 3107 Function Annotations Winter, Lownds
I 3108 Standard Library Reorganization Cannon I 3108 Standard Library Reorganization Cannon
SA 3109 Raising Exceptions in Python 3000 Winter SA 3109 Raising Exceptions in Python 3000 Winter

View File

@ -3,7 +3,7 @@ Title: Revamping dict.keys(), .values() and .items()
Version: $Revision$ Version: $Revision$
Last-Modified: $Date$ Last-Modified: $Date$
Author: Guido van Rossum Author: Guido van Rossum
Status: Accepted Status: Draft
Type: Standards Type: Standards
Content-Type: text/x-rst Content-Type: text/x-rst
Created: 19-Dec-2006 Created: 19-Dec-2006
@ -14,11 +14,10 @@ Abstract
======== ========
This PEP proposes to change the .keys(), .values() and .items() This PEP proposes to change the .keys(), .values() and .items()
methods of the built-in dict type to return a set-like or methods of the built-in dict type to return a set-like or unordered
multiset-like (== bag-like) object whose contents are derived of the container object whose contents are derived of the underlying
underlying dictionary rather than a list which is a copy of the keys, dictionary rather than a list which is a copy of the keys, etc.; and
etc.; and to remove the .iterkeys(), .itervalues() and .iteritems() to remove the .iterkeys(), .itervalues() and .iteritems() methods.
methods.
The approach is inspired by that taken in the Java Collections The approach is inspired by that taken in the Java Collections
Framework [1]_. Framework [1]_.
@ -72,25 +71,16 @@ d.iterkeys() (etc.) does in Python 2.x; but in most contexts we don't
have to write the iter() call because it is implied by a for-loop. have to write the iter() call because it is implied by a for-loop.
The objects returned by the .keys() and .items() methods behave like The objects returned by the .keys() and .items() methods behave like
sets with limited mutability; they allow removing elements, but not sets. The object returned by the values() method behaves like a much
adding them. Removing an item from these sets removes it from the simpler unordered collection; anything more would require too much
underlying dict. The object returned by the values() method behaves implementation effort for the rare use case.
like a multiset (Java calls this a Collection). It does not allow
removing elements, because a value might occur multiple times and the
implementation wouldn't know which key to remove from the underlying
dict. (The Java Collections Framework has a way around this by
removing from an iterator, but I see no practical use case for that
functionality.)
Because of the set behavior, it will be possible to check whether two Because of the set behavior, it will be possible to check whether two
dicts have the same keys by simply testing:: dicts have the same keys by simply testing::
if a.keys() == b.keys(): ... if a.keys() == b.keys(): ...
and similarly for values. (Two multisets are deemed equal if they and similarly for .items().
have the same elements with the same cardinalities, e.g. the multiset
{1, 2, 2} is equal to the multiset {2, 1, 2} but differs from the
multiset {1, 2}.)
These operations are thread-safe only to the extent that using them in These operations are thread-safe only to the extent that using them in
a thread-unsafe way may cause an exception but will not cause a thread-unsafe way may cause an exception but will not cause
@ -145,26 +135,12 @@ I'm using pseudo-code to specify the semantics::
for key in self.__d: for key in self.__d:
yield key yield key
def remove(self, key):
del self.__d[key]
def discard(self, key):
if key in self:
self.remove(key)
def pop(self):
return self.__d.popitem()[0]
def clear(self):
self.__d.clear()
# The following operations should be implemented to be # The following operations should be implemented to be
# compatible with sets; this can be done by exploiting # compatible with sets; this can be done by exploiting
# the above primitive operations: # the above primitive operations:
# #
# <, <=, ==, !=, >=, > (returning a bool) # <, <=, ==, !=, >=, > (returning a bool)
# &, |, ^, - (returning a new, real set object) # &, |, ^, - (returning a new, real set object)
# &=, -= (updating in place and returning self; but not |=, ^=)
# #
# as well as their method counterparts (.union(), etc.). # as well as their method counterparts (.union(), etc.).
# #
@ -191,23 +167,6 @@ I'm using pseudo-code to specify the semantics::
for key in self.__d: for key in self.__d:
yield key, self.__d[key] yield key, self.__d[key]
def remove(self, (key, value)):
if (key, value) not in self:
raise KeyError((key, value))
del self.__d[key]
def discard(self, item):
# Defined in terms of 'in' and .remove() so overriding
# those will update discard appropriately.
if item in self:
self.remove(item)
def pop(self):
return self.__d.popitem()
def clear(self):
self.__d.clear()
# As well as the set operations mentioned for d_keys above. # As well as the set operations mentioned for d_keys above.
# However the specifications suggested there will not work if # However the specifications suggested there will not work if
# the values aren't hashable. Fortunately, the operations can # the values aren't hashable. Fortunately, the operations can
@ -288,11 +247,8 @@ I'm using pseudo-code to specify the semantics::
# XXX Sometimes this could be optimized, but these are the # XXX Sometimes this could be optimized, but these are the
# semantics: we can't depend on the values to be hashable # semantics: we can't depend on the values to be hashable
# or comparable. # or comparable.
o = list(other)
for x in self: for x in self:
try: if not o in other:
o.remove(x)
except ValueError:
return False return False
return True return True
@ -302,13 +258,21 @@ I'm using pseudo-code to specify the semantics::
result = not result result = not result
return result return result
Note that we don't implement .copy() -- the presence of a .copy() Notes:
The view objects are not directly mutable, but don't implement
__hash__(); their value can change if the underlying dict is mutated.
The only requirements on the underlying dict are that it implements
__getitem__(), __contains__(), __iter__(), and __len__(0.
We don't implement .copy() -- the presence of a .copy()
method suggests that the copy has the same type as the original, but method suggests that the copy has the same type as the original, but
that's not feasible without copying the underlying dict. If you want that's not feasible without copying the underlying dict. If you want
a copy of a specific type, like list or set, you can just pass one a copy of a specific type, like list or set, you can just pass one
of the above to the list() or set() constructor. of the above to the list() or set() constructor.
Also note that the specification implies that the order in which items The specification implies that the order in which items
are returned by .keys(), .values() and .items() is the same (just as are returned by .keys(), .values() and .items() is the same (just as
it was in Python 2.x), because the order is all derived from the dict it was in Python 2.x), because the order is all derived from the dict
iterator (which is presumably arbitrary but stable as long as a dict iterator (which is presumably arbitrary but stable as long as a dict
@ -325,7 +289,7 @@ set operations on keys and items without having to copy them should
speak for itself. speak for itself.
I've left out the implementation of various set operations. These I've left out the implementation of various set operations. These
could still present surprises. could still present small surprises.
It would be okay if multiple calls to d.keys() (etc.) returned the It would be okay if multiple calls to d.keys() (etc.) returned the
same object, since the object's only state is the dict to which it same object, since the object's only state is the dict to which it
@ -334,11 +298,6 @@ Should that be a weak reference or should the d_keys (etc.) object
live forever once created? Strawman: probably not worth the extra live forever once created? Strawman: probably not worth the extra
slots in every dict. slots in every dict.
Should d_values have mutating methods (pop(), clear())? Strawman: no.
Should d_values implement set operations (as defined for multisets).
Strawman: no.
Should d_keys, d_values and d_items have a public instance variable or Should d_keys, d_values and d_items have a public instance variable or
method through which one can retrieve the underlying dict? Strawman: method through which one can retrieve the underlying dict? Strawman:
yes (but what should it be called?). yes (but what should it be called?).