bidict

The bidirectional mapping library for Python.

bidict logo

Status

Latest release Documentation Travis-CI build status Test coverage License Downloads past month

bidict:

  • has been used for many years by several teams at Google, Venmo, CERN, Bank of America Merrill Lynch, Bloomberg, Two Sigma, and others

  • has carefully designed APIs for safety, simplicity, flexibility, and ergonomics

  • is fast, lightweight, and has no runtime dependencies other than Python’s standard library

  • integrates natively with Python’s collections interfaces

  • is implemented in concise, well-factored, pure (PyPy-compatible) Python code optimized both for reading and learning from 1 as well as for running efficiently

  • has extensive docs and test coverage (including property-based tests and benchmarks) run continuously on all supported Python versions and OSes

Note: Python 3 Required

As promised in the 0.18.2 release (see Changelog 2), Python 2 is no longer supported. Version 0.18.3 is the last release of bidict that supports Python 2. This makes bidict more efficient on Python 3 and enables further improvement to bidict in the future. See python3statement.org for more info.

Installation

pip install bidict

Quick Start

>>> from bidict import bidict
>>> element_by_symbol = bidict({'H': 'hydrogen'})
>>> element_by_symbol['H']
'hydrogen'
>>> element_by_symbol.inverse['hydrogen']
'H'

For more usage documentation, head to the Introduction 3 and proceed from there.

Community Support

Chat

If you are thinking of using bidict in your work, or if you have any questions, comments, or suggestions, I’d love to know about your use case and provide as much voluntary support for it as possible.

Please feel free to leave a message in the chatroom or open a new issue on GitHub. You can search through existing issues before creating a new one in case your questions or concerns have been adressed there already.

Enterprise-Grade Support via Tidelift

Paid support available via Tidelift

If your use case requires a greater level of support, enterprise-grade support for bidict can be obtained via the Tidelift subscription.

Notice of Usage

If you use bidict, and especially if your usage or your organization is significant in some way, please let me know.

You can:

Changelog

See the Changelog 2 for a history of notable changes to bidict.

Release Notifications

Follow on libraries.io

Subscribe to releases on GitHub or libraries.io to be notified when new versions of bidict are released.

Learning from bidict

One of the best things about bidict is that it touches a surprising number of interesting Python corners, especially given its small size and scope.

Check out Learning from bidict 1 if you’re interested in learning more.

Contributing

bidict is currently a one-person operation maintained on a voluntary basis.

Your help would be most welcome!

Reviewers Wanted!

One of the most valuable ways to contribute to bidict – and to explore some interesting Python corners 1 while you’re at it – is to review the relatively small codebase.

Please create an issue or pull request with any improvements you’d propose or any other results you found. Submitting a draft PR with feedback in inline code comments, or a “Review results” issue, would each work well.

You can also +1 this issue to sign up to give feedback on future proposed changes that are in need of a reviewer.

Giving Back

bidict is the product of hundreds of hours of unpaid, voluntary work.

If bidict has helped you accomplish your work, especially work you’ve been paid for, please consider chipping in toward the costs of its maintenance and development and/or ask your organization to do the same.

Support bidict

Finding Documentation

If you’re viewing this on https://bidict.readthedocs.io, note that multiple versions of the documentation are available, and you can choose a different version using the popup menu at the bottom-right. Please make sure you’re viewing the version of the documentation that corresponds to the version of bidict you’d like to use.

If you’re viewing this on GitHub, PyPI, or some other place that can’t render and link this documentation properly and are seeing broken links, try these alternate links instead:

1(1,2,3)

docs/learning-from-bidict.rst | https://bidict.readthedocs.io/learning-from-bidict.html

2(1,2)

CHANGELOG.rst | https://bidict.readthedocs.io/changelog.html

3(1,2)

docs/intro.rst | https://bidict.readthedocs.io/intro.html


Next: Introduction 3

Introduction

The bidict library provides several friendly, efficient data structures for working with bidirectional mappings in Python.

bidict.bidict

bidict.bidict is the main bidirectional mapping data structure provided. It allows looking up the value associated with a key, just like a dict:

>>> element_by_symbol = bidict({'H': 'hydrogen'})
>>> element_by_symbol['H']
'hydrogen'

But it also allows looking up the key associated with a value, via the special inverse attribute:

>>> element_by_symbol.inverse['hydrogen']
'H'

The inverse attribute actually references the entire inverse bidirectional mapping:

>>> element_by_symbol
bidict({'H': 'hydrogen'})
>>> element_by_symbol.inverse
bidict({'hydrogen': 'H'})

…which is automatically kept in sync as the original mapping is updated:

>>> element_by_symbol['H'] = 'hydrogène'
>>> element_by_symbol.inverse
bidict({'hydrogène': 'H'})

If you’re used to working with dicts, you’ll feel right at home using bidict:

>>> dir(element_by_symbol)
[..., '__getitem__', ..., '__setitem__', ..., 'items', 'keys', ...]

Familiar, concise, Pythonic.

Why can’t I just use a dict?

A skeptic writes:

If I want a mapping associating a → b and b → a, I can just create the dict {a: b, b: a}. Why bother using bidict?

One answer is better ergonomics for maintaining a correct representation. For example, to get the correct length, you’d have to take the number reported by len() and cut it in half.

But now consider what happens when we need to store a new association, and we try to do so naively:

>>> el_by_sym = {'H': 'hydrogen', 'hydrogen': 'H'}
>>> # Later we need to associate 'H' with a different value
>>> el_by_sym.update({'H': 'hydrogène', 'hydrogène': 'H'}  # Too naive

Here is what we’re left with:

>>> el_by_sym
{'H': 'hydrogène', 'hydrogène': 'H', 'hydrogen': 'H'}

Oops.

We forgot to look up whether the key and value we wanted to set already had any previous associations and remove them as necessary.

In general, if we want to store the assocation k ⟷ v, but we may have already stored the associations k ⟷ V or K ⟷ v, a correct implementation using the single dict approach would require code like this:

>>> d = {'k': 'V', 'V': 'k'}

>>> def update(d, key, val):
...     _sentinel = object()
...     oldval = d.pop(key, _sentinel)
...     d.pop(oldval, None)
...     oldkey = d.pop(val, _sentinel)
...     d.pop(oldkey, None)
...     d.update({key: val, val: key})

>>> update(d, 'k', 'v')
>>> d == {'k': 'v', 'v': 'k'}
True

With bidict, we can instead just write:

>>> b = bidict({'k': 'V'})
>>> b['k'] = 'v'

And bidict takes care of all the fussy details, leaving us with just what we wanted:

>>> b
bidict({'k': 'v'})

>>> b.inverse
bidict({'v': 'k'})

Even more important…

Beyond this, consider what would happen if we needed to work with just the keys, values, or items that we have associated.

Since the single-dict approach inserts values as keys into the same dict that it inserts keys into, we’d never be able to tell our keys and values apart.

So iterating over the keys would also yield the values (and vice versa), with no way to tell which was which.

Iterating over the items would yield twice as many as we wanted, with a (v, k) item that we’d have to ignore for each (k, v) item that we expect, and no way to tell which was which.

>>> # Compare this...
>>> sorted(d.keys())    # also gives values
['k', 'v']
>>> sorted(d.values())  # also gives keys
['k', 'v']

>>> # ...to this:
>>> sorted(b.keys())    # just the keys
['k']
>>> sorted(b.values())  # just the values
['v']

In short, to model a bidirectional mapping, we need two separate one-directional mappings, one for the forward associations and one for the inverse, that are kept in sync as the associations change.

This is exactly what bidict does under the hood, abstracting it into a clean and ergonomic interface.

bidict’s APIs also provide power, flexibility, and safety, making sure the one-to-one invariant is maintained and inverse mappings are kept consistent, while also helping make sure you don’t accidentally shoot yourself in the foot.

Additional Functionality

Besides the standard bidict.bidict type, the bidict module provides other bidirectional mapping variants: frozenbidict, OrderedBidict FrozenOrderedBidict, and namedbidict(). These and remaining functionality will be covered in later sections.

But first, let’s look at a few more details of Basic Usage.

Basic Usage

Let’s return to the example from the Introduction:

>>> element_by_symbol = bidict(H='hydrogen')

As we saw, this behaves just like a dict, but maintains a special inverse attribute giving access to inverse items:

>>> element_by_symbol.inverse['helium'] = 'He'
>>> del element_by_symbol.inverse['hydrogen']
>>> element_by_symbol
bidict({'He': 'helium'})

bidict.bidict supports the rest of the collections.abc.MutableMapping interface as well:

>>> 'C' in element_by_symbol
False
>>> element_by_symbol.get('C', 'carbon')
'carbon'
>>> element_by_symbol.pop('He')
'helium'
>>> element_by_symbol
bidict()
>>> element_by_symbol.update(Hg='mercury')
>>> element_by_symbol
bidict({'Hg': 'mercury'})
>>> 'mercury' in element_by_symbol.inverse
True
>>> element_by_symbol.inverse.pop('mercury')
'Hg'

Because inverse items are maintained alongside forward items, referencing a bidict’s inverse is always a constant-time operation.

Values Must Be Hashable

Because you must be able to look up keys by value as well as values by key, values must also be hashable.

Attempting to insert an unhashable value will result in an error:

>>> anagrams_by_alphagram = dict(opt=['opt', 'pot', 'top'])
>>> bidict(anagrams_by_alphagram)
Traceback (most recent call last):
...
TypeError: ...

So in this example, using a tuple or a frozenset instead of a list would do the trick:

>>> bidict(opt=('opt', 'pot', 'top'))
bidict({'opt': ('opt', 'pot', 'top')})

Values Must Be Unique

As we know, in a bidirectional map, not only must keys be unique, but values must be unique as well. This has immediate implications for bidict’s API.

Consider the following:

>>> b = bidict({'one': 1})
>>> b['two'] = 1  

What should happen next?

If the bidict allowed this to succeed, because of the uniqueness-of-values constraint, it would silently clobber the existing item, resulting in:

>>> b  
bidict({'two': 1})

This could result in surprises or problems down the line.

Instead, bidict raises a ValueDuplicationError so you have an opportunity to catch this early and resolve the conflict before it causes problems later on:

>>> b['two'] = 1
Traceback (most recent call last):
    ...
ValueDuplicationError: 1

The purpose of this is to be more in line with the Zen of Python, which advises,

Errors should never pass silently.
Unless explicitly silenced.

So if you really just want to clobber any existing items, all you have to do is say so explicitly:

>>> b.forceput('two', 1)
>>> b
bidict({'two': 1})

Similarly, initializations and update() calls that would overwrite the key of an existing value raise an exception too:

>>> bidict({'one': 1, 'uno': 1})
Traceback (most recent call last):
    ...
ValueDuplicationError: 1

>>> b = bidict({'one': 1})
>>> b.update([('uno', 1)])
Traceback (most recent call last):
    ...
ValueDuplicationError: 1

>>> b
bidict({'one': 1})

Setting an existing key to a new value does not cause an error, and is considered an intentional overwrite of the value associated with the existing key, in keeping with dict’s behavior:

>>> b = bidict({'one': 1})
>>> b['one'] = 2  # succeeds
>>> b
bidict({'one': 2})
>>> b.update([('one', 3), ('one', 4), ('one', 5)])
>>> b
bidict({'one': 5})
>>> bidict([('one', 1), ('one', 2)])
bidict({'one': 2})

In summary, when attempting to insert an item whose key duplicates an existing item’s, bidict’s default behavior is to allow the insertion, overwriting the existing item with the new one. When attempting to insert an item whose value duplicates an existing item’s, bidict’s default behavior is to raise. This design naturally falls out of the behavior of Python’s built-in dict, and protects against unexpected data loss.

One set of alternatives to this behavior is provided by forceput() (mentioned above) and forceupdate(), which allow you to explicitly overwrite existing keys and values:

>>> b = bidict({'one': 1})
>>> b.forceput('two', 1)
>>> b
bidict({'two': 1})

>>> b.forceupdate([('three', 1), ('four', 1)])
>>> b
bidict({'four': 1})

For even more control, you can use put() and putall(). These variants allow you to pass an OnDup instance to specify custom OnDupActions for each type of duplication that can occur.

>>> from bidict import OnDup, RAISE

>>> b = bidict({1: 'one'})
>>> b.put(1, 'uno', OnDup(key=RAISE))
Traceback (most recent call last):
    ...
KeyDuplicationError: 2
>>> b
bidict({1: 'one'})

bidict provides the ON_DUP_DEFAULT, ON_DUP_RAISE, and ON_DUP_DROP_OLD OnDup instances for convenience.

If no on_dup argument is passed, put() and putall() will use ON_DUP_RAISE, providing stricter-by-default alternatives to __setitem__() and update(). (These defaults complement the looser alternatives provided by forceput() and forceupdate().)

Key and Value Duplication

Note that it’s possible for a given item to duplicate the key of one existing item, and the value of another existing item. In the following example, the key of the third item duplicates the first item’s key, and the value of the third item dulicates the second item’s value:

>>> b.putall([(1, 2), (3, 4), (1, 4)], OnDup(key=...))

What should happen next?

Keep in mind, the active OnDup may specify one OnDupAction for key duplication and a different OnDupAction for value duplication.

To account for this, OnDup allows you to use its kv field to indicate how you want to handle this case without ambiguity:

>>> from bidict import DROP_OLD
>>> on_dup = OnDup(key=DROP_OLD, val=RAISE, kv=RAISE)
>>> b.putall([(1, 2), (3, 4), (1, 4)], on_dup)
Traceback (most recent call last):
    ...
KeyAndValueDuplicationError: (1, 4)

If not specified, kv defaults to whatever was provided for val.

Note that repeated insertions of the same item are construed as a no-op and will not raise, no matter what the active OnDup is:

>>> b = bidict({1: 'one'})
>>> b.put(1, 'one')  # no-op, not a DuplicationError
>>> b.putall([(2, 'two'), (2, 'two')])  # The repeat (2, 'two') is also a no-op.
>>> sorted(b.items())
[(1, 'one'), (2, 'two')]

See the YoloBidict Recipe for another way to customize this behavior.

Updates Fail Clean

If an update to a bidict fails, you can be sure that it fails clean. In other words, a bidict will never apply only part of an update that ultimately fails, without restoring itself to the state it was in before processing the update:

>>> b = bidict({1: 'one', 2: 'two'})
>>> b.putall([(3, 'three'), (1, 'uno')])
Traceback (most recent call last):
    ...
KeyDuplicationError: 1

>>> # (1, 'uno') was the problem...
>>> b  # ...but (3, 'three') was not added either:
bidict({1: 'one', 2: 'two'})

Order Matters

Performing a bulk insert operation – i.e. passing multiple items to __init__(), update(), forceupdate(), or putall() – is like inserting each of those items individually in sequence. 1

Therefore, the order of the items provided to the bulk insert operation is significant to the result:

>>> b = bidict({0: 0, 1: 2})
>>> b.forceupdate([(2, 0), (0, 1), (0, 0)])

>>> # 1. (2, 0) overwrites (0, 0)             -> bidict({2: 0, 1: 2})
>>> # 2. (0, 1) is added                      -> bidict({2: 0, 1: 2, 0: 1})
>>> # 3. (0, 0) overwrites (0, 1) and (2, 0)  -> bidict({0: 0, 1: 2})

>>> sorted(b.items())
[(0, 0), (1, 2)]

>>> b = bidict({0: 0, 1: 2})  # as before
>>> # Give the same items to forceupdate() but in a different order:
>>> b.forceupdate([(0, 1), (0, 0), (2, 0)])

>>> # 1. (0, 1) overwrites (0, 0)             -> bidict({0: 1, 1: 2})
>>> # 2. (0, 0) overwrites (0, 1)             -> bidict({0: 0, 1: 2})
>>> # 3. (2, 0) overwrites (0, 0)             -> bidict({1: 2, 2: 0})

>>> sorted(b.items())  # different items!
[(1, 2), (2, 0)]
1

Albeit with the extremely important advantage of failing clean.

Interop

bidicts interoperate well with other types of mappings. For example, they support (efficient) polymorphic equality testing:

>>> bidict(a=1) == dict(a=1)
True

And converting back and forth works as expected (assuming no value duplication):

>>> dict(bidict(a=1))
{'a': 1}
>>> bidict(dict(a=1))
bidict({'a': 1})

See the Polymorphism section for more interoperability documentation.


Hopefully bidict feels right at home among the Python built-ins you already know. Proceed to Other bidict Types for documentation on the remaining bidict variants.

Other bidict Types

Now that we’ve covered Basic Usage with the bidict.bidict type, let’s look at some other bidirectional mapping types.

Bidict Types Diagram

bidict types diagram

All bidirectional mapping types that bidict provides are subclasses of bidict.BidirectionalMapping. This abstract base class extends collections.abc.Mapping by adding the “inverseabstractproperty. 1

1

In fact, any collections.abc.Mapping that provides an inverse attribute will be considered a virtual subclass of bidict.BidirectionalMapping automatically, enabling interoperability with external implementations.

As you may have noticed, bidict.bidict is also a collections.abc.MutableMapping. But bidict provides immutable bidirectional mapping types as well.

frozenbidict

frozenbidict is an immutable, hashable bidirectional mapping type.

As you would expect, attempting to mutate a frozenbidict causes an error:

>>> from bidict import frozenbidict
>>> f = frozenbidict({'H': 'hydrogen'})
>>> f['C'] = 'carbon'
Traceback (most recent call last):
    ...
TypeError: ...

frozenbidict also implements collections.abc.Hashable, so it’s suitable for insertion into sets or other mappings:

>>> my_set = {f}      # not an error
>>> my_dict = {f: 1}  # also not an error

See the frozenbidict API documentation for more information.

OrderedBidict

bidict.OrderedBidict is a mutable BidirectionalMapping that preserves the order in which its items are inserted. It’s like a bidirectional version of collections.OrderedDict.

>>> from bidict import OrderedBidict
>>> element_by_symbol = OrderedBidict([
...     ('H', 'hydrogen'), ('He', 'helium'), ('Li', 'lithium')])

>>> element_by_symbol.inverse
OrderedBidict([('hydrogen', 'H'), ('helium', 'He'), ('lithium', 'Li')])

>>> first, second, third = element_by_symbol.values()
>>> first, second, third
('hydrogen', 'helium', 'lithium')

>>> # Insert an additional item and verify it now comes last:
>>> element_by_symbol['Be'] = 'beryllium'
>>> last_item = list(element_by_symbol.items())[-1]
>>> last_item
('Be', 'beryllium')

Additional functionality modeled after OrderedDict is provided as well:

>>> element_by_symbol.popitem(last=True)   # Remove the last item
('Be', 'beryllium')
>>> element_by_symbol.popitem(last=False)  # Remove the first item
('H', 'hydrogen')

>>> # Re-adding hydrogen after it's been removed moves it to the end:
>>> element_by_symbol['H'] = 'hydrogen'
>>> element_by_symbol
OrderedBidict([('He', 'helium'), ('Li', 'lithium'), ('H', 'hydrogen')])

>>> # But there's also a `move_to_end` method just for this purpose:
>>> element_by_symbol.move_to_end('Li')
>>> element_by_symbol
OrderedBidict([('He', 'helium'), ('H', 'hydrogen'), ('Li', 'lithium')])

>>> element_by_symbol.move_to_end('H', last=False)  # move to front
>>> element_by_symbol
OrderedBidict([('H', 'hydrogen'), ('He', 'helium'), ('Li', 'lithium')])

As with OrderedDict, updating an existing item preserves its position in the order:

>>> element_by_symbol['He'] = 'updated in place!'
>>> element_by_symbol
OrderedBidict([('H', 'hydrogen'), ('He', 'updated in place!'), ('Li', 'lithium')])

Collapsing overwrites

When setting an item in an ordered bidict whose key duplicates that of an existing item, and whose value duplicates that of a different existing item, the existing item whose value is duplicated will be dropped, and the existing item whose key is duplicated will have its value overwritten in place:

>>> o = OrderedBidict([(1, 2), (3, 4), (5, 6), (7, 8)])
>>> o.forceput(3, 8)  # item with duplicated value (7, 8) is dropped...
>>> o  # and the item with duplicated key (3, 4) is updated in place:
OrderedBidict([(1, 2), (3, 8), (5, 6)])
>>> # (3, 8) took the place of (3, 4), not (7, 8)

>>> o = OrderedBidict([(1, 2), (3, 4), (5, 6), (7, 8)])  # as before
>>> o.forceput(5, 2)  # another example
>>> o
OrderedBidict([(3, 4), (5, 2), (7, 8)])
>>> # (5, 2) took the place of (5, 6), not (1, 2)

__eq__() is order-insensitive

To ensure that equality of bidicts is transitive (and to uphold the Liskov substitution principle), equality tests between an ordered bidict and other mappings are always order-insensitive:

>>> b = bidict([('one', 1), ('two', 2)])
>>> o1 = OrderedBidict([('one', 1), ('two', 2)])
>>> o2 = OrderedBidict([('two', 2), ('one', 1)])
>>> b == o1
True
>>> b == o2
True
>>> o1 == o2
True

For order-sensitive equality tests, use equals_order_sensitive():

>>> o1.equals_order_sensitive(o2)
False
>>> from collections import OrderedDict
>>> od = OrderedDict(o2)
>>> o1.equals_order_sensitive(od)
False

Note that this differs from the behavior of collections.OrderedDict’s __eq__(), by recommendation of Raymond Hettinger (the author) himself. He later said that making OrderedDict’s __eq__() intransitive was a mistake.

What if my Python version has order-preserving dicts?

In PyPy as well as CPython ≥ 3.6, dict preserves insertion order. If you are using one of these versions of Python, you may wonder whether you can get away with using a regular bidict.bidict in places where you need an insertion order-preserving bidirectional mapping.

In general the answer is no, particularly if you need to be able to change existing associations in the bidirectional mapping while preserving order correctly.

Consider this example using a regular bidict with an order-preserving dict version of Python:

 >>> b = bidict([(1, -1), (2, -2), (3, -3)])
 >>> b[2] = 'UPDATED'
 >>> b
 bidict({1: -1, 2: 'UPDATED', 3: -3})
 >>> b.inverse  # oops:
 bidict({-1: 1, -3: 3, 'UPDATED': 2})

When the value associated with the key 2 was changed, the corresponding item stays in place in the forward mapping, but moves to the end of the inverse mapping. Since regular bidicts provide no guarantees about order preservation (which allows for a more efficient implementation), non-order-preserving behavior (as in the example above) is exactly what you get.

If you never mutate a bidict (or are even using a frozenbidict) and you’re running a version of Python with order-preserving dicts, then you’ll find that the order of the items in your bidict and its inverse happens to be preserved. However, you won’t get the additional order-specific APIs (such as move_to_end(), equals_order_sensitive(), and __reversed__() – indeed the lack of a dict.__reversed__ API is what stops us from making FrozenOrderedBidict an alias of frozenbidict on dict-order-preserving Pythons, as this would mean FrozenOrderedBidict.__reversed__() would have to be O(n) in space complexity).

If you need order-preserving behavior guaranteed, then OrderedBidict is your best choice.

FrozenOrderedBidict

FrozenOrderedBidict is an immutable ordered bidict type. It’s like an OrderedBidict without the mutating APIs, or equivalently like an order-preserving frozenbidict.

namedbidict()

bidict.namedbidict(), inspired by collections.namedtuple(), allows you to easily generate a new bidirectional mapping type with custom attribute-based access to forward and inverse mappings:

>>> from bidict import namedbidict
>>> ElementMap = namedbidict('ElementMap', 'symbol', 'name')
>>> noble_gases = ElementMap(He='helium')
>>> noble_gases.name_for['He']
'helium'
>>> noble_gases.symbol_for['helium']
'He'
>>> noble_gases.name_for['Ne'] = 'neon'
>>> del noble_gases.symbol_for['helium']
>>> noble_gases
ElementMap({'Ne': 'neon'})

Using the base_type keyword arg – whose default value is bidict.bidict – you can override the bidict type used as the base class, allowing the creation of e.g. a named frozenbidict type:

>>> ElMap = namedbidict('ElMap', 'symbol', 'name', base_type=frozenbidict)
>>> noble = ElMap(He='helium')
>>> noble.symbol_for['helium']
'He'
>>> hash(noble) is not 'an error'
True
>>> noble['C'] = 'carbon'  # mutation fails
Traceback (most recent call last):
...
TypeError: ...

Polymorphism

(Or: ABCs ftw!)

You may be tempted to write something like isinstance(obj, dict) to check whether obj is a Mapping. However, this check is too specific, and will fail for many types that implement the Mapping interface:

>>> from collections import ChainMap
>>> issubclass(ChainMap, dict)
False

The same is true for all the bidict types:

>>> issubclass(bidict, dict)
False

The proper way to check whether an object is a Mapping is to use the abstract base classes (ABCs) from the collections module that are provided for this purpose:

>>> issubclass(ChainMap, Mapping)
True
>>> isinstance(bidict(), Mapping)
True

Also note that the proper way to check whether an object is an (im)mutable mapping is to use the MutableMapping ABC:

>>> from bidict import BidirectionalMapping

>>> def is_immutable_bimap(obj):
...     return (isinstance(obj, BidirectionalMapping)
...             and not isinstance(obj, MutableMapping))

>>> is_immutable_bimap(bidict())
False

>>> is_immutable_bimap(frozenbidict())
True

Checking for isinstance(obj, frozenbidict) is too specific and could fail in some cases. For example, FrozenOrderedBidict is an immutable mapping but it does not subclass frozenbidict:

>>> from bidict import FrozenOrderedBidict
>>> obj = FrozenOrderedBidict()
>>> is_immutable_bimap(obj)
True
>>> isinstance(obj, frozenbidict)
False

Besides the above, there are several other collections ABCs whose interfaces are implemented by various bidict types. Have a look through the collections.abc documentation if you’re interested.

One thing you might notice is that there is no Ordered or OrderedMapping ABC. However, Python 3.6 introduced the collections.abc.Reversible ABC. Since being reversible implies having an ordering, you could check for reversibility instead. For example:

>>> from collections.abc import Reversible

>>> def is_reversible_mapping(cls):
...     return issubclass(cls, Reversible) and issubclass(cls, Mapping)
...

>>> is_reversible_mapping(OrderedBidict)
True

>>> is_reversible_mapping(OrderedDict)
True

For more you can do with bidict, check out Extending bidict next.

Extending bidict

Although bidict provides the various bidirectional mapping types covered already, it’s possible that some use case might require something more than what’s provided. For this reason, bidict was written with extensibility in mind.

Let’s look at some examples.

YoloBidict Recipe

If you’d like ON_DUP_DROP_OLD to be the default on_dup behavior (for __init__(), __setitem__(), and update()), you can use the following recipe:

>>> from bidict import bidict, ON_DUP_DROP_OLD

>>> class YoloBidict(bidict):
...     __slots__ = ()
...     on_dup = ON_DUP_DROP_OLD

>>> b = YoloBidict({'one': 1})
>>> b['two'] = 1  # succeeds, no ValueDuplicationError
>>> b
YoloBidict({'two': 1})

>>> b.update({'three': 1})  # ditto
>>> b
YoloBidict({'three': 1})

Of course, YoloBidict’s inherited put() and putall() methods still allow specifying a custom OnDup per call via the on_dup argument, and will both still default to raising for all duplication types.

Further demonstrating bidict’s extensibility, to make an OrderedYoloBidict, simply have the subclass above inherit from bidict.OrderedBidict rather than bidict.bidict.

Beware of ON_DUP_DROP_OLD

There’s a good reason that bidict does not provide a YoloBidict out of the box.

Before you decide to use a YoloBidict in your own code, beware of the following potentially unexpected, dangerous behavior:

>>> b = YoloBidict({'one': 1, 'two': 2})  # contains two items
>>> b['one'] = 2                          # update one of the items
>>> b                                     # now only has one item!
YoloBidict({'one': 2})

As covered in Key and Value Duplication, setting an existing key to the value of a different existing item causes both existing items to quietly collapse into a single new item.

A safer example of this type of customization would be something like:

>>> from bidict import ON_DUP_RAISE

>>> class YodoBidict(bidict):
...     __slots__ = ()
...     on_dup = ON_DUP_RAISE

>>> b = YodoBidict({'one': 1})
>>> b['one'] = 2  # Works with a regular bidict, but Yodo plays it safe.
Traceback (most recent call last):
    ...
KeyDuplicationError: one
>>> b
YodoBidict({'one': 1})
>>> b.forceput('one', 2)  # Any destructive change requires more force.
>>> b
YodoBidict({'one': 2})

SortedBidict Recipes

Suppose you need a bidict that maintains its items in sorted order. The Python standard library does not include any sorted dict types, but the excellent sortedcontainers and sortedcollections libraries do. Armed with these along with bidict’s _fwdm_cls and _invm_cls attributes, creating a sorted bidict type is dead simple:

>>> # As an optimization, bidict.bidict includes a mixin class that
>>> # we can't use here (namely bidict._delegating_mixins._DelegateKeysAndItemsToFwdm),
>>> # so extend the main parent class, bidict.MutableBidict, instead.
>>> from bidict import MutableBidict

>>> import sortedcontainers

>>> class SortedBidict(MutableBidict):
...     """A sorted bidict whose forward items stay sorted by their keys,
...     and whose inverse items stay sorted by *their* keys.
...     Note: As a result, an instance and its inverse yield their items
...     in different orders.
...     """
...     __slots__ = ()
...     _fwdm_cls = sortedcontainers.SortedDict
...     _invm_cls = sortedcontainers.SortedDict
...     _repr_delegate = list

>>> b = SortedBidict({'Tokyo': 'Japan', 'Cairo': 'Egypt'})
>>> b
SortedBidict([('Cairo', 'Egypt'), ('Tokyo', 'Japan')])

>>> b['Lima'] = 'Peru'

>>> # b stays sorted by its keys:
>>> list(b.items())
[('Cairo', 'Egypt'), ('Lima', 'Peru'), ('Tokyo', 'Japan')]

>>> # b.inverse stays sorted by *its* keys (b's values)
>>> list(b.inverse.items())
[('Egypt', 'Cairo'), ('Japan', 'Tokyo'), ('Peru', 'Lima')]

Here’s a recipe for a sorted bidict whose forward items stay sorted by their keys, and whose inverse items stay sorted by their values. i.e. An instance and its inverse will yield their items in the same order:

>>> import sortedcollections

>>> class KeySortedBidict(MutableBidict):
...     __slots__ = ()
...     _fwdm_cls = sortedcontainers.SortedDict
...     _invm_cls = sortedcollections.ValueSortedDict
...     _repr_delegate = list

>>> element_by_atomic_number = KeySortedBidict({
...     3: 'lithium', 1: 'hydrogen', 2: 'helium'})

>>> # stays sorted by key:
>>> element_by_atomic_number
KeySortedBidict([(1, 'hydrogen'), (2, 'helium'), (3, 'lithium')])

>>> # .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.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.inverse  # Note the different class, which was auto-generated:
KeySortedBidictInv([('hydrogen', 1), ('helium', 2), ('lithium', 3), ('beryllium', 4)])
>>> 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.inverse)
>>> atomic_number_by_element
KeySortedBidictInv([('hydrogen', 1), ('helium', 2), ('lithium', 3), ('beryllium', 4)])
>>> KeySortedBidict(atomic_number_by_element.inverse) == element_by_atomic_number
True

>>> # One other useful trick:
>>> # To pass method calls through to the _fwdm SortedDict when not present
>>> # on the bidict instance, provide a custom __getattribute__ method:
>>> def __getattribute__(self, name):
...     try:
...         return object.__getattribute__(self, name)
...     except AttributeError as e:
...         return getattr(self._fwdm, name)

>>> KeySortedBidict.__getattribute__ = __getattribute__

>>> # bidict has no .peekitem attr, so the call is passed through to _fwdm:
>>> element_by_atomic_number.peekitem()
(4, 'beryllium')
>>> element_by_atomic_number.inverse.peekitem()
('beryllium', 4)

Next proceed to Other Functionality.

Other Functionality

bidict.inverted()

bidict provides the inverted iterator to help you get inverse items from various types of objects.

Pass in a mapping to get the inverse mapping:

>>> from bidict import inverted
>>> it = inverted({1: 'one'})
>>> {k: v for (k, v) in it}
{'one': 1}

…an iterable of pairs to get the pairs’ inverses:

>>> list(inverted([(1, 'one'), (2, 'two')]))
[('one', 1), ('two', 2)]
>>> list(inverted((i*i, i) for i in range(2, 5)))
[(2, 4), (3, 9), (4, 16)]

…or any object implementing an __inverted__ method, which objects that already know their own inverses (such as bidicts) can implement themselves:

>>> dict(inverted(bidict({1: 'one'})))
{'one': 1}
>>> list(inverted(OrderedBidict([(2, 4), (3, 9)])))
[(4, 2), (9, 3)]

Perhaps you’d be interested in taking a look at the Addendum next.

Addendum

Performance

bidict strives to be as performant as possible while being faithful to its purpose. The need for speed is balanced with the responsibility to protect users from shooting themselves in the foot.

In general, accomplishing some task using bidict should have about the same performance as keeping two inverse dicts in sync manually. The test suite includes benchmarks for common workloads to catch any performance regressions.

If you spot a case where bidict’s performance could be improved, please don’t hesitate to file an issue or submit a pull request.

bidict Avoids Reference Cycles

A careful reader might notice the following…

>>> fwd = bidict(one=1)
>>> inv = fwd.inverse
>>> inv.inverse is fwd
True

…and worry that a bidict and its inverse create a reference cycle. If this were true, in CPython this would mean that the memory for a bidict could not be immediately reclaimed when you retained no more references to it, but rather would have to wait for the next gargage collection to kick in before it could be reclaimed.

However, bidicts use a weakref.ref to store the inverse reference in one direction, avoiding the strong reference cycle. As a result, when you no longer retain any references to a bidict you create, you can be sure that its refcount in CPython drops to zero, and that its memory will therefore be reclaimed immediately.

Note

In PyPy this is not an issue, as PyPy doesn’t use reference counts. The memory for unreferenced objects in PyPy is only reclaimed when GC kicks in, which is unpredictable.

Terminology

  • It’s intentional that the term “inverse” is used rather than “reverse”.

    Consider a collection of (k, v) pairs. Taking the reverse of the collection can only be done if it is ordered, and (as you’d expect) reverses the order of the pairs in the collection. But each original (k, v) pair remains in the resulting collection.

    By contrast, taking the inverse of such a collection neither requires the collection to be ordered nor guarantees any ordering in the result, but rather just replaces every (k, v) pair with the inverse pair (v, k).

  • “keys” and “values” could perhaps more properly be called “primary keys” and “secondary keys” (as in a database), or even “forward keys” and “inverse keys”, respectively. bidict sticks with the terms “keys” and “values” for the sake of familiarity and to avoid potential confusion, but technically values are also keys themselves.

    Concretely, this allows bidicts to return a set-like (dict_keys) object for values(), rather than a non-set-like dict_values object.

Missing bidicts in the Standard Library

The Python standard library actually contains some examples where bidicts could be used for fun and profit (depending on your ideas of fun and profit):

  • The logging module contains a private _levelToName dict which maps integer levels like 10 to their string names like DEBUG. If I had a nickel for every time I wanted that exposed in a bidirectional map (and as a public attribute, no less), I bet I could afford some better turns of phrase.

  • The dis module maintains a mapping from opnames to opcodes dis.opmap and a separate list of opnames indexed by opcode dis.opnames. These could be combined into a single bidict.

  • Python 3’s html.entities module maintains separate html.entities.name2codepoint and html.entities.codepoint2name dicts. These could be combined into a single bidict.

Caveats

Non-Atomic Mutation

As with built-in dicts, mutating operations on a bidict are not atomic. If you need to mutate the same bidict from different threads, use a synchronization primitive to coordinate access. 1

1

See also: [2], [3]

Equivalent but distinct Hashables

Consider the following:

>>> d = {1: int, 1.0: float}

How many items do you expect d to contain? The actual result might surprise you:

>>> len(d)
1

And similarly,

>>> dict([(1, int), (1.0, float), (1+0j, complex), (True, bool)])
{1: <... 'bool'>}
>>> 1.0 in {True}
True

(Note that 1 == 1.0 == 1+0j == True.)

This illustrates that a mapping cannot contain two items with equivalent but distinct keys (and likewise a set cannot contain two equivalent but distinct elements). If an object that is being looked up in a set or mapping is equal to a contained object, the contained object will be found, even if it is distinct.

With a bidict, since values function as keys in the inverse mapping, this behavior occurs in the inverse direction too, and means that a bidict can end up with a different but equivalent key from the corresponding value in its own inverse:

>>> b = bidict({'false': 0})
>>> b.forceput('FALSE', False)
>>> b
bidict({'FALSE': False})
>>> b.inverse
bidict({0: 'FALSE'})

nan as a Key

In CPython, nan is especially tricky when used as a dictionary key:

>>> d = {float('nan'): 'nan'}
>>> d
{nan: 'nan'}
>>> d[float('nan')]  
Traceback (most recent call last):
    ...
KeyError: nan
>>> d[float('nan')] = 'not overwritten'
>>> d  
{nan: 'nan', nan: 'not overwritten'}

In other Python implementations such as PyPy, nan behaves just like any other dictionary key. But in CPython, beware of this unexpected behavior, which applies to bidicts too. bidict contains no special-case logic for dealing with nan as a key, so the behavior will match dict’s wherever bidict is running.

See e.g. these docs for more info (search the page for “nan”).


For more in this vein, check out Learning from bidict.

Changelog

Release Notifications

Follow on libraries.io

Tip: Subscribe to releases on GitHub or libraries.io to be notified when new versions of bidict are released.

0.20.0 (2020-07-23)

The following breaking changes are expected to affect few if any users.

Remove APIs deprecated in the previous release:

0.19.0 (2020-01-09)

0.18.3 (2019-09-22)

0.18.2 (2019-09-08)

  • Warn that Python 2 support will be dropped in a future release when Python 2 is detected.

0.18.1 (2019-09-03)

  • Fix a regression introduced by the memory optimizations added in 0.15.0 which caused deepcopied and unpickled bidicts to have their inverses set incorrectly. #94

0.18.0 (2019-02-14)

  • Rename bidict.BidirectionalMapping.inv to inverse and make bidict.BidictBase.inv an alias for inverse. #86

  • 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 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.

    Python 3.4 reaches end of life on 2019-03-18. As of January 2019, 3.4 represents only about 3% of bidict downloads on PyPI Stats.

0.17.5 (2018-11-19)

Improvements to performance and delegation logic, with minor breaking changes to semi-private APIs.

  • Remove the __delegate__ instance attribute added in the previous release. It was overly general and not worth the cost.

    Instead of checking self.__delegate__ and delegating accordingly each time a possibly-delegating method is called, revert back to using “delegated-to-fwdm” mixin classes (now found in bidict._delegating_mixins), and resurrect a mutable bidict parent class that omits the mixins as bidict.MutableBidict.

  • Rename __repr_delegate__ to _repr_delegate.

0.17.4 (2018-11-14)

Minor code, interop, and (semi-)private API improvements.

  • OrderedBidict optimizations and code improvements.

    Use bidicts for the backing _fwdm and _invm mappings, obviating the need to store key and value data in linked list nodes.

  • Refactor proxied- (i.e. delegated-) to-_fwdm logic for better composability and interoperability.

    Drop the _Proxied* mixin classes and instead move their methods into BidictBase, which now checks for an object defined by the BidictBase.__delegate__ attribute. The BidictBase.__delegate__ object will be delegated to if the method is available on it, otherwise a default implementation (e.g. inherited from Mapping) will be used otherwise. Subclasses may set __delegate__ = None to opt out.

    Consolidate _MutableBidict into bidict.bidict now that the dropped mixin classes make it unnecessary.

  • Change __repr_delegate__ to simply take a type like dict or list.

  • Upgrade to latest major sortedcontainers version (from v1 to v2) for the SortedBidict Recipes.

  • bidict.compat.{view,iter}{keys,values,items} on Python 2 no longer assumes the target object implements these methods, as they’re not actually part of the Mapping interface, and provides fallback implementations when the methods are unavailable. This allows the SortedBidict Recipes to continue to work with sortedcontainers v2 on Python 2.

0.17.3 (2018-09-18)

  • Improve packaging by adding a pyproject.toml and by including more supporting files in the distribution. #81

  • Drop pytest-runner and support for running tests via python setup.py test in preference to pytest or python -m pytest.

0.17.2 (2018-04-30)

Memory usage improvements

  • Use less memory in the linked lists that back OrderedBidicts by storing node data unpacked rather than in (key, value) tuple objects.

0.17.1 (2018-04-28)

Bugfix Release

Fix a regression in 0.17.0 that could cause erroneous behavior when updating items of an Orderedbidict’s inverse, e.g. some_ordered_bidict.inv[foo] = bar.

0.17.0 (2018-04-25)

Speedups and memory usage improvements

  • Pass keys(), values(), and items() calls (as well as their iter* and view* counterparts on Python 2) through to the backing _fwdm and _invm dicts so that they run as fast as possible (i.e. at C speed on CPython), rather than using the slower implementations inherited from collections.abc.Mapping.

  • Use weakrefs in the linked lists that back OrderedBidicts to avoid creating strong reference cycles.

    Memory for an ordered bidict that you create can now be reclaimed in CPython as soon as you no longer hold any references to it, rather than having to wait until the next garbage collection. #71

Misc

0.16.0 (2018-04-06)

Minor code and efficiency improvements to inverted() and _iteritems_args_kw() (formerly bidict.pairs()).

Minor Breaking API Changes

The following breaking changes are expected to affect few if any users.

0.15.0 (2018-03-29)

Speedups and memory usage improvements

  • Use __slots__ to speed up bidict attribute access and reduce memory usage. On Python 3, instantiating a large number of bidicts now uses ~57% the amount of memory that it used before, and on Python 2 only ~33% the amount of memory that it used before, in a simple but representative benchmark.

  • Use weakrefs to refer to a bidict’s inverse internally, no longer creating a strong reference cycle. Memory for a bidict that you create can now be reclaimed in CPython as soon as you no longer hold any references to it, rather than having to wait for the next garbage collection. See the new bidict Avoids Reference Cycles documentation. #24

  • Make bidict.BidictBase.__eq__() significantly more speed- and memory-efficient when comparing to a non-dict Mapping. (Mapping.__eq__()’s inefficient implementation will now never be used.) The implementation is now more reusable as well.

  • Make bidict.OrderedBidictBase.__iter__() as well as equality comparison slightly faster for ordered bidicts.

Minor Bugfixes

  • namedbidict() now verifies that the provided keyname and valname are distinct, raising ValueError if they are equal.

  • namedbidict() now raises TypeError if the provided base_type is not a BidirectionalMapping.

  • If you create a custom bidict subclass whose _fwdm_cls differs from its _invm_cls (as in the FwdKeySortedBidict example from the SortedBidict Recipes), the inverse bidirectional mapping type (with _fwdm_cls and _invm_cls swapped) is now correctly computed and used automatically for your custom bidict’s inverse bidict.

Miscellaneous

  • Classes no longer have to provide an __inverted__ attribute to be considered virtual subclasses of BidirectionalMapping.

  • If bidict.inverted() is passed an object with an __inverted__ attribute, it now ensures it is callable() before returning the result of calling it.

  • __repr__() no longer checks for a __reversed__ method to determine whether to use an ordered or unordered-style repr. It now calls the new __repr_delegate__ instead (which may be overridden if needed), for better composability.

Minor Breaking API Changes

The following breaking changes are expected to affect few if any users.

  • Split back out the BidictBase class from frozenbidict and OrderedBidictBase from FrozenOrderedBidict, reverting the merging of these in 0.14.0. Having e.g. issubclass(bidict, frozenbidict) == True was confusing, so this change restores issubclass(bidict, frozenbidict) == False.

    See the updated Bidict Types Diagram and Polymorphism documentation.

  • Rename:

    • bidict.BidictBase.fwdm._fwdm

    • bidict.BidictBase.invm._invm

    • bidict.BidictBase.fwd_cls._fwdm_cls

    • bidict.BidictBase.inv_cls._invm_cls

    • bidict.BidictBase.isinv._isinv

    Though overriding _fwdm_cls and _invm_cls remains supported (see Extending bidict), this is not a common enough use case to warrant public names. Most users do not need to know or care about any of these.

  • The RAISE, OVERWRITE, and IGNORE duplication policies are no longer available as attributes of DuplicationPolicy, and can now only be accessed as attributes of the bidict module namespace, which was the canonical way to refer to them anyway. It is now no longer possible to create an infinite chain like DuplicationPolicy.RAISE.RAISE.RAISE...

  • Make bidict.pairs() and bidict.inverted() no longer importable from bidict.util, and now only importable from the top-level bidict module. (bidict.util was renamed bidict._util.)

  • Pickling ordered bidicts now requires at least version 2 of the pickle protocol. If you are using Python 3, pickle.DEFAULT_PROTOCOL is 3 anyway, so this will not affect you. However if you are using in Python 2, DEFAULT_PROTOCOL is 0, so you must now explicitly specify the version in your pickle.dumps() calls, e.g. pickle.dumps(ob, 2).

0.14.2 (2017-12-06)

  • Make initializing (or updating an empty bidict) from only another BidirectionalMapping more efficient by skipping unnecessary duplication checking.

  • Fix accidental ignoring of specified base_type argument when (un)pickling a namedbidict().

  • Fix incorrect inversion of some_named_bidict.inv.<fwdname>_for and some_named_bidict.inv.<invname>_for.

  • Only warn when an unsupported Python version is detected (e.g. Python < 2.7) rather than raising AssertionError.

0.14.1 (2017-11-28)

  • Fix a bug introduced in 0.14.0 where hashing a frozenbidict’s inverse (e.g. f = frozenbidict(); {f.inv: '...'}) would cause an AttributeError.

  • Fix a bug introduced in 0.14.0 for Python 2 users where attempting to call viewitems() would cause a TypeError. #48

0.14.0 (2017-11-20)

  • Fix a bug where bidict’s default on_dup_kv policy was set to RAISE, rather than matching whatever on_dup_val policy was in effect as was documented.

  • Fix a bug that could happen when using Python’s optimization (-O) flag that could leave an ordered bidict in an inconsistent state when dealing with duplicated, overwritten keys or values. If you do not use optimizations (specifically, skipping assert statements), this would not have affected you.

  • Fix a bug introduced by the optimizations in 0.13.0 that could cause a frozen bidict that compared equal to another mapping to have a different hash value from the other mapping, violating Python’s object model. This would only have affected you if you were inserting a frozen bidict and some other immutable mapping that it compared equal to into the same set or mapping.

  • Add equals_order_sensitive().

  • Reduce the memory usage of ordered bidicts.

  • Make copying of ordered bidicts faster.

  • Improvements to tests and CI, including:

    • Test on Windows

    • Test with PyPy3

    • Test with CPython 3.7-dev

    • Test with optimization flags

    • Require pylint to pass

Breaking API Changes

This release includes multiple API simplifications and improvements.

  • Rename:

    so that these now match the case of collections.OrderedDict.

    The names of the bidict, namedbidict(), and frozenbidict classes have been retained as all-lowercase so that they continue to match the case of dict, namedtuple(), and frozenset, respectively.

  • The ON_DUP_VAL duplication policy value for on_dup_kv has been removed. Use None instead.

  • Merge frozenbidict and BidictBase together and remove BidictBase. frozenbidict is now the concrete base class that all other bidict types derive from. See the updated Bidict Types Diagram.

  • Merge frozenbidict and FrozenBidictBase together and remove FrozenBidictBase. See the updated Bidict Types Diagram.

  • Merge frozenorderedbidict and OrderedBidictBase together into a single FrozenOrderedBidict class and remove OrderedBidictBase. OrderedBidict now extends FrozenOrderedBidict to add mutable behavior. See the updated Bidict Types Diagram.

  • Make __eq__() always perform an order-insensitive equality test, even if the other mapping is ordered.

    Previously, __eq__() was only order-sensitive for other OrderedBidictBase subclasses, and order-insensitive otherwise.

    Use the new equals_order_sensitive() method for order-sensitive equality comparison.

  • orderedbidict._should_compare_order_sensitive() has been removed.

  • frozenorderedbidict._HASH_NITEMS_MAX has been removed. Since its hash value must be computed from all contained items (so that hash results are consistent with equality comparisons against unordered mappings), the number of items that influence the hash value should not be limitable.

  • frozenbidict._USE_ITEMSVIEW_HASH has been removed, and frozenbidict.compute_hash() now uses collections.ItemsView._hash() to compute the hash always, not just when running on PyPy.

    Override frozenbidict.compute_hash() to return hash(frozenset(iteritems(self))) if you prefer the old default behavior on CPython, which takes linear rather than constant space, but which uses the frozenset_hash routine (implemented in setobject.c) rather than the pure Python ItemsView._hash() routine.

  • loosebidict and looseorderedbidict have been removed. A simple recipe to implement equivalents yourself is now given in Extending bidict.

  • Rename FrozenBidictBase._compute_hash()frozenbidict.compute_hash().

  • Rename DuplicationBehaviorDuplicationPolicy.

  • Rename:

    • BidictBase._fwd_class.fwd_cls

    • BidictBase._inv_class.inv_cls

    • BidictBase._on_dup_keyon_dup_key

    • BidictBase._on_dup_valon_dup_val

    • BidictBase._on_dup_kvon_dup_kv

0.13.1 (2017-03-15)

  • Fix regression introduced by the new __subclasshook__() functionality in 0.13.0 so that issubclass(OldStyleClass, BidirectionalMapping) once again works with old-style classes, returning False rather than raising AttributeError #41

0.13.0 (2017-01-19)

  • Support Python 3.6.

    (Earlier versions of bidict should work fine on 3.6, but it is officially supported starting in this version.)

  • BidirectionalMapping has been refactored into an abstract base class, following the way collections.abc.Mapping works. The concrete method implementations it used to provide have been moved into a new BidictBase subclass.

    BidirectionalMapping now also implements __subclasshook__(), so any class that provides a conforming set of attributes (enumerated in _subclsattrs) will be considered a BidirectionalMapping subclass automatically.

  • OrderedBidirectionalMapping has been renamed to OrderedBidictBase, to better reflect its function. (It is not an ABC.)

  • A new FrozenBidictBase class has been factored out of frozenbidict and frozenorderedbidict. This implements common behavior such as caching the result of __hash__ after the first call.

  • The hash implementations of frozenbidict and frozenorderedbidict. have been reworked to improve performance and flexibility. frozenorderedbidict’s hash implementation is now order-sensitive.

    See frozenbidict._compute_hash() and frozenorderedbidict._compute_hash for more documentation of the changes, including the new frozenbidict._USE_ITEMSVIEW_HASH and frozenorderedbidict._HASH_NITEMS_MAX attributes. If you have an interesting use case that requires overriding these, or suggestions for an alternative implementation, please share your feedback.

  • Add _fwd_class and _inv_class attributes representing the backing Mapping types used internally to store the forward and inverse dictionaries, respectively.

    This allows creating custom bidict types with extended functionality simply by overriding these attributes in a subclass.

    See the new Extending bidict documentation for examples.

  • Pass any parameters passed to popitem() through to _fwd.popitem for greater extensibility.

  • More concise repr strings for empty bidicts.

    e.g. bidict() rather than bidict({}) and orderedbidict() rather than orderedbidict([]).

  • Add bidict.compat.PYPY and remove unused bidict.compat.izip_longest.

0.12.0 (2016-07-03)

  • New/renamed exceptions:

  • put() now accepts on_dup_key, on_dup_val, and on_dup_kv keyword args which allow you to override the default policy when the key or value of a given item duplicates any existing item’s. These can take the following values:

    on_dup_kv can also take ON_DUP_VAL.

    If not provided, put() uses the RAISE policy by default.

  • New putall() method provides a bulk put() API, allowing you to override the default duplication handling policy that update() uses.

  • update() now fails clean, so if an update() call raises a DuplicationError, you can now be sure that none of the given items was inserted.

    Previously, all of the given items that were processed before the one causing the failure would have been inserted, and no facility was provided to recover which items were inserted and which weren’t, nor to revert any changes made by the failed update() call. The new behavior makes it easier to reason about and control the effects of failed update() calls.

    The new putall() method also fails clean.

    Internally, this is implemented by storing a log of changes made while an update is being processed, and rolling back the changes when one of them is found to cause an error. This required reimplementing orderedbidict on top of two dicts and a linked list, rather than two OrderedDicts, since OrderedDict does not expose its backing linked list.

  • orderedbidict.move_to_end() now works on Python < 3.2 as a result of the new orderedbidict implementation.

  • Add

    • bidict.compat.viewkeys()

    • bidict.compat.viewvalues()

    • bidict.compat.iterkeys()

    • bidict.compat.itervalues()

    • bidict.compat.izip

    • bidict.compat.izip_longest

    to complement the existing iteritems() and viewitems() compatibility helpers.

  • More efficient implementations of bidict.pairs(), inverted(), and copy().

  • Implement __copy__() for use with the copy module.

  • Fix issue preventing a client class from inheriting from loosebidict. #34

  • Add benchmarking to tests.

  • Drop official support for CPython 3.3. (It may continue to work, but is no longer being tested.)

Breaking API Changes

  • Rename KeyExistsExceptionKeyDuplicationError and ValueExistsExceptionValueDuplicationError.

  • When overwriting the key of an existing value in an orderedbidict, the position of the existing item is now preserved, overwriting the key of the existing item in place, rather than moving the item to the end. This now matches the behavior of overwriting the value of an existing key, which has always preserved the position of the existing item. (If inserting an item whose key duplicates that of one existing item and whose value duplicates that of another, the existing item whose value is duplicated is still dropped, and the existing item whose key is duplicated still gets its value overwritten in place, as before.)

    For example:

    >>> from bidict import orderedbidict  
    >>> o = orderedbidict([(0, 1), (2, 3)])  
    >>> o.forceput(4, 1)  
    

    previously would have resulted in:

    >>> o  
    orderedbidict([(2, 3), (4, 1)])
    

    but now results in:

    >>> o  
    orderedbidict([(4, 1), (2, 3)])
    

0.11.0 (2016-02-05)

0.10.0.post1 (2015-12-23)

  • Minor documentation fixes and improvements.

0.10.0 (2015-12-23)

  • Remove several features in favor of keeping the API simpler and the code more maintainable.

  • In the interest of protecting data safety more proactively, by default bidict now raises an error on attempting to insert a non-unique value, rather than allowing its associated key to be silently overwritten. See discussion in #21.

  • New forceupdate() method provides a bulk forceput() operation.

  • Fix bugs in pop and setdefault which could leave a bidict in an inconsistent state.

Breaking API Changes

  • Remove bidict.__invert__, and with it, support for the ~b syntax. Use inv instead. #19

  • Remove support for the slice syntax. Use b.inv[val] rather than b[:val]. #19

  • Remove bidict.invert. Use inv rather than inverting a bidict in place. #20

  • Raise ValueExistsException when attempting to insert a mapping with a non-unique key. #21

  • Rename collapsingbidictloosebidict now that it suppresses ValueExistsException rather than the less general CollapseException. #21

  • CollapseException has been subsumed by ValueExistsException. #21

  • put() now raises KeyExistsException when attempting to insert an already-existing key, and ValueExistsException when attempting to insert an already-existing value.

0.9.0.post1 (2015-06-06)

  • Fix metadata missing in the 0.9.0rc0 release.

0.9.0rc0 (2015-05-30)

Breaking API Changes

  • Move bidict.iteritems() and bidict.viewitems() to new bidict.compat module.

  • Move bidict.inverted to new bidict.util module (still available from top-level bidict module as well).

  • Move bidict.fancy_iteritems()bidict.util.pairs() (also available from top level as bidict.pairs()).

  • Rename bidict.namedbidict()’s bidict_type argument → base_type.

API

This page contains auto-generated documentation from the bidict source code.

bidict

The bidirectional mapping library for Python.

bidict by example:

>>> 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.

class bidict.BidirectionalMapping[source]

Bases: collections.abc.Mapping

Abstract base class (ABC) for bidirectional mapping types.

Extends collections.abc.Mapping primarily by adding the (abstract) inverse property, which implementors of BidirectionalMapping should override to return a reference to the inverse BidirectionalMapping instance.

__slots__ = ()
abstract property inverse

The inverse of this bidirectional mapping instance.

See also bidict.BidictBase.inverse, bidict.BidictBase.inv

Raises

NotImplementedError – Meant to be overridden in subclasses.

__inverted__()[source]

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

values()[source]

A set-like object providing a view on the contained values.

Override the implementation inherited from Mapping. Because the values of a BidirectionalMapping are the keys of its inverse, this returns a KeysView rather than a ValuesView, which has the advantages of constant-time containment checks and supporting set operations.

__class__

alias of abc.ABCMeta

__contains__(key)
__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__(other)

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

abstract __getitem__(key)
__gt__()

Return self>value.

__hash__ = None
__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

abstract __iter__()
__le__()

Return self<=value.

abstract __len__()
__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__reversed__ = None
__setattr__()

Implement setattr(self, name, value).

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
items() → a set-like object providing a view on D's items
keys() → a set-like object providing a view on D's keys
class bidict.BidictBase(*args, **kw)[source]

Bases: bidict._abc.BidirectionalMapping

Base class implementing BidirectionalMapping.

__slots__ = ('_fwdm', '_invm', '_inv', '_invweak', '_hash', '__weakref__')
on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)

The default OnDup that governs behavior when a provided item duplicates the key or value of other item(s).

See also Values Must Be Unique, Extending bidict

__init__(*args, **kw)[source]

Make a new bidirectional dictionary. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

property inverse

The inverse of this bidict.

See also inv

property inv

Alias for inverse.

__getstate__()[source]

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__setstate__(state)[source]

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__repr__()[source]

See repr().

__eq__(other)[source]

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

copy()[source]

A shallow copy.

__copy__()[source]

Used for the copy protocol.

See also the copy module

__len__()[source]

The number of contained items.

__iter__()[source]

Iterator over the contained keys.

__getitem__(key)[source]

x.__getitem__(key) ⟺ x[key]

__class__

alias of abc.ABCMeta

__contains__(key)
__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__ = None
__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__reversed__ = None
__setattr__()

Implement setattr(self, name, value).

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
items() → a set-like object providing a view on D's items
keys() → a set-like object providing a view on D's keys
values()

A set-like object providing a view on the contained values.

Override the implementation inherited from Mapping. Because the values of a BidirectionalMapping are the keys of its inverse, this returns a KeysView rather than a ValuesView, which has the advantages of constant-time containment checks and supporting set operations.

class bidict.MutableBidict(*args, **kw)[source]

Bases: bidict._base.BidictBase, collections.abc.MutableMapping

Base class for mutable bidirectional mappings.

__slots__ = ()
__delitem__(key)[source]

x.__delitem__(y) ⟺ del x[y]

__setitem__(key, val)[source]

Set the value for key to val.

If key is already associated with val, this is a no-op.

If key is already associated with a different value, the old value will be replaced with val, as with dict’s __setitem__().

If val is already associated with a different key, an exception is raised to protect against accidental removal of the key that’s currently associated with val.

Use put() instead if you want to specify different behavior in the case that the provided key or value duplicates an existing one. Or use forceput() to unconditionally associate key with val, replacing any existing items as necessary to preserve uniqueness.

Raises
put(key, val, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))[source]

Associate key with val, honoring the OnDup given in on_dup.

For example, if on_dup is ON_DUP_RAISE, then key will be associated with val if and only if key is not already associated with an existing value and val is not already associated with an existing key, otherwise an exception will be raised.

If key is already associated with val, this is a no-op.

Raises
forceput(key, val)[source]

Associate key with val unconditionally.

Replace any existing mappings containing key key or value val as necessary to preserve uniqueness.

clear()[source]

Remove all items.

pop(key, default=<MISS>)[source]

x.pop(k[, d]) → v

Remove specified key and return the corresponding value.

Raises

KeyError – if key is not found and no default is provided.

popitem()[source]

x.popitem() → (k, v)

Remove and return some item as a (key, value) pair.

Raises

KeyError – if x is empty.

update(*args, **kw)[source]

Like calling putall() with self.on_dup passed for on_dup.

forceupdate(*args, **kw)[source]

Like a bulk forceput().

putall(items, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))[source]

Like a bulk put().

If one of the given items causes an exception to be raised, none of the items is inserted.

__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__(key)

x.__getitem__(key) ⟺ x[key]

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__hash__ = None
__init__(*args, **kw)

Make a new bidirectional dictionary. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__iter__()

Iterator over the contained keys.

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__reversed__ = None
__setattr__()

Implement setattr(self, name, value).

__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

copy()

A shallow copy.

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items() → a set-like object providing a view on D's items
keys() → a set-like object providing a view on D's keys
on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
setdefault(k[, d]) → D.get(k,d), also set D[k]=d if k not in D
values()

A set-like object providing a view on the contained values.

Override the implementation inherited from Mapping. Because the values of a BidirectionalMapping are the keys of its inverse, this returns a KeysView rather than a ValuesView, which has the advantages of constant-time containment checks and supporting set operations.

class bidict.bidict(*args, **kw)[source]

Bases: bidict._delegating._DelegatingMixin, bidict._mut.MutableBidict

Base class for mutable bidirectional mappings.

__slots__ = ()
__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__delitem__(key)

x.__delitem__(y) ⟺ del x[y]

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__(key)

x.__getitem__(key) ⟺ x[key]

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__hash__ = None
__init__(*args, **kw)

Make a new bidirectional dictionary. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__iter__()

Iterator over the contained keys.

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__reversed__ = None
__setattr__()

Implement setattr(self, name, value).

__setitem__(key, val)

Set the value for key to val.

If key is already associated with val, this is a no-op.

If key is already associated with a different value, the old value will be replaced with val, as with dict’s __setitem__().

If val is already associated with a different key, an exception is raised to protect against accidental removal of the key that’s currently associated with val.

Use put() instead if you want to specify different behavior in the case that the provided key or value duplicates an existing one. Or use forceput() to unconditionally associate key with val, replacing any existing items as necessary to preserve uniqueness.

Raises
__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

clear()

Remove all items.

copy()

A shallow copy.

forceput(key, val)

Associate key with val unconditionally.

Replace any existing mappings containing key key or value val as necessary to preserve uniqueness.

forceupdate(*args, **kw)

Like a bulk forceput().

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items()

A set-like object providing a view on the contained items.

keys()

A set-like object providing a view on the contained keys.

on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
pop(key, default=<MISS>)

x.pop(k[, d]) → v

Remove specified key and return the corresponding value.

Raises

KeyError – if key is not found and no default is provided.

popitem()

x.popitem() → (k, v)

Remove and return some item as a (key, value) pair.

Raises

KeyError – if x is empty.

put(key, val, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))

Associate key with val, honoring the OnDup given in on_dup.

For example, if on_dup is ON_DUP_RAISE, then key will be associated with val if and only if key is not already associated with an existing value and val is not already associated with an existing key, otherwise an exception will be raised.

If key is already associated with val, this is a no-op.

Raises
putall(items, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))

Like a bulk put().

If one of the given items causes an exception to be raised, none of the items is inserted.

setdefault(k[, d]) → D.get(k,d), also set D[k]=d if k not in D
update(*args, **kw)

Like calling putall() with self.on_dup passed for on_dup.

values()

A set-like object providing a view on the contained values.

class bidict.frozenbidict(*args, **kw)[source]

Bases: bidict._delegating._DelegatingMixin, bidict._base.BidictBase

Immutable, hashable bidict type.

__slots__ = ()
__hash__()[source]

The hash of this bidict as determined by its items.

__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__(key)

x.__getitem__(key) ⟺ x[key]

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__init__(*args, **kw)

Make a new bidirectional dictionary. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__iter__()

Iterator over the contained keys.

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__reversed__ = None
__setattr__()

Implement setattr(self, name, value).

__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

copy()

A shallow copy.

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items()

A set-like object providing a view on the contained items.

keys()

A set-like object providing a view on the contained keys.

on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
values()

A set-like object providing a view on the contained values.

class bidict.FrozenOrderedBidict(*args, **kw)[source]

Bases: bidict._orderedbase.OrderedBidictBase

Hashable, immutable, ordered bidict type.

__slots__ = ()
__hash__()

The hash of this bidict as determined by its items.

__iter__(reverse=False)[source]

Iterator over the contained keys in insertion order.

keys()[source]

A set-like object providing a view on the contained keys.

values()[source]

A set-like object providing a view on the contained values.

__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__(key)

x.__getitem__(key) ⟺ x[key]

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__init__(*args, **kw)

Make a new ordered bidirectional mapping. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

The order in which items are inserted is remembered, similar to collections.OrderedDict.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__reversed__()

Iterator over the contained keys in reverse insertion order.

__setattr__()

Implement setattr(self, name, value).

__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

copy()

A shallow copy of this ordered bidict.

equals_order_sensitive(other)

Order-sensitive equality check.

See also __eq__() is order-insensitive

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items() → a set-like object providing a view on D's items
on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
bidict.namedbidict(typename, keyname, valname, base_type=<class 'bidict._bidict.bidict'>)[source]

Create a new subclass of base_type with custom accessors.

Analagous to collections.namedtuple().

The new class’s __name__ and __qualname__ will be set based on typename.

Instances of it will provide access to their inverses via the custom keyname_for property, and access to themselves via the custom valname_for property.

See also the namedbidict usage documentation

Raises
  • ValueError – if any of the typename, keyname, or valname strings is not a valid Python identifier, or if keyname == valname.

  • TypeError – if base_type is not a subclass of BidirectionalMapping. (This function requires slightly more of base_type, e.g. the availability of an _isinv attribute, but all the concrete bidict types that the bidict module provides can be passed in. Check out the code if you actually need to pass in something else.)

class bidict.OrderedBidictBase(*args, **kw)[source]

Bases: bidict._base.BidictBase

Base class implementing an ordered BidirectionalMapping.

__slots__ = ('_sntl',)
__init__(*args, **kw)[source]

Make a new ordered bidirectional mapping. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

The order in which items are inserted is remembered, similar to collections.OrderedDict.

copy()[source]

A shallow copy of this ordered bidict.

__getitem__(key)[source]

x.__getitem__(key) ⟺ x[key]

__iter__(reverse=False)[source]

Iterator over the contained keys in insertion order.

__reversed__()[source]

Iterator over the contained keys in reverse insertion order.

equals_order_sensitive(other)[source]

Order-sensitive equality check.

See also __eq__() is order-insensitive

__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__hash__ = None
__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__setattr__()

Implement setattr(self, name, value).

__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items() → a set-like object providing a view on D's items
keys() → a set-like object providing a view on D's keys
on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
values()

A set-like object providing a view on the contained values.

Override the implementation inherited from Mapping. Because the values of a BidirectionalMapping are the keys of its inverse, this returns a KeysView rather than a ValuesView, which has the advantages of constant-time containment checks and supporting set operations.

class bidict.OrderedBidict(*args, **kw)[source]

Bases: bidict._orderedbase.OrderedBidictBase, bidict._mut.MutableBidict

Mutable bidict type that maintains items in insertion order.

__slots__ = ()
clear()[source]

Remove all items.

popitem(last=True)[source]

x.popitem() → (k, v)

Remove and return the most recently added item as a (key, value) pair if last is True, else the least recently added item.

Raises

KeyError – if x is empty.

move_to_end(key, last=True)[source]

Move an existing key to the beginning or end of this ordered bidict.

The item is moved to the end if last is True, else to the beginning.

Raises

KeyError – if the key does not exist

__class__

alias of abc.ABCMeta

__contains__(key)
__copy__()

Used for the copy protocol.

See also the copy module

__delattr__()

Implement delattr(self, name).

__delitem__(key)

x.__delitem__(y) ⟺ del x[y]

__dir__()

Default dir() implementation.

__eq__(other)

x.__eq__(other) ⟺ x == other

Equivalent to dict(x.items()) == dict(other.items()) but more efficient.

Note that bidict's __eq__() implementation is inherited by subclasses, in particular by the ordered bidict subclasses, so even with ordered bidicts, == comparison is order-insensitive.

See also bidict.FrozenOrderedBidict.equals_order_sensitive()

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__(key)

x.__getitem__(key) ⟺ x[key]

__getstate__()

Needed to enable pickling due to use of __slots__ and weakrefs.

See also object.__getstate__()

__gt__()

Return self>value.

__hash__ = None
__init__(*args, **kw)

Make a new ordered bidirectional mapping. The signature behaves like that of dict. Items passed in are added in the order they are passed, respecting the on_dup class attribute in the process.

The order in which items are inserted is remembered, similar to collections.OrderedDict.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__inverted__()

Get an iterator over the items in 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 inverse, just return an iterator over them directly.

Providing this default implementation enables external functions, particularly inverted(), to use this optimized implementation when available, instead of having to invert on the fly.

See also bidict.inverted()

__iter__(reverse=False)

Iterator over the contained keys in insertion order.

__le__()

Return self<=value.

__len__()

The number of contained items.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

See repr().

__reversed__()

Iterator over the contained keys in reverse insertion order.

__setattr__()

Implement setattr(self, name, value).

__setitem__(key, val)

Set the value for key to val.

If key is already associated with val, this is a no-op.

If key is already associated with a different value, the old value will be replaced with val, as with dict’s __setitem__().

If val is already associated with a different key, an exception is raised to protect against accidental removal of the key that’s currently associated with val.

Use put() instead if you want to specify different behavior in the case that the provided key or value duplicates an existing one. Or use forceput() to unconditionally associate key with val, replacing any existing items as necessary to preserve uniqueness.

Raises
__setstate__(state)

Implemented because use of __slots__ would prevent unpickling otherwise.

See also object.__setstate__()

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

classmethod __subclasshook__(C)

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

copy()

A shallow copy of this ordered bidict.

equals_order_sensitive(other)

Order-sensitive equality check.

See also __eq__() is order-insensitive

forceput(key, val)

Associate key with val unconditionally.

Replace any existing mappings containing key key or value val as necessary to preserve uniqueness.

forceupdate(*args, **kw)

Like a bulk forceput().

get(k[, d]) → D[k] if k in D, else d. d defaults to None.
property inv

Alias for inverse.

property inverse

The inverse of this bidict.

See also inv

items() → a set-like object providing a view on D's items
keys() → a set-like object providing a view on D's keys
on_dup = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)
pop(key, default=<MISS>)

x.pop(k[, d]) → v

Remove specified key and return the corresponding value.

Raises

KeyError – if key is not found and no default is provided.

put(key, val, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))

Associate key with val, honoring the OnDup given in on_dup.

For example, if on_dup is ON_DUP_RAISE, then key will be associated with val if and only if key is not already associated with an existing value and val is not already associated with an existing key, otherwise an exception will be raised.

If key is already associated with val, this is a no-op.

Raises
putall(items, on_dup=OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>))

Like a bulk put().

If one of the given items causes an exception to be raised, none of the items is inserted.

setdefault(k[, d]) → D.get(k,d), also set D[k]=d if k not in D
update(*args, **kw)

Like calling putall() with self.on_dup passed for on_dup.

values()

A set-like object providing a view on the contained values.

Override the implementation inherited from Mapping. Because the values of a BidirectionalMapping are the keys of its inverse, this returns a KeysView rather than a ValuesView, which has the advantages of constant-time containment checks and supporting set operations.

class bidict.OnDup[source]

Bases: bidict._dup._OnDup

A 3-tuple of OnDupActions specifying how to handle the 3 kinds of duplication.

See also Values Must Be Unique

If kv is not specified, val will be used for kv.

__slots__ = ()
static __new__(cls, key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)[source]

Override to provide user-friendly default values.

__add__()

Return self+value.

__class__

alias of builtins.type

__contains__()

Return key in self.

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__getitem__()

Return self[key].

__getnewargs__()

Return self as a plain tuple. Used by copy and pickle.

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__iter__()

Implement iter(self).

__le__()

Return self<=value.

__len__()

Return len(self).

__lt__()

Return self<value.

__mul__()

Return self*value.

__ne__()

Return self!=value.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return a nicely formatted representation string

__rmul__()

Return value*self.

__setattr__()

Implement setattr(self, name, value).

__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

count()

Return number of occurrences of value.

index()

Return first index of value.

Raises ValueError if the value is not present.

property key

Alias for field number 0

property kv

Alias for field number 2

property val

Alias for field number 1

class bidict.OnDupAction[source]

Bases: enum.Enum

An action to take to prevent duplication from occurring.

RAISE = 'RAISE'

Raise a DuplicationError.

DROP_OLD = 'DROP_OLD'

Overwrite existing items with new items.

DROP_NEW = 'DROP_NEW'

Keep existing items and drop new items.

__class__

alias of enum.EnumMeta

__members__ = mappingproxy(OrderedDict([('RAISE', <bidict.RAISE>), ('DROP_OLD', <bidict.DROP_OLD>), ('DROP_NEW', <bidict.DROP_NEW>)]))
exception bidict.BidictException[source]

Bases: Exception

Base class for bidict exceptions.

__cause__

exception cause

__class__

alias of builtins.type

__context__

exception context

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__setattr__()

Implement setattr(self, name, value).

__setstate__()
__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

__suppress_context__
__traceback__
args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception bidict.DuplicationError[source]

Bases: bidict._exc.BidictException

Base class for exceptions raised when uniqueness is violated as per the :attr:~bidict.RAISE` OnDupAction.

__cause__

exception cause

__class__

alias of builtins.type

__context__

exception context

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__setattr__()

Implement setattr(self, name, value).

__setstate__()
__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

__suppress_context__
__traceback__
args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception bidict.KeyDuplicationError[source]

Bases: bidict._exc.DuplicationError

Raised when a given key is not unique.

__cause__

exception cause

__class__

alias of builtins.type

__context__

exception context

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__setattr__()

Implement setattr(self, name, value).

__setstate__()
__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

__suppress_context__
__traceback__
args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception bidict.ValueDuplicationError[source]

Bases: bidict._exc.DuplicationError

Raised when a given value is not unique.

__cause__

exception cause

__class__

alias of builtins.type

__context__

exception context

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__setattr__()

Implement setattr(self, name, value).

__setstate__()
__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

__suppress_context__
__traceback__
args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

exception bidict.KeyAndValueDuplicationError[source]

Bases: bidict._exc.KeyDuplicationError, bidict._exc.ValueDuplicationError

Raised when a given item’s key and value are not unique.

That is, its key duplicates that of another item, and its value duplicates that of a different other item.

__cause__

exception cause

__class__

alias of builtins.type

__context__

exception context

__delattr__()

Implement delattr(self, name).

__dir__()

Default dir() implementation.

__eq__()

Return self==value.

__format__()

Default object formatter.

__ge__()

Return self>=value.

__getattribute__()

Return getattr(self, name).

__gt__()

Return self>value.

__hash__()

Return hash(self).

__init__()

Initialize self. See help(type(self)) for accurate signature.

__init_subclass__()

This method is called when a class is subclassed.

The default implementation does nothing. It may be overridden to extend subclasses.

__le__()

Return self<=value.

__lt__()

Return self<value.

__ne__()

Return self!=value.

__new__()

Create and return a new object. See help(type) for accurate signature.

__reduce__()

Helper for pickle.

__reduce_ex__()

Helper for pickle.

__repr__()

Return repr(self).

__setattr__()

Implement setattr(self, name, value).

__setstate__()
__sizeof__()

Size of object in memory, in bytes.

__str__()

Return str(self).

__subclasshook__()

Abstract classes can override this to customize issubclass().

This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached).

__suppress_context__
__traceback__
args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

bidict.inverted(arg)[source]

Yield the inverse items of the provided object.

If arg has a callable() __inverted__ attribute, return the result of calling it.

Otherwise, return an iterator over the items in arg, inverting each item on the fly.

See also bidict.BidirectionalMapping.__inverted__

bidict.RAISE = <bidict.RAISE>

An action to take to prevent duplication from occurring.

bidict.DROP_OLD = <bidict.DROP_OLD>

An action to take to prevent duplication from occurring.

bidict.DROP_NEW = <bidict.DROP_NEW>

An action to take to prevent duplication from occurring.

bidict.ON_DUP_DEFAULT = OnDup(key=<bidict.DROP_OLD>, val=<bidict.RAISE>, kv=<bidict.RAISE>)

A 3-tuple of OnDupActions specifying how to handle the 3 kinds of duplication.

See also Values Must Be Unique

If kv is not specified, val will be used for kv.

bidict.ON_DUP_RAISE = OnDup(key=<bidict.RAISE>, val=<bidict.RAISE>, kv=<bidict.RAISE>)

A 3-tuple of OnDupActions specifying how to handle the 3 kinds of duplication.

See also Values Must Be Unique

If kv is not specified, val will be used for kv.

bidict.ON_DUP_DROP_OLD = OnDup(key=<bidict.DROP_OLD>, val=<bidict.DROP_OLD>, kv=<bidict.DROP_OLD>)

A 3-tuple of OnDupActions specifying how to handle the 3 kinds of duplication.

See also Values Must Be Unique

If kv is not specified, val will be used for kv.

bidict._util._iteritems_mapping_or_iterable(arg)[source]

Yield the items in arg.

If arg is a Mapping, return an iterator over its items. Otherwise return an iterator over arg itself.

bidict._util._iteritems_args_kw(*args, **kw)[source]

Yield the items from the positional argument (if given) and then any from kw.

Raises

TypeError – if more than one positional argument is given.

bidict.__version__

The version of bidict represented as a string.

bidict.__version_info__

The version of bidict represented as a tuple.

bidict.compat

Compatibility helpers.

bidict.compat.PYMAJOR = 3
bidict.compat.PYMINOR = 7
bidict.compat.PY2 = False
bidict.compat.PYIMPL = 'CPython'
bidict.compat.CPY = True
bidict.compat.PYPY = False
bidict.compat.DICTS_ORDERED = True

Learning from bidict

Below is an outline of some of the more fascinating and lesser-known Python corners I got to explore further thanks to working on bidict.

If you are interested in learning more about any of the following, I highly encourage you to read bidict’s code.

I’ve sought to optimize the code not just for correctness and performance, but also to make for a clear and enjoyable read, illuminating anything that could otherwise be obscure or subtle.

I hope it brings you some of the joy it’s brought me. 😊

Python syntax hacks

Bidict used to support (ab)using a specialized form of Python’s slice syntax for getting and setting keys by value:

>>> element_by_symbol = bidict(H='hydrogen')
>>> element_by_symbol['H']  # [normal] syntax for the forward mapping
'hydrogen'
>>> element_by_symbol[:'hydrogen']  # [:slice] syntax for the inverse (no longer supported)
'H'

See this code for how this was implemented, and #19 for why this was dropped.

Code structure

Bidicts come in every combination of mutable, immutable, ordered, and unordered types, implementing Python’s various relevant collections interfaces as appropriate.

Factoring the code to maximize reuse, modularity, and adherence to SOLID design principles (while not missing any chances for special-case optimizations) has been one of the most fun parts of working on bidict.

To see how this is done, check out this code:

Data structures are amazing

Data structures are one of the most fascinating and important building blocks of programming and computer science.

It’s all too easy to lose sight of the magic when having to implement them for computer science courses or job interview questions. Part of this is because many of the most interesting real-world details get left out, and you miss all the value that comes from ongoing, direct practical application.

Bidict shows how fundamental data structures can be implemented in Python for important real-world usage, with practical concerns at top of mind. Come to catch sight of a real, live, industrial-strength linked list in the wild. Stay for the rare, exotic bidirectional mapping breeds you’ll rarely see at home. 1

1

To give you a taste:

A regular bidict encapsulates two regular dicts, keeping them in sync to preserve the bidirectional mapping invariants. Since dicts are unordered, regular bidicts are unordered too. How should we extend this to implement an ordered bidict?

We’ll still need two backing mappings to store the forward and inverse associations. To store the ordering, we use a (circular, doubly-) linked list. This allows us to e.g. delete an item in any position in O(1) time.

Interestingly, the nodes of the linked list encode only the ordering of the items; the nodes themselves contain no key or value data. The two backing mappings associate the key and value data with the nodes, providing the final pieces of the puzzle.

Can we use dicts for the backing mappings, as we did for the unordered bidict? It turns out that dicts aren’t enough—the backing mappings must actually be (unordered) bidicts themselves!

Check out _orderedbase.py to see this in action.

Property-based testing is revolutionary

When your automated tests run, are they only checking the test cases you happened to hard-code into your test suite? How do you know these test cases aren’t missing some important edge cases?

With property-based testing, you describe the types of test case inputs your functions accept, along with the properties that should hold for all inputs. Rather than having to think up your test case inputs manually and hard-code them into your test suite, they get generated for you dynamically, in much greater quantity and edge case-exercising diversity than you could come up with by hand. This dramatically increases test coverage and confidence that your code is correct.

Bidict never would have survived so many refactorings with so few bugs if it weren’t for property-based testing, enabled by the amazing Hypothesis library. It’s game-changing.

Check out bidict’s property-based tests to see this in action.

Python surprises, gotchas, regrets

  • See nan as a Key.

  • See Equivalent but distinct Hashables.

  • What should happen when checking equality of several ordered mappings that contain the same items but in a different order? What about when comparing with an unordered mapping?

    Check out what Python’s OrderedDict does, and the surprising results:

    >>> from collections import OrderedDict
    >>> d = dict([(0, 1), (2, 3)])
    >>> od = OrderedDict([(0, 1), (2, 3)])
    >>> od2 = OrderedDict([(2, 3), (0, 1)])
    >>> d == od
    True
    >>> d == od2
    True
    >>> od == od2
    False
    
    >>> class MyDict(dict):
    ...   __hash__ = lambda self: 0
    ...
    
    >>> class MyOrderedDict(OrderedDict):
    ...   __hash__ = lambda self: 0
    ...
    
    >>> d = MyDict([(0, 1), (2, 3)])
    >>> od = MyOrderedDict([(0, 1), (2, 3)])
    >>> od2 = MyOrderedDict([(2, 3), (0, 1)])
    >>> len({d, od, od2})
    1
    >>> len({od, od2, d})
    2
    

    According to Raymond Hettinger (Python core developer responsible for much of Python’s collections), this design was a mistake (e.g. it violates the Liskov substitution principle and the transitive property of equality), but it’s too late now to fix.

    Fortunately, it wasn’t too late for bidict to learn from this. Hence __eq__() is order-insensitive for ordered bidicts, and their separate equals_order_sensitive() method.

  • If you define a custom __eq__() on a class, it will not be used for != comparisons on Python 2 automatically; you must explicitly add an __ne__() implementation that calls your __eq__() implementation. If you don’t, object.__ne__() will be used instead, which behaves like is not. Python 3 thankfully fixes this.

Better memory usage through __slots__

Using __slots__ dramatically reduces memory usage in CPython and speeds up attribute access to boot. Must be careful with pickling and weakrefs though! See BidictBase.__getstate__().

Better memory usage through weakref

A bidict and its inverse use weakref to avoid creating a strong reference cycle, so that when you release your last reference to a bidict, its memory is reclaimed immediately in CPython rather than having to wait for the next garbage collection. See bidict Avoids Reference Cycles.

The (doubly) linked lists that back ordered bidicts also use weakrefs to avoid creating strong reference cycles.

Subclassing namedtuple() classes

To get the performance benefits, intrinsic sortability, etc. of namedtuple() while customizing behavior, state, API, etc., you can subclass a namedtuple() class. Just make sure to include __slots__ = (), or you’ll lose a lot of the performance benefits.

_marker.py contains a small example. Here’s a larger one:

>>> from collections import namedtuple
>>> from itertools import count

>>> class Node(namedtuple('_Node', 'cost tiebreaker data parent depth')):
...     """Represent nodes in a graph traversal. Suitable for use with e.g. heapq."""
...
...     __slots__ = ()
...     _counter = count()  # break ties between equal-cost nodes, avoid comparing data
...
...     # Give call sites a cleaner API for creating new Nodes
...     def __new__(cls, cost, data, parent=None):
...         tiebreaker = next(cls._counter)
...         depth = parent.depth + 1 if parent else 0
...         return super().__new__(cls, cost, tiebreaker, data, parent, depth)
...
...     def __repr__(self):
...         return 'Node(cost={cost}, data={data!r})'.format(**self._asdict())

>>> start = Node(cost=0, data='foo')
>>> child = Node(cost=5, data='bar', parent=start)
>>> child
Node(cost=5, data='bar')
>>> child.parent
Node(cost=0, data='foo')
>>> child.depth
1

namedtuple()-style dynamic class generation

See the implementation of namedbidict().

API Design

How to deeply integrate with Python’s collections and other built-in APIs?

  • Beyond implementing collections.abc.Mapping, bidicts implement additional APIs that dict and OrderedDict implement (e.g. setdefault(), popitem(), etc.).

    • When creating a new API, making it familiar, memorable, and intuitive is hugely important to a good user experience.

  • Thanks to Hashable’s implementing abc.ABCMeta.__subclasshook__(), any class that implements the required methods of the Hashable interface (namely, __hash__()) makes it a virtual subclass already, no need to explicitly extend. I.e. As long as Foo implements a __hash__() method, issubclass(Foo, Hashable) will always be True, no need to explicitly subclass via class Foo(Hashable): ...

  • How to make your own open ABC like Hashable, i.e. how does BidirectionalMapping work?

  • What if we needed to add a second metaclass in addition to BidirectionalMapping (e.g. to conditionally implement an optimized version of some methods based on the type of _fwmd_cls, as _delegating.py currently does without a metaclass)? Would have to be careful to avoid “TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases”. See the great write-up in https://blog.ionelmc.ro/2015/02/09/understanding-python-metaclasses/.

  • collections.abc.Mapping and collections.abc.MutableMapping don’t implement __subclasshook__(), so must either explicitly subclass (if you want to inherit any of their implementations) or use abc.ABCMeta.register() (to register as a virtual subclass without inheriting any implementation)

  • Notice we have collections.abc.Reversible but no collections.abc.Ordered or collections.abc.OrderedMapping. Proposed in bpo-28912 but rejected. Would have been useful for bidict’s __repr__() implementation (see _base.py), and potentially for interop with other ordered mapping implementations such as SortedDict.

How to make APIs Pythonic?

  • See the Zen of Python.

  • “Errors should never pass silently.

    Unless explicitly silenced.

    In the face of ambiguity, refuse the temptation to guess.”

    Manifested in bidict’s default on_dup class attribute (see ON_DUP_DEFAULT).

  • “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.

Python’s data model

  • What happens when you implement a custom __eq__()? e.g. What’s the difference between a == b and b == a when only a is an instance of your class? See the great write-up in https://eev.ee/blog/2012/03/24/python-faq-equality/ for the answer.

  • If an instance of your special mapping type is being compared against a mapping of some foreign mapping type that contains the same items, should your __eq__() method return true?

    Bidict says yes, again based on the Liskov substitution principle. Only returning true when the types matched exactly would violate this. And returning NotImplemented would cause Python to fall back on using identity comparison, which is not what is being asked for.

    (Just for fun, suppose you did only return true when the types matched exactly, and suppose your special mapping type were also hashable. Would it be worth having your __hash__() method include your type as well as your items? The only point would be to reduce collisions when multiple instances of different types contained the same items and were going to be inserted into the same dict or set, since they’d now be unequal but would hash to the same value otherwise.)

  • Making an immutable type hashable (so it can be inserted into dicts and sets): Must implement __hash__() such that a == b hash(a) == hash(b). See the object.__hash__() and object.__eq__() docs, and the implementation of frozenbidict.

    • Consider FrozenOrderedBidict: its __eq__() is order-insensitive. So all contained items must participate in the hash order-insensitively.

    • Can use collections.abc.Set._hash which provides a pure Python implementation of the same hash algorithm used to hash frozensets. (Since ItemsView extends Set, bidict.frozenbidict.__hash__() just calls ItemsView(self)._hash().)

      • Does this argue for making collections.abc.Set._hash() non-private?

      • Why isn’t the C implementation of this algorithm directly exposed in CPython? The only way to use it is to call hash(frozenset(self.items())), which wastes memory allocating the ephemeral frozenset, and time copying all the items into it before they’re hashed.

    • Unlike other attributes, if a class implements __hash__(), any subclasses of that class will not inherit it. It’s like Python implicitly adds __hash__ = None to the body of every class that doesn’t explicitly define __hash__. So if you do want a subclass to inherit a base class’s __hash__() implementation, you have to set that manually, e.g. by adding __hash__ = BaseClass.__hash__ in the class body. See FrozenOrderedBidict.

      This is consistent with the fact that object implements __hash__(), but subclasses of object that override __eq__() are not hashable by default.

  • Using __new__() to bypass default object initialization, e.g. for better copy() performance. See _base.py.

  • Overriding object.__getattribute__() for custom attribute lookup. See SortedBidict Recipes.

  • Using object.__getstate__(), object.__setstate__(), and object.__reduce__() to make an object pickleable that otherwise wouldn’t be, due to e.g. using weakrefs, as bidicts do (covered further below).

Portability

Other interesting stuff in the standard library

Tools

See the Thanks page for some of the fantastic tools for software verification, performance, code quality, etc. that bidict has provided an excuse to play with and learn.

Contributors’ Guide

Bug reports, feature requests, patches, and other contributions are warmly welcomed. Contribution should be as easy and friendly as possible. Below are a few guidelines contributors should follow to facilitate the process.

Getting Started

  • Create a GitHub account if you don’t have one already.

  • Search through the issue tracker to see if an issue or pull request has already been created for what you’re interested in. If so, feel free to add comments to it or just hit the “subscribe” button to follow progress. If not, you can join the chat room to discuss there, or go ahead and create a new issue:

    • Clearly describe the issue giving as much relevant context as possible.

    • If it is a bug, include reproduction steps, all known environments in which the bug is exhibited, and ideally a failing test case.

  • If you would like to contribute a patch, make sure you’ve created your own fork and have cloned it to your computer.

Making Changes

  • Before making changes, please install the extra packages required for development: pip install -e .[dev]

    We use EditorConfig and pre-commit to help achieve uniform style and quality standards across a diversity of development environments.

    pre-commit gets installed when you run pip install -e .[dev] and ensures that various code checks are run before every commit (look in .pre-commit-config.yaml to see which hooks are run).

    EditorConfig allows us to provide a single .editorconfig file to configure settings like indentation consistently across a variety of supported editors. See https://editorconfig.org/#download to install the plugin for your editor.

  • Create a topic branch off of master for your changes: git checkout -b <topic> master

  • Make commits of logical units.

  • Match the existing code style and quality standards. If you’re adding a feature, include accompanying tests and documentation demonstrating its correctness and usage.

  • Run the tests locally with tox to make sure they pass for all supported Python versions (see envlist in tox.ini for the complete list). If you do not have all the referenced Python versions available locally, you can also push the changes on your branch to GitHub to automatically trigger a new Travis-CI build, which should run the tests for all supported Python versions. You should be able to see the results at travis-ci.org/<user>/bidict, where <user> is the GitHub username you used to fork bidict.

  • Create a concise but comprehensive commit message in the following style:

    Include an example commit message in CONTRIBUTING guide #9999
    
    Without this patch the CONTRIBUTING guide would contain no examples of
    a model commit message. This is a problem because the contributor is left
    to imagine what the commit message should look like and may not get it
    right. This patch fixes the problem by providing a concrete example.
    
    The first line is an imperative statement summarizing the changes with an
    issue number from the tracker. The body describes the behavior without
    the patch, why it's a problem, and how the patch fixes the problem.
    

Submitting Changes

  • Push your changes to a topic branch in your fork of the repository: git push --set-upstream origin <topic>

  • Submit a pull request providing any additional relevant details necessary.

  • Acknowledgment should typically be fast but please allow 1-2 weeks for a full response / code review.

  • The code review process often involves some back-and-forth to get everything right before merging. This is typical of quality software projects that accept patches.

  • All communication should be supportive and appreciative of good faith efforts to contribute, creating a welcoming and inclusive community. Expect nothing less of any project.

Other Ways to Contribute

Chip in via Gumroad Chip in via PayPal

Bidict is the product of hundreds of hours of unpaid, voluntary work.

If bidict has helped you accomplish your work, especially work you’ve been paid for, please consider chipping in toward the costs of bidict’s maintenance and development and/or ask your organization to do the same. Any amount contributed is gratefully received.

Besides chipping in financially, filing issues, and submitting pull requests, there are other ways to contribute.

  • If you read the code and learned something new, please let me know 1.

  • If you’re using bidict in a project you work on, please post about your experience and send me a link.

  • If you come across other people who could find bidict useful, please spread the word.

Please support bidict:

Support bidict

Code of Conduct

All participation in this project should respect the Code of Conduct. 2

By participating, you are expected to honor this code.

1

https://bidict.readthedocs.io/#notice-of-usage

2

CODE_OF_CONDUCT.rst | https://bidict.readthedocs.io/code-of-conduct.html

Code of Conduct

Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

Our Standards

Examples of behavior that contributes to creating a positive environment include:

  • Using welcoming and inclusive language

  • Being respectful of differing viewpoints and experiences

  • Gracefully accepting constructive criticism

  • Focusing on what is best for the community

  • Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

  • The use of sexualized language or imagery and unwelcome sexual attention or advances

  • Trolling, insulting/derogatory comments, and personal or political attacks

  • Public or private harassment

  • Publishing others’ private information, such as a physical or electronic address, without explicit permission

  • Other conduct which could reasonably be considered inappropriate in a professional setting

Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at <jab@math.brown.edu>. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership.

Attribution

This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4.

Thanks

Bidict has benefited from the assistance of many people and projects.

People

  • Gregory Ewing for the name.

  • Terry Reedy for suggesting the slice syntax (it was fun while it lasted).

  • Raymond Hettinger for suggesting namedbidict and pointing out various caveats.

  • Francis Carr for the idea of storing the inverse bidict.

  • Adopt Pytest Month 2015 for choosing bidict, Tom Viner for being bidict’s Adopt Pytest helper for the month, and Brianna Laugher for coordinating.

  • Daniel Pope for suggestions, code reviews, and design discussion.

  • David Turner for code reviews and design discussion.

  • Michael Arntzenius for design discussion.

  • Jozef Knaperek for the bugfix.

  • Bernát Gábor for pyproject.toml support.

  • Muhammad Faisal for the CC BY 3.0 icon that bidict’s logo was derived from.

  • Richard Sanger, Zeyi Wang, and Amol Sahsrabudhe for reporting bugs.

bidict

The bidirectional mapping library for Python.

bidict logo

Status

Latest release Documentation Travis-CI build status Test coverage License Downloads past month

bidict:

  • has been used for many years by several teams at Google, Venmo, CERN, Bank of America Merrill Lynch, Bloomberg, Two Sigma, and others

  • has carefully designed APIs for safety, simplicity, flexibility, and ergonomics

  • is fast, lightweight, and has no runtime dependencies other than Python’s standard library

  • integrates natively with Python’s collections interfaces

  • is implemented in concise, well-factored, pure (PyPy-compatible) Python code optimized both for reading and learning from 1 as well as for running efficiently

  • has extensive docs and test coverage (including property-based tests and benchmarks) run continuously on all supported Python versions and OSes

Note: Python 3 Required

As promised in the 0.18.2 release (see Changelog 2), Python 2 is no longer supported. Version 0.18.3 is the last release of bidict that supports Python 2. This makes bidict more efficient on Python 3 and enables further improvement to bidict in the future. See python3statement.org for more info.

Installation

pip install bidict

Quick Start

>>> from bidict import bidict
>>> element_by_symbol = bidict({'H': 'hydrogen'})
>>> element_by_symbol['H']
'hydrogen'
>>> element_by_symbol.inverse['hydrogen']
'H'

For more usage documentation, head to the Introduction 3 and proceed from there.

Community Support

Chat

If you are thinking of using bidict in your work, or if you have any questions, comments, or suggestions, I’d love to know about your use case and provide as much voluntary support for it as possible.

Please feel free to leave a message in the chatroom or open a new issue on GitHub. You can search through existing issues before creating a new one in case your questions or concerns have been adressed there already.

Enterprise-Grade Support via Tidelift

Paid support available via Tidelift

If your use case requires a greater level of support, enterprise-grade support for bidict can be obtained via the Tidelift subscription.

Notice of Usage

If you use bidict, and especially if your usage or your organization is significant in some way, please let me know.

You can:

Changelog

See the Changelog 2 for a history of notable changes to bidict.

Release Notifications

Follow on libraries.io

Subscribe to releases on GitHub or libraries.io to be notified when new versions of bidict are released.

Learning from bidict

One of the best things about bidict is that it touches a surprising number of interesting Python corners, especially given its small size and scope.

Check out Learning from bidict 1 if you’re interested in learning more.

Contributing

bidict is currently a one-person operation maintained on a voluntary basis.

Your help would be most welcome!

Reviewers Wanted!

One of the most valuable ways to contribute to bidict – and to explore some interesting Python corners 1 while you’re at it – is to review the relatively small codebase.

Please create an issue or pull request with any improvements you’d propose or any other results you found. Submitting a draft PR with feedback in inline code comments, or a “Review results” issue, would each work well.

You can also +1 this issue to sign up to give feedback on future proposed changes that are in need of a reviewer.

Giving Back

bidict is the product of hundreds of hours of unpaid, voluntary work.

If bidict has helped you accomplish your work, especially work you’ve been paid for, please consider chipping in toward the costs of its maintenance and development and/or ask your organization to do the same.

Support bidict

Finding Documentation

If you’re viewing this on https://bidict.readthedocs.io, note that multiple versions of the documentation are available, and you can choose a different version using the popup menu at the bottom-right. Please make sure you’re viewing the version of the documentation that corresponds to the version of bidict you’d like to use.

If you’re viewing this on GitHub, PyPI, or some other place that can’t render and link this documentation properly and are seeing broken links, try these alternate links instead:

1(1,2,3)

docs/learning-from-bidict.rst | https://bidict.readthedocs.io/learning-from-bidict.html

2(1,2)

CHANGELOG.rst | https://bidict.readthedocs.io/changelog.html

3(1,2)

docs/intro.rst | https://bidict.readthedocs.io/intro.html


Next: Introduction 3