Skip to content

Commit

Permalink
Improvements to bidict view methods and docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
jab committed Jan 6, 2022
1 parent 2598ccb commit 38a3c51
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 56 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ Development
See `the tests <https://github.com/jab/bidict/blob/main/tests/>`__
for examples.

- Ordered bidicts' :meth:`~bidict.OrderedBidictBase.items` method
now returns a :class:`reversible <collections.abc.Reversible>`
:class:`~collections.abc.ItemsView`.

- Expanded docstrings for
:meth:`~bidict.BidictBase.keys`,
:meth:`~bidict.BidictBase.values`, and
:meth:`~bidict.BidictBase.items`.

- Remove the use of slots from (non-ABC) bidict types.

This better matches the mapping implementations in Python's standard library,
Expand Down
14 changes: 0 additions & 14 deletions bidict/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,6 @@ def __inverted__(self) -> _t.Iterator[_t.Tuple[VT, KT]]:
"""
return iter(self.inverse.items())

def values(self) -> _t.KeysView[VT]: # type: ignore [override] # https://github.com/python/typeshed/issues/4435
"""A set-like object providing a view on the contained values.
Override the implementation inherited from
:class:`~collections.abc.Mapping`.
Because the values of a :class:`~bidict.BidirectionalMapping`
are the keys of its inverse,
this returns a :class:`~collections.abc.KeysView`
rather than a :class:`~collections.abc.ValuesView`,
which has the advantages of constant-time containment checks
and supporting set operations.
"""
return self.inverse.keys()


class MutableBidirectionalMapping(BidirectionalMapping[KT, VT], _t.MutableMapping[KT, VT]):
"""Abstract base class (ABC) for mutable bidirectional mapping types."""
Expand Down
47 changes: 45 additions & 2 deletions bidict/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,57 @@ def __repr__(self) -> str:
return f'{clsname}()'
return f'{clsname}({self._repr_delegate(self.items())})'

# The inherited Mapping.__contains__ method is implemented by doing a ``try``
# The inherited collections.abc.Mapping.keys() method returns a collections.abc.KeysView,
# which is currently implemented in pure Python rather than optimized C, so override:
def keys(self) -> _t.KeysView[KT]:
"""A set-like object providing a view on the contained keys.
Returns a dict_keys object that behaves exactly the same as collections.abc.KeysView(b),
except for being much faster when running on CPython, being reversible,
and having a .mapping attribute in Python 3.10+ that exposes a mappingproxy
pointing back to the (one-way) forward dictionary that backs this bidict.
"""
return self._fwdm.keys()

# The inherited collections.abc.Mapping.values() method returns a collections.abc.ValuesView, so override:
def values(self) -> _t.KeysView[VT]: # type: ignore [override] # https://github.com/python/typeshed/issues/4435
"""A set-like object providing a view on the contained values.
Since the values of a bidict are equivalent to the keys of its inverse,
this method returns a KeysView for this bidict's inverse
rather than just a ValuesView for this bidict.
The KeysView offers the benefit of supporting set operations
(including constant- rather than linear-time containment checks)
and is just as cheap to provide as the less capable ValuesView would be.
Returns a dict_keys object that behaves exactly the same as collections.abc.KeysView(b.inverse),
except for being much faster when running on CPython, being reversible,
and having a .mapping attribute in Python 3.10+ that exposes a mappingproxy
pointing back to the (one-way) inverse dictionary that backs this bidict.
"""
return self._invm.keys()

# The inherited collections.abc.Mapping.items() methods returns collections.abc.ItemsView,
# which is currently implemented in pure Python rather than optimized C, so override:
def items(self) -> _t.ItemsView[KT, VT]:
"""A set-like object providing a view on the contained items.
Returns a dict_items object that behaves exactly the same as collections.abc.ItemsView(b),
except for being much faster when running on CPython, being reversible,
and having a .mapping attribute in Python 3.10+ that exposes a mappingproxy
pointing back to the (one-way) forward dictionary that backs this bidict.
"""
# Overridden by _orderedbase.OrderedBidictBase.
return self._fwdm.items()

# The inherited collections.abc.Mapping.__contains__() method is implemented by doing a ``try``
# ``except KeyError`` around ``self[key]``. The following implementation is much faster,
# especially in the missing case.
def __contains__(self, key: _t.Any) -> bool:
"""True if the mapping contains the specified key, else False."""
return key in self._fwdm

# The inherited Mapping.__eq__ method is implemented in terms of an inefficient
# The inherited collections.abc.Mapping.__eq__() method is implemented in terms of an inefficient
# ``dict(self.items()) == dict(other.items())`` comparison, so override it with a
# more efficient implementation.
def __eq__(self, other: object) -> bool:
Expand Down
3 changes: 1 addition & 2 deletions bidict/_bidict.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@

import typing as _t

from ._delegating import _DelegatingBidict
from ._mut import MutableBidict
from ._typing import KT, VT


class bidict(_DelegatingBidict[KT, VT], MutableBidict[KT, VT]):
class bidict(MutableBidict[KT, VT]):
"""Base class for mutable bidirectional mappings."""

if _t.TYPE_CHECKING:
Expand Down
36 changes: 0 additions & 36 deletions bidict/_delegating.py

This file was deleted.

4 changes: 2 additions & 2 deletions bidict/_frozenbidict.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@

import typing as _t

from ._delegating import _DelegatingBidict
from ._base import BidictBase
from ._typing import KT, VT


class frozenbidict(_DelegatingBidict[KT, VT]):
class frozenbidict(BidictBase[KT, VT]):
"""Immutable, hashable bidict type."""

_hash: int
Expand Down
11 changes: 11 additions & 0 deletions bidict/_orderedbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,17 @@ def __reversed__(self) -> _t.Iterator[KT]:
"""Iterator over the contained keys in reverse insertion order."""
return self._iter(reverse=True)

def items(self) -> _t.ItemsView[KT, VT]:
"""A set-like object providing a view on the contained items."""
return _OrderedBidictItemsView(self)


class _OrderedBidictItemsView(_t.ItemsView[KT, VT]):
def __reversed__(self) -> _t.Iterator[_t.Tuple[KT, VT]]:
ob = self._mapping # type: ignore[attr-defined]
for key in reversed(ob):
yield (key, ob[key])


# * Code review nav *
#==============================================================================
Expand Down
6 changes: 6 additions & 0 deletions tests/test_orderedbidict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,9 @@ and the doctests in the Sphinx docs don't get counted in the coverage report)::
Traceback (most recent call last):
...
KeyError: 'NOT FOUND'

The object returned by .items() is reversible:

>>> b = OrderedBidict([(0, 1), (2, 3)])
>>> list(reversed(b.items()))
[(2, 3), (0, 1)]

0 comments on commit 38a3c51

Please sign in to comment.