Changelog¶
If you or your organization depends on bidict, please consider sponsoring bidict on GitHub.
Tip
Watch bidict releases on GitHub to be notified when new versions of bidict are published. Click the “Watch” dropdown, choose “Custom”, and then choose “Releases”.
0.23.2 (not yet released)¶
Remove
bidict.metadataand associated metadata from thebidictmodule (e.g.bidict.__version__). Use e.g.importlib.metadata.metadata("bidict")["Version"]instead.
0.23.1 (2024-02-18)¶
Fix a regression in 0.23.0 that could defeat type inference of a bidict’s key type and value type when running in Python 3.8 or 3.9. #310
0.23.0 (2024-02-14)¶
Primarily, this release simplifies bidict by removing minor features that are no longer necessary or that have little to no apparent usage, and it also includes some performance optimizations.
Specifically, initializing or updating a bidict is now up to 70% faster in microbenchmarks.
The changes in this release will also make it easier to maintain and improve bidict in the future, including further potential performance optimizations.
It also contains several other improvements.
Drop support for Python 3.7, which reached end of life on 2023-06-27, and take advantage of features available in Python 3.8+.
Remove
FrozenOrderedBidictnow that Python 3.7 is no longer supported.frozenbidictnow provides everything thatFrozenOrderedBidictprovided (includingreversibility) on all supported Python versions, but with less space overhead.Remove
namedbidictdue to low usage.Remove the
kvfield ofOnDupwhich specified theOnDupActionto take in the case of Key and Value Duplication. Thevalfield now specifies the action to take in the case of Key and Value Duplication as well as just value duplication.Fix a bug where calls like
bidict(None),bi.update(False), etc. would fail to raise aTypeError.All
__init__(),update(), and related methods now handle SupportsKeysAndGetItem objects that are notMappings the same way that MutableMapping.update() does, before falling back to handling the provided object as an iterable of pairs.The
repr()of ordered bidicts now matches that of regular bidicts, e.g.OrderedBidict({1: 1})rather thanOrderedBidict([(1, 1)]).(Accordingly, the
bidict.__repr_delegate__field has been removed now that it’s no longer needed.)This tracks with the change to
collections.OrderedDict'srepr()in Python 3.12.Test with Python 3.12 in CI.
Note: Older versions of bidict also support Python 3.12, even though they don’t explicitly declare support for it.
Drop use of Trove classifiers that declare support for specific Python versions in package metadata.
0.22.1 (2022-12-31)¶
Only include the source code in the source distribution. This reduces the size of the source distribution from ~200kB to ~30kB.
Fix the return type hint of
bidict.inverted()to return anIterator, rather than anIterable.
0.22.0 (2022-03-23)¶
Drop support for Python 3.6, which reached end of life on 2021-12-23 and is no longer supported by pip as of pip version 22. Take advantage of this to reduce bidict’s maintenance costs.
Use mypy-appeasing explicit re-exports in
__init__.py(e.g.import x as x) so that mypy no longer gives you an implicit re-export error if you run it with--no-implicit-reexport(or--strict) against code that imports frombidict.Update the implementations and type annotations of
bidict.BidictBase.keys()andbidict.BidictBase.values()to make use of the newBidictKeysViewtype, which works a bit better with type checkers.Inverse bidict instances are now computed lazily the first time the
inverseattribute is accessed rather than being computed eagerly during initialization. (A bidict’s backing, inverse, one-way mapping is still kept in sync eagerly as any mutations are made, to preserve key- and value-uniqueness.)Optimize initializing a bidict with another bidict. In a microbenchmark on Python 3.10, this now performs over 2x faster.
Optimize updating an empty bidict with another bidict. In a microbenchmark on Python 3.10, this now performs 60-75% faster.
Optimize
copy(). In a microbenchmark on Python 3.10, this now performs 10-20x faster.Optimize rolling back failed updates to a bidict in the case that the number of items passed to the update call can be determined to be larger than the bidict being updated. Previously this rollback was O(n) in the number of items passed. Now it is O(1), i.e. unboundedly faster.
Optimize
bidict.BidictBase.__contains__()(the method called when you runkey in mybidict). In a microbenchmark on Python 3.10, this now performs over 3-10x faster in the False case, and at least 50% faster in the True case.Optimize
bidict.BidictBase.__eq__()(the method called when you runmybidict == other). In a microbenchmark on Python 3.10, this now performs 15-25x faster for ordered bidicts, and 7-12x faster for unordered bidicts.Optimize
equals_order_sensitive(). In a microbenchmark on Python 3.10, this now performs 2x faster for ordered bidicts and 60-90% faster for unordered bidicts.Optimize the
MappingViewobjects returned bybidict.OrderedBidict.keys(),bidict.OrderedBidict.values, andbidict.OrderedBidict.items()to delegate to backingdict_keysanddict_itemsobjects if available, which are much faster in CPython. For example, in a microbenchmark on Python 3.10,orderedbi.items() == d.items()now performs 30-50x faster.Fix a bug where
bidict.BidictBase.__eq__()was always returning False rather thanNotImplementedin the case that the argument was not aMapping, defeating the argument’s own__eq__()if implemented. As a notable example, bidicts now correctly compare equal tounittest.mock.ANY.bidict.BidictBasenow adds a__reversed__implementation to subclasses that don’t have an overridden implementation depending on whether both their backing mappings areReversible. Previously, a__reversed__implementation was only added toBidictBasewhenBidictBase._fwdm_clswasReversible. So if aBidictBasesubclass set its_fwdm_clsto a non-reversible mutable mapping, it would also have to manually set its__reversed__attribute to None to override the implementation inherited fromBidictBase. This is no longer necessary thanks to bidict’s newobject.__init_subclass__()logic.The
MappingViewobjects returned bybidict.OrderedBidict.keys(),bidict.OrderedBidict.values, andbidict.OrderedBidict.items()are nowReversible. (This was already the case for unordered bidicts when running on Python 3.8+.)Add support for Python 3.9-style dict merge operators (PEP 584).
See the tests for examples.
Update docstrings for
bidict.BidictBase.keys(),bidict.BidictBase.values(), andbidict.BidictBase.items()to include more details.namedbidictnow exposes the passed-in keyname and valname in the corresponding properties on the generated class.namedbidictnow requires base_type to be a subclass ofBidictBase, but no longer requires base_type to provide an_isinvattribute, whichBidictBasesubclasses no longer provide.When attempting to pickle a bidict’s inverse whose class was dynamically generated, and no reference to the dynamically-generated class has been stored anywhere in
sys.moduleswherepicklecan find it, the pickle call is now more likely to succeed rather than failing with aPicklingError.Remove the use of slots from (non-ABC) bidict types.
This better matches the mapping implementations in Python’s standard library, and significantly reduces code complexity and maintenance burden. The memory savings conferred by using slots are not noticeable unless you’re creating millions of bidict instances anyway, which is an extremely unusual usage pattern.
Of course, bidicts can still contain millions (or more) items (which is not an unusual usage pattern) without using any more memory than before these changes. Notably, slots are still used in the internal linked list nodes of ordered bidicts to save memory, since as many node instances are created as there are items inserted.
0.21.4 (2021-10-23)¶
Explicitly declare support for Python 3.10 as well as some minor internal improvements.
0.21.3 (2021-09-05)¶
All bidicts now provide the
equals_order_sensitive()method, not justOrderedBidicts.Since support for Python < 3.6 was dropped in v0.21.0,
dicts provide a deterministic ordering on all supported Python versions, and as a result, all bidicts do too. So now even non-Orderedbidicts might as well provideequals_order_sensitive().See the updated What about order-preserving dicts? docs for more info.
Take better advantage of the fact that dicts became
reversiblein Python 3.8.Specifically, now even non-
Orderedbidicts provide a__reversed__()implementation on Python 3.8+ that callsreversed()on the backing_fwdmmapping.As a result, if you are using Python 3.8+,
frozenbidictnow gives you everything thatFrozenOrderedBidictgives you, but with less space overhead.Drop setuptools_scm as a
setup_requiresdependency.Remove the
bidict.__version_info__attribute.
0.21.2 (2020-09-07)¶
0.21.1 (2020-09-07)¶
This release was yanked and replaced with the 0.21.2 release, which actually provides the intended changes.
0.21.0 (2020-08-22)¶
bidictnow provides type hints! ⌨️ ✅Adding type hints to
bidictposes particularly interesting challenges due to the combination of generic types, dynamically-generated types (such as inverse bidict classes andnamedbidicts), and complicating optimizations such as the use of slots and weakrefs.It didn’t take long to hit bugs and missing features in the state of the art for type hinting in Python today, e.g. missing higher-kinded types support (python/typing#548), too-narrow type hints for
collections.abc.Mapping(python/typeshed#4435), atyping.Genericbug in Python 3.6 (BPO-41451), etc.That said, this release should provide a solid foundation for code using
bidictthat enables static type checking.As always, if you spot any opportunities to improve
bidict(including its new type hints), please don’t hesitate to submit a PR!Add
bidict.MutableBidirectionalMappingABC.The Bidict Types Diagram has been updated accordingly.
Drop support for Python 3.5, which reaches end of life on 2020-09-13, represents a tiny percentage of bidict downloads on PyPI Stats, and lacks support for variable type hint syntax, ordered dicts, and
object.__init_subclass__.Remove the no-longer-needed
bidict.compatmodule.Move inverse bidict class access from a property to an attribute set in
__init_subclass__, to save function call overhead on repeated access.bidict.OrderedBidictBase.__iter__()no longer accepts areversekeyword argument so that it matches the signature ofcontainer.__iter__().Set the
__module__attribute of variousbidicttypes (usingsys._getframe()when necessary) so that private, internal modules are not exposed e.g. in classes’ repr strings.namedbidictnow immediately raisesTypeErrorif the providedbase_typedoes not provide_isinvor__getstate__(), rather than succeeding with a class whose instances may raiseAttributeErrorwhen these attributes are accessed.
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:
bidict.OVERWRITEandbidict.IGNORE.The
on_dup_key,on_dup_val, andon_dup_kvarguments ofput()andputall().The
on_dup_key,on_dup_val, andon_dup_kvbidictclass attributes.Remove
bidict.BidirectionalMapping.__subclasshook__due to lack of use and maintenance cost.Fixes a bug introduced in 0.15.0 that caused any class with an
inverseattribute to be incorrectly considered a subclass ofcollections.abc.Mapping. #111
0.19.0 (2020-01-09)¶
Drop support for Python 2 as promised in v0.18.2.
The
bidict.compatmodule has been pruned accordingly.This makes bidict more efficient on Python 3 and enables further improvement to bidict in the future.
Deprecate
bidict.OVERWRITEandbidict.IGNORE. AUserWarningwill now be emitted if these are used.bidict.DROP_OLDandbidict.DROP_NEWshould be used instead.Rename
DuplicationPolicytoOnDupAction(and implement it via anEnum).An
OnDupActionmay be one ofRAISE,DROP_OLD, orDROP_NEW.Expose the new
OnDupclass to contain the threeOnDupActions that should be taken upon encountering the three kinds of duplication that can occur (key, val, kv).Provide the
ON_DUP_DEFAULT,ON_DUP_RAISE, andON_DUP_DROP_OLDOnDupconvenience instances.Deprecate the
on_dup_key,on_dup_val, andon_dup_kvarguments ofput()andputall(). AUserWarningwill now be emitted if these are used.These have been subsumed by the new on_dup argument, which takes an
OnDupinstance.Use it like this:
bi.put(1, 2, OnDup(key=RAISE, val=...)). Or pass one of the instances already provided, such asON_DUP_DROP_OLD. Or just don’t pass an on_dup argument to use the default value ofON_DUP_RAISE.The Values Must Be Unique docs have been updated accordingly.
Deprecate the
on_dup_key,on_dup_val, andon_dup_kvbidictclass attributes. AUserWarningwill now be emitted if these are used.These have been subsumed by the new
on_dupclass attribute, which takes anOnDupinstance.See the updated Extending bidict docs for example usage.
Improve the more efficient implementations of
bidict.BidirectionalMapping.keys,bidict.BidirectionalMapping.values, andbidict.BidirectionalMapping.items, and now also provide a more efficient implementation ofbidict.BidirectionalMapping.__iter__by delegating to backingdicts in the bidict types for which this is possible.Move
bidict.BidictBase.values()tobidict.BidirectionalMapping.values, since the implementation is generic.No longer use
__all__inbidict’s__init__.py.
0.18.4 (2020-11-02)¶
Backport fix from v0.20.0 that removes
bidict.BidirectionalMapping.__subclasshook__due to lack of use and maintenance cost.
0.18.3 (2019-09-22)¶
Improve validation of names passed to
namedbidict: Usestr.isidentifier()on Python 3, and a better regex on Python 2.On Python 3, set
__qualname__onnamedbidictclasses based on the providedtypenameargument.
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
deepcopiedandunpickledbidicts to have their inverses set incorrectly. #94
0.18.0 (2019-02-14)¶
Rename
bidict.BidirectionalMapping.invtoinverseand makebidict.BidictBase.invan alias forinverse. #86bidict.BidirectionalMapping.__subclasshook__now requires aninverseattribute rather than aninvattribute 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_abcalias.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 inbidict._delegating_mixins), and resurrect a mutable bidict parent class that omits the mixins asbidict.MutableBidict.Rename
__repr_delegate__to_repr_delegate.
0.17.4 (2018-11-14)¶
Minor code, interop, and (semi-)private API improvements.
OrderedBidictoptimizations and code improvements.Use
bidicts for the backing_fwdmand_invmmappings, obviating the need to store key and value data in linked list nodes.Refactor proxied- (i.e. delegated-) to-
_fwdmlogic for better composability and interoperability.Drop the
_Proxied*mixin classes and instead move their methods intoBidictBase, which now checks for an object defined by theBidictBase.__delegate__attribute. TheBidictBase.__delegate__object will be delegated to if the method is available on it, otherwise a default implementation (e.g. inherited fromMapping) will be used otherwise. Subclasses may set__delegate__ = Noneto opt out.Consolidate
_MutableBidictintobidict.bidictnow that the dropped mixin classes make it unnecessary.Change
__repr_delegate__to simply take a type likedictorlist.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 theMappinginterface, 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 testin preference topytestorpython -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(), anditems()calls (as well as theiriter*andview*counterparts on Python 2) through to the backing_fwdmand_invmdicts so that they run as fast as possible (i.e. at C speed on CPython), rather than using the slower implementations inherited fromcollections.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
Add
bidict.__version_info__attribute to complementbidict.__version__.
0.16.0 (2018-04-06)¶
Minor code and efficiency improvements to
inverted() and
bidict._iter._iteritems_args_kw
(formerly bidict.pairs()).
Minor Breaking API Changes
The following breaking changes are expected to affect few if any users.
Rename
bidict.pairs()→bidict._iter._iteritems_args_kw.
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-dictMapping. (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
namedbidictnow verifies that the providedkeynameandvalnameare distinct, raisingValueErrorif they are equal.namedbidictnow raisesTypeErrorif the providedbase_typeis not aBidirectionalMapping.If you create a custom bidict subclass whose
_fwdm_clsdiffers from its_invm_cls(as in theFwdKeySortedBidictexample from the SortedBidict Recipes), the inverse bidirectional mapping type (with_fwdm_clsand_invm_clsswapped) is now correctly computed and used automatically for your custom bidict’sinversebidict.
Misc
Classes no longer have to provide an
__inverted__attribute to be considered virtual subclasses ofBidirectionalMapping.If
bidict.inverted()is passed an object with an__inverted__attribute, it now ensures it iscallable()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
BidictBaseclass fromfrozenbidictandOrderedBidictBasefromFrozenOrderedBidict, reverting the merging of these in 0.14.0. Having e.g.issubclass(bidict, frozenbidict) == Truewas confusing, so this change restoresissubclass(bidict, frozenbidict) == False.See the updated Bidict Types Diagram and Polymorphism documentation.
Rename:
bidict.BidictBase.fwdm→._fwdmbidict.BidictBase.invm→._invmbidict.BidictBase.fwd_cls→._fwdm_clsbidict.BidictBase.inv_cls→._invm_clsbidict.BidictBase.isinv→._isinv
Though overriding
_fwdm_clsand_invm_clsremains 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, andIGNOREduplication policies are no longer available as attributes ofDuplicationPolicy, and can now only be accessed as attributes of thebidictmodule namespace, which was the canonical way to refer to them anyway. It is now no longer possible to create an infinite chain likeDuplicationPolicy.RAISE.RAISE.RAISE...Make
bidict.pairs()andbidict.inverted()no longer importable frombidict.util, and now only importable from the top-levelbidictmodule. (bidict.utilwas renamedbidict._util.)Pickling ordered bidicts now requires at least version 2 of the pickle protocol. If you are using Python 3,
pickle.DEFAULT_PROTOCOLis 3 anyway, so this will not affect you. However if you are using in Python 2,DEFAULT_PROTOCOLis 0, so you must now explicitly specify the version in yourpickle.dumps()calls, e.g.pickle.dumps(ob, 2).