mirror of https://github.com/jab/bidict.git
Rename .inv to .inverse and add an alias for .inv.
bidict.BidirectionalMapping.__subclasshook__ now requires an ``inverse`` attribute rather than an ``inv`` attribute for a class to qualify as a virtual subclass. Closes #86.
This commit is contained in:
parent
e9e1c41c28
commit
ce345d49ba
|
@ -22,9 +22,19 @@ Tip: `Subscribe to bidict releases <https://libraries.io/pypi/bidict>`__
|
|||
on libraries.io to be notified when new versions of bidict are released.
|
||||
|
||||
|
||||
0.17.6 (not yet released)
|
||||
0.18.0 (not yet released)
|
||||
-------------------------
|
||||
|
||||
- Rename ``bidict.BidirectionalMapping.inv`` to :attr:`~bidict.BidirectionalMapping.inverse`
|
||||
and make :attr:`bidict.BidictBase.inv`` an alias for :attr:`~bidict.BidictBase.inverse`.
|
||||
`#86 <https://github.com/jab/bidict/issues/86>`__
|
||||
|
||||
- :meth:`bidict.BidirectionalMapping.__subclasshook__` now requires an ``inverse`` attribute
|
||||
rather than an ``inv`` attribute for a class to qualify as a virtual subclass.
|
||||
This breaking change is expected to affect few if any users.
|
||||
|
||||
- Add Python 2/3-compatible :attr:`bidict.compat.collections_abc` alias.
|
||||
|
||||
- Stop testing Python 3.4 on CI,
|
||||
and warn when Python 3 < 3.5 is detected
|
||||
rather than Python 3 < 3.3.
|
||||
|
@ -33,9 +43,6 @@ on libraries.io to be notified when new versions of bidict are released.
|
|||
Python 3.4 represents only about 3% of bidict downloads as of January 2019.
|
||||
The latest release of Pip has also deprecated support for Python 3.4.
|
||||
|
||||
- Add Python 2/3-compatible :attr:`bidict.compat.collections_abc` alias.
|
||||
|
||||
|
||||
|
||||
0.17.5 (2018-11-19)
|
||||
-------------------
|
||||
|
@ -240,7 +247,7 @@ Minor Bugfixes
|
|||
(with ``_fwdm_cls`` and ``_invm_cls`` swapped)
|
||||
is now correctly computed and used automatically
|
||||
for your custom bidict's
|
||||
:attr:`~bidict.BidictBase.inv` bidict.
|
||||
:attr:`~bidict.BidictBase.inverse` bidict.
|
||||
|
||||
Miscellaneous
|
||||
+++++++++++++
|
||||
|
|
|
@ -100,7 +100,7 @@ Quick Start
|
|||
>>> element_by_symbol = bidict({'H': 'hydrogen'})
|
||||
>>> element_by_symbol['H']
|
||||
'hydrogen'
|
||||
>>> element_by_symbol.inv['hydrogen']
|
||||
>>> element_by_symbol.inverse['hydrogen']
|
||||
'H'
|
||||
|
||||
|
||||
|
|
|
@ -29,11 +29,20 @@
|
|||
"""
|
||||
Efficient, Pythonic bidirectional map implementation and related functionality.
|
||||
|
||||
.. note::
|
||||
.. code-block:: python
|
||||
|
||||
>>> from bidict import bidict
|
||||
>>> element_by_symbol = bidict({'H': 'hydrogen'})
|
||||
>>> element_by_symbol['H']
|
||||
'hydrogen'
|
||||
>>> element_by_symbol.inverse['hydrogen']
|
||||
'H'
|
||||
|
||||
|
||||
Please see https://github.com/jab/bidict for the most up-to-date code and
|
||||
https://bidict.readthedocs.io for the most up-to-date documentation
|
||||
if you are reading this elsewhere.
|
||||
|
||||
If you are reading this elsewhere,
|
||||
please see https://bidict.readthedocs.io for the most up-to-date documentation,
|
||||
and https://github.com/jab/bidict for the most up-to-date code.
|
||||
|
||||
.. :copyright: (c) 2019 Joshua Bronson.
|
||||
.. :license: MPLv2. See LICENSE for details.
|
||||
|
|
|
@ -35,41 +35,41 @@ class BidirectionalMapping(Mapping): # pylint: disable=abstract-method,no-init
|
|||
"""Abstract base class (ABC) for bidirectional mapping types.
|
||||
|
||||
Extends :class:`collections.abc.Mapping` primarily by adding the
|
||||
(abstract) :attr:`inv` property,
|
||||
(abstract) :attr:`inverse` property,
|
||||
which implementors of :class:`BidirectionalMapping`
|
||||
should override to return a reference to the inverse
|
||||
:class:`BidirectionalMapping` instance.
|
||||
|
||||
Implements :attr:`__subclasshook__` such that any
|
||||
:class:`~collections.abc.Mapping` that also provides
|
||||
:attr:`~BidirectionalMapping.inv`
|
||||
:attr:`~BidirectionalMapping.inverse`
|
||||
will be considered a (virtual) subclass of this ABC.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
@abstractproperty
|
||||
def inv(self):
|
||||
def inverse(self):
|
||||
"""The inverse of this bidirectional mapping instance.
|
||||
|
||||
*See also* :attr:`bidict.BidictBase.inv`
|
||||
*See also* :attr:`bidict.BidictBase.inverse`, :attr:`bidict.BidictBase.inv`
|
||||
|
||||
:raises NotImplementedError: Meant to be overridden in subclasses.
|
||||
"""
|
||||
# The @abstractproperty decorator prevents BidirectionalMapping subclasses from being
|
||||
# instantiated unless they override this method. So users shouldn't be able to get to the
|
||||
# point where they can unintentionally call this implementation of .inv on something
|
||||
# point where they can unintentionally call this implementation of .inverse on something
|
||||
# anyway. Could leave the method body empty, but raise NotImplementedError so it's extra
|
||||
# clear there's no reason to call this implementation (e.g. via super() after overriding).
|
||||
raise NotImplementedError
|
||||
|
||||
def __inverted__(self):
|
||||
"""Get an iterator over the items in :attr:`inv`.
|
||||
"""Get an iterator over the items in :attr:`inverse`.
|
||||
|
||||
This is functionally equivalent to iterating over the items in the
|
||||
forward mapping and inverting each one on the fly, but this provides a
|
||||
more efficient implementation: Assuming the already-inverted items
|
||||
are stored in :attr:`inv`, just return an iterator over them directly.
|
||||
are stored in :attr:`inverse`, just return an iterator over them directly.
|
||||
|
||||
Providing this default implementation enables external functions,
|
||||
particularly :func:`~bidict.inverted`, to use this optimized
|
||||
|
@ -77,12 +77,12 @@ class BidirectionalMapping(Mapping): # pylint: disable=abstract-method,no-init
|
|||
|
||||
*See also* :func:`bidict.inverted`
|
||||
"""
|
||||
return iteritems(self.inv)
|
||||
return iteritems(self.inverse)
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, C): # noqa: N803 (argument name should be lowercase)
|
||||
"""Check if *C* is a :class:`~collections.abc.Mapping`
|
||||
that also provides an ``inv`` attribute,
|
||||
that also provides an ``inverse`` attribute,
|
||||
thus conforming to the :class:`BidirectionalMapping` interface,
|
||||
in which case it will be considered a (virtual) C
|
||||
even if it doesn't explicitly extend it.
|
||||
|
@ -94,7 +94,7 @@ class BidirectionalMapping(Mapping): # pylint: disable=abstract-method,no-init
|
|||
mro = getattr(C, '__mro__', None)
|
||||
if mro is None: # Python 2 old-style class
|
||||
return NotImplemented
|
||||
if not any(B.__dict__.get('inv') for B in mro):
|
||||
if not any(B.__dict__.get('inverse') for B in mro):
|
||||
return NotImplemented
|
||||
return True
|
||||
|
||||
|
|
|
@ -45,9 +45,9 @@ from .compat import PY2, KeysView, ItemsView, Mapping, iteritems
|
|||
# provides all the required attributes that the __subclasshook__ checks for,
|
||||
# BidictBase would be a (virtual) subclass of BidirectionalMapping even if
|
||||
# it didn't subclass it explicitly. But subclassing BidirectionalMapping
|
||||
# explicitly allows BidictBase to inherit any useful methods that
|
||||
# explicitly allows BidictBase to inherit any useful implementations that
|
||||
# BidirectionalMapping provides that aren't part of the required interface,
|
||||
# such as its __inverted__ implementation.
|
||||
# such as its `__inverted__` implementation and `inverse` alias.
|
||||
|
||||
class BidictBase(BidirectionalMapping):
|
||||
"""Base class implementing :class:`BidirectionalMapping`."""
|
||||
|
@ -123,7 +123,7 @@ class BidictBase(BidirectionalMapping):
|
|||
inv._invm = self._fwdm # pylint: disable=protected-access
|
||||
# Only give the inverse a weak reference to this bidict to avoid creating a reference cycle,
|
||||
# stored in the _invweak attribute. See also the docs in
|
||||
# :ref:`addendum:\:attr\:\`~bidict.BidictBase.inv\` Avoids Reference Cycles`
|
||||
# :ref:`addendum:Bidict Avoids Reference Cycles`
|
||||
inv._inv = None # pylint: disable=protected-access
|
||||
inv._invweak = ref(self) # pylint: disable=protected-access
|
||||
# Since this bidict has a strong reference to its inverse already, set its _invweak to None.
|
||||
|
@ -148,8 +148,11 @@ class BidictBase(BidirectionalMapping):
|
|||
return self._inv is None
|
||||
|
||||
@property
|
||||
def inv(self):
|
||||
"""The inverse of this bidict."""
|
||||
def inverse(self):
|
||||
"""The inverse of this bidict.
|
||||
|
||||
*See also* :attr:`inv`
|
||||
"""
|
||||
# Resolve and return a strong reference to the inverse bidict.
|
||||
# One may be stored in self._inv already.
|
||||
if self._inv is not None:
|
||||
|
@ -162,6 +165,11 @@ class BidictBase(BidirectionalMapping):
|
|||
self._init_inv() # Now this bidict will retain a strong ref to its inverse.
|
||||
return self._inv
|
||||
|
||||
@property
|
||||
def inv(self):
|
||||
"""Alias for :attr:`inverse`."""
|
||||
return self.inverse
|
||||
|
||||
def __getstate__(self):
|
||||
"""Needed to enable pickling due to use of :attr:`__slots__` and weakrefs.
|
||||
|
||||
|
@ -417,7 +425,7 @@ class BidictBase(BidirectionalMapping):
|
|||
which has the advantages of constant-time containment checks
|
||||
and supporting set operations.
|
||||
"""
|
||||
return self.inv.keys()
|
||||
return self.inverse.keys()
|
||||
|
||||
if PY2:
|
||||
# For iterkeys and iteritems, inheriting from Mapping already provides
|
||||
|
@ -425,13 +433,13 @@ class BidictBase(BidirectionalMapping):
|
|||
|
||||
def itervalues(self):
|
||||
"""An iterator over the contained values."""
|
||||
return self.inv.iterkeys()
|
||||
return self.inverse.iterkeys()
|
||||
|
||||
def viewkeys(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self): # noqa: D102; pylint: disable=missing-docstring
|
||||
return self.inv.viewkeys()
|
||||
return self.inverse.viewkeys()
|
||||
|
||||
viewvalues.__doc__ = values.__doc__
|
||||
values.__doc__ = 'A list of the contained values.'
|
||||
|
|
|
@ -24,7 +24,7 @@ def namedbidict(typename, keyname, valname, base_type=bidict):
|
|||
The new class's ``__name__`` will be set to *typename*.
|
||||
|
||||
Instances of it will provide access to their
|
||||
:attr:`inverse <BidirectionalMapping.inv>`\s
|
||||
:attr:`inverse <BidirectionalMapping.inverse>`\s
|
||||
via the custom *keyname*\_for property,
|
||||
and access to themselves
|
||||
via the custom *valname*\_for property.
|
||||
|
@ -60,10 +60,10 @@ def namedbidict(typename, keyname, valname, base_type=bidict):
|
|||
__slots__ = ()
|
||||
|
||||
def _getfwd(self):
|
||||
return self.inv if self._isinv else self
|
||||
return self.inverse if self._isinv else self
|
||||
|
||||
def _getinv(self):
|
||||
return self if self._isinv else self.inv
|
||||
return self if self._isinv else self.inverse
|
||||
|
||||
@property
|
||||
def _keyname(self):
|
||||
|
|
|
@ -163,7 +163,7 @@ class OrderedBidictBase(BidictBase):
|
|||
|
||||
def _init_inv(self):
|
||||
super(OrderedBidictBase, self)._init_inv()
|
||||
self.inv._sntl = self._sntl # pylint: disable=protected-access
|
||||
self.inverse._sntl = self._sntl # pylint: disable=protected-access
|
||||
|
||||
# Can't reuse BidictBase.copy since ordered bidicts have different internal structure.
|
||||
def copy(self):
|
||||
|
@ -188,12 +188,12 @@ class OrderedBidictBase(BidictBase):
|
|||
|
||||
def __getitem__(self, key):
|
||||
nodefwd = self._fwdm[key]
|
||||
val = self._invm.inv[nodefwd]
|
||||
val = self._invm.inverse[nodefwd]
|
||||
return val
|
||||
|
||||
def _pop(self, key):
|
||||
nodefwd = self._fwdm.pop(key)
|
||||
val = self._invm.inv.pop(nodefwd)
|
||||
val = self._invm.inverse.pop(nodefwd)
|
||||
nodefwd.prv.nxt = nodefwd.nxt
|
||||
nodefwd.nxt.prv = nodefwd.prv
|
||||
return val
|
||||
|
@ -221,8 +221,8 @@ class OrderedBidictBase(BidictBase):
|
|||
elif isdupkey and isdupval:
|
||||
# Key and value duplication across two different nodes.
|
||||
assert nodefwd is not nodeinv
|
||||
oldval = invm.inv[nodefwd]
|
||||
oldkey = fwdm.inv[nodeinv]
|
||||
oldval = invm.inverse[nodefwd]
|
||||
oldkey = fwdm.inverse[nodeinv]
|
||||
assert oldkey != key
|
||||
assert oldval != val
|
||||
# We have to collapse nodefwd and nodeinv into a single node, i.e. drop one of them.
|
||||
|
@ -238,13 +238,13 @@ class OrderedBidictBase(BidictBase):
|
|||
assert tmp is nodefwd
|
||||
fwdm[key] = invm[val] = nodefwd
|
||||
elif isdupkey:
|
||||
oldval = invm.inv[nodefwd]
|
||||
oldval = invm.inverse[nodefwd]
|
||||
oldkey = _MISS
|
||||
oldnodeinv = invm.pop(oldval)
|
||||
assert oldnodeinv is nodefwd
|
||||
invm[val] = nodefwd
|
||||
else: # isdupval
|
||||
oldkey = fwdm.inv[nodeinv]
|
||||
oldkey = fwdm.inverse[nodeinv]
|
||||
oldval = _MISS
|
||||
oldnodefwd = fwdm.pop(oldkey)
|
||||
assert oldnodefwd is nodeinv
|
||||
|
@ -278,7 +278,7 @@ class OrderedBidictBase(BidictBase):
|
|||
"""An iterator over this bidict's items in order."""
|
||||
fwdm = self._fwdm
|
||||
for node in self._sntl.__iter__(reverse=reverse):
|
||||
yield fwdm.inv[node]
|
||||
yield fwdm.inverse[node]
|
||||
|
||||
def __reversed__(self):
|
||||
"""An iterator over this bidict's items in reverse order."""
|
||||
|
|
|
@ -34,8 +34,8 @@ A careful reader might notice the following...
|
|||
.. doctest::
|
||||
|
||||
>>> fwd = bidict(one=1)
|
||||
>>> inv = fwd.inv
|
||||
>>> inv.inv is fwd
|
||||
>>> inv = fwd.inverse
|
||||
>>> inv.inverse is fwd
|
||||
True
|
||||
|
||||
...and become concerned that a bidict and its inverse create a reference cycle.
|
||||
|
@ -184,7 +184,7 @@ in its own inverse:
|
|||
>>> b.forceput('FALSE', False)
|
||||
>>> b
|
||||
bidict({'FALSE': False})
|
||||
>>> b.inv
|
||||
>>> b.inverse
|
||||
bidict({0: 'FALSE'})
|
||||
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ Let's return to the example from the :doc:`intro`:
|
|||
|
||||
As we saw, this behaves just like a dict,
|
||||
but maintains a special
|
||||
:attr:`~bidict.BidictBase.inv` attribute
|
||||
:attr:`~bidict.BidictBase.inverse` attribute
|
||||
giving access to inverse items:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> element_by_symbol.inv['helium'] = 'He'
|
||||
>>> del element_by_symbol.inv['hydrogen']
|
||||
>>> element_by_symbol.inverse['helium'] = 'He'
|
||||
>>> del element_by_symbol.inverse['hydrogen']
|
||||
>>> element_by_symbol
|
||||
bidict({'He': 'helium'})
|
||||
|
||||
|
@ -40,9 +40,9 @@ as well:
|
|||
>>> element_by_symbol.update(Hg='mercury')
|
||||
>>> element_by_symbol
|
||||
bidict({'Hg': 'mercury'})
|
||||
>>> 'mercury' in element_by_symbol.inv
|
||||
>>> 'mercury' in element_by_symbol.inverse
|
||||
True
|
||||
>>> element_by_symbol.inv.pop('mercury')
|
||||
>>> element_by_symbol.inverse.pop('mercury')
|
||||
'Hg'
|
||||
|
||||
Because inverse items are maintained alongside forward items,
|
||||
|
|
|
@ -115,8 +115,8 @@ creating a sorted bidict type is dead simple:
|
|||
>>> list(b.items())
|
||||
[('Cairo', 'Egypt'), ('Lima', 'Peru'), ('Tokyo', 'Japan')]
|
||||
|
||||
>>> # b.inv stays sorted by *its* keys (b's values)
|
||||
>>> list(b.inv.items())
|
||||
>>> # b.inverse stays sorted by *its* keys (b's values)
|
||||
>>> list(b.inverse.items())
|
||||
[('Egypt', 'Cairo'), ('Japan', 'Tokyo'), ('Peru', 'Lima')]
|
||||
|
||||
|
||||
|
@ -140,32 +140,32 @@ will yield their items in *the same* order:
|
|||
>>> element_by_atomic_number
|
||||
KeySortedBidict([(1, 'hydrogen'), (2, 'helium'), (3, 'lithium')])
|
||||
|
||||
>>> # .inv stays sorted by value:
|
||||
>>> list(element_by_atomic_number.inv.items())
|
||||
>>> # .inverse stays sorted by value:
|
||||
>>> list(element_by_atomic_number.inverse.items())
|
||||
[('hydrogen', 1), ('helium', 2), ('lithium', 3)]
|
||||
|
||||
>>> element_by_atomic_number[4] = 'beryllium'
|
||||
|
||||
>>> list(element_by_atomic_number.inv.items())
|
||||
>>> list(element_by_atomic_number.inverse.items())
|
||||
[('hydrogen', 1), ('helium', 2), ('lithium', 3), ('beryllium', 4)]
|
||||
|
||||
>>> # This works because a bidict whose _fwdm_cls differs from its _invm_cls computes
|
||||
>>> # its inverse class -- which (note) is not actually the same class as the original,
|
||||
>>> # as it needs to have its _fwdm_cls and _invm_cls swapped -- automatically.
|
||||
>>> # You can see this if you inspect the inverse bidict:
|
||||
>>> element_by_atomic_number.inv # Note the different class, which was auto-generated:
|
||||
>>> element_by_atomic_number.inverse # Note the different class, which was auto-generated:
|
||||
KeySortedBidictInv([('hydrogen', 1), ('helium', 2), ('lithium', 3), ('beryllium', 4)])
|
||||
>>> ValueSortedBidict = element_by_atomic_number.inv.__class__
|
||||
>>> ValueSortedBidict = element_by_atomic_number.inverse.__class__
|
||||
>>> ValueSortedBidict._fwdm_cls
|
||||
<class 'sortedcollections.recipes.ValueSortedDict'>
|
||||
>>> ValueSortedBidict._invm_cls
|
||||
<class 'sortedcontainers.sorteddict.SortedDict'>
|
||||
|
||||
>>> # Round trips work as expected:
|
||||
>>> atomic_number_by_element = ValueSortedBidict(element_by_atomic_number.inv)
|
||||
>>> atomic_number_by_element = ValueSortedBidict(element_by_atomic_number.inverse)
|
||||
>>> atomic_number_by_element
|
||||
KeySortedBidictInv([('hydrogen', 1), ('helium', 2), ('lithium', 3), ('beryllium', 4)])
|
||||
>>> KeySortedBidict(atomic_number_by_element.inv) == element_by_atomic_number
|
||||
>>> KeySortedBidict(atomic_number_by_element.inverse) == element_by_atomic_number
|
||||
True
|
||||
|
||||
>>> # One other useful trick:
|
||||
|
@ -182,7 +182,7 @@ will yield their items in *the same* order:
|
|||
>>> # bidict has no .peekitem attr, so the call is passed through to _fwdm:
|
||||
>>> element_by_atomic_number.peekitem()
|
||||
(4, 'beryllium')
|
||||
>>> element_by_atomic_number.inv.peekitem()
|
||||
>>> element_by_atomic_number.inverse.peekitem()
|
||||
('beryllium', 4)
|
||||
|
||||
|
||||
|
|
|
@ -26,13 +26,13 @@ It implements the familiar API you're used to from dict:
|
|||
'hydrogen'
|
||||
|
||||
But it also maintains the inverse bidict via the
|
||||
:attr:`~bidict.BidictBase.inv` attribute:
|
||||
:attr:`~bidict.BidictBase.inverse` attribute:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> element_by_symbol.inv
|
||||
>>> element_by_symbol.inverse
|
||||
bidict({'hydrogen': 'H'})
|
||||
>>> element_by_symbol.inv['hydrogen']
|
||||
>>> element_by_symbol.inverse['hydrogen']
|
||||
'H'
|
||||
|
||||
Concise, efficient, Pythonic.
|
||||
|
@ -96,7 +96,7 @@ leaving us with just what we wanted:
|
|||
>>> m
|
||||
bidict({'a': 'b'})
|
||||
|
||||
>>> m.inv
|
||||
>>> m.inverse
|
||||
bidict({'b': 'a'})
|
||||
|
||||
|
||||
|
|
|
@ -256,10 +256,12 @@ API Design
|
|||
In the face of ambiguity, refuse the temptation to guess."
|
||||
→ bidict's default duplication policies
|
||||
|
||||
- "Explicit is better than implicit.
|
||||
There should be one—and preferably only one—obvious way to do it."
|
||||
→ dropped the alternate ``.inv`` APIs that used
|
||||
the ``~`` operator and the old slice syntax
|
||||
- "Readability counts."
|
||||
"There should be one – and preferably only one – obvious way to do it."
|
||||
→ an early version of bidict allowed using the ``~`` operator to access ``.inverse``
|
||||
and a special slice syntax like ``b[:val]`` to look up a key by value,
|
||||
but these were removed in preference to the more obvious and readable
|
||||
``.inverse``-based spellings.
|
||||
|
||||
|
||||
Portability
|
||||
|
|
|
@ -23,12 +23,12 @@ are subclasses of :class:`bidict.BidirectionalMapping`.
|
|||
This abstract base class
|
||||
extends :class:`collections.abc.Mapping`
|
||||
by adding the
|
||||
":attr:`~bidict.BidirectionalMapping.inv`"
|
||||
":attr:`~bidict.BidirectionalMapping.inverse`"
|
||||
:obj:`~abc.abstractproperty`. [#fn-subclasshook]_
|
||||
|
||||
.. [#fn-subclasshook]
|
||||
In fact, any :class:`collections.abc.Mapping`
|
||||
that provides an ``inv`` attribute
|
||||
that provides an ``inverse`` attribute
|
||||
will be considered a virtual subclass of
|
||||
:class:`bidict.BidirectionalMapping`
|
||||
:meth:`automatically <bidict.BidirectionalMapping.__subclasshook__>`,
|
||||
|
@ -89,7 +89,7 @@ It's like a bidirectional version of :class:`collections.OrderedDict`.
|
|||
>>> element_by_symbol = OrderedBidict([
|
||||
... ('H', 'hydrogen'), ('He', 'helium'), ('Li', 'lithium')])
|
||||
|
||||
>>> element_by_symbol.inv
|
||||
>>> element_by_symbol.inverse
|
||||
OrderedBidict([('hydrogen', 'H'), ('helium', 'He'), ('lithium', 'Li')])
|
||||
|
||||
>>> first, second, third = element_by_symbol.values()
|
||||
|
@ -229,7 +229,7 @@ with an order-preserving :class:`dict` version of Python:
|
|||
>>> b[2] = 'UPDATED'
|
||||
>>> b
|
||||
bidict({1: -1, 2: 'UPDATED', 3: -3})
|
||||
>>> b.inv # oops:
|
||||
>>> b.inverse # oops:
|
||||
bidict({-1: 1, -3: 3, 'UPDATED': 2})
|
||||
|
||||
When the value associated with the key ``2`` was changed,
|
||||
|
|
|
@ -338,6 +338,13 @@ def test_slots(bi_cls):
|
|||
cls_by_slot[slot] = cls
|
||||
|
||||
|
||||
@given(st.BIDICTS)
|
||||
def test_inv_aliases_inverse(bi):
|
||||
"""bi.inv should alias bi.inverse."""
|
||||
assert bi.inverse is bi.inv
|
||||
assert bi.inv.inverse is bi.inverse.inv
|
||||
|
||||
|
||||
@given(st.BIDICTS)
|
||||
def test_pickle_roundtrips(bi):
|
||||
"""A bidict should equal the result of unpickling its pickle."""
|
||||
|
|
|
@ -32,17 +32,17 @@ class VirtualBimapSubclass(Mapping): # pylint: disable=abstract-method
|
|||
but doesn't need to be for the purposes of this test.)
|
||||
"""
|
||||
|
||||
inv = NotImplemented
|
||||
inverse = NotImplemented
|
||||
|
||||
|
||||
class AbstractBimap(BidirectionalMapping): # pylint: disable=abstract-method
|
||||
"""Dummy type that explicitly extends BidirectionalMapping
|
||||
but fails to provide a concrete implementation for the
|
||||
:attr:`BidirectionalMapping.inv` :func:`abc.abstractproperty`.
|
||||
:attr:`BidirectionalMapping.inverse` :func:`abc.abstractproperty`.
|
||||
|
||||
As a result, attempting to create an instance of this class
|
||||
should result in ``TypeError: Can't instantiate abstract class
|
||||
AbstractBimap with abstract methods inv``
|
||||
AbstractBimap with abstract methods inverse``
|
||||
"""
|
||||
|
||||
__getitem__ = NotImplemented
|
||||
|
@ -134,9 +134,9 @@ def test_abstract_bimap_init_fails():
|
|||
AbstractBimap() # pylint: disable=abstract-class-instantiated
|
||||
|
||||
|
||||
def test_bimap_inv_notimplemented():
|
||||
"""Calling .inv on a BidirectionalMapping should raise :class:`NotImplementedError`."""
|
||||
def test_bimap_inverse_notimplemented():
|
||||
"""Calling .inverse on a BidirectionalMapping should raise :class:`NotImplementedError`."""
|
||||
with pytest.raises(NotImplementedError):
|
||||
# Can't instantiate a BidirectionalMapping that hasn't overridden the abstract methods of
|
||||
# the interface, so only way to call this implementation is on the class.
|
||||
BidirectionalMapping.inv.fget(bidict()) # pylint: disable=no-member
|
||||
BidirectionalMapping.inverse.fget(bidict()) # pylint: disable=no-member
|
||||
|
|
Loading…
Reference in New Issue