mirror of https://github.com/jab/bidict.git
update "learning" docs
This commit is contained in:
parent
1eb330ae34
commit
d63712571b
|
@ -13,7 +13,9 @@ I've sought to optimize the code not just for correctness and performance,
|
||||||
but also to make for a clear and enjoyable read,
|
but also to make for a clear and enjoyable read,
|
||||||
illuminating anything that could otherwise be obscure or subtle.
|
illuminating anything that could otherwise be obscure or subtle.
|
||||||
|
|
||||||
I hope it brings you some of the joy it's brought me. 😊
|
I hope it brings you some of the
|
||||||
|
`joy <https://joy.recurse.com/posts/148-bidict>`__
|
||||||
|
it's brought me. 😊
|
||||||
|
|
||||||
|
|
||||||
Python syntax hacks
|
Python syntax hacks
|
||||||
|
@ -47,11 +49,13 @@ as appropriate.
|
||||||
|
|
||||||
Factoring the code to maximize reuse, modularity, and
|
Factoring the code to maximize reuse, modularity, and
|
||||||
adherence to `SOLID <https://en.wikipedia.org/wiki/SOLID>`__ design principles
|
adherence to `SOLID <https://en.wikipedia.org/wiki/SOLID>`__ design principles
|
||||||
|
(while not missing any chances for special-case optimizations)
|
||||||
has been one of the most fun parts of working on bidict.
|
has been one of the most fun parts of working on bidict.
|
||||||
|
|
||||||
To see how this is done, check out this code:
|
To see how this is done, check out this code:
|
||||||
|
|
||||||
- `_base.py <https://github.com/jab/bidict/blob/master/bidict/_base.py#L10>`__
|
- `_base.py <https://github.com/jab/bidict/blob/master/bidict/_base.py#L10>`__
|
||||||
|
- `_delegating_mixins.py <https://github.com/jab/bidict/blob/master/bidict/_delegating_mixins.py#L10>`__
|
||||||
- `_frozenbidict.py <https://github.com/jab/bidict/blob/master/bidict/_frozenbidict.py#L10>`__
|
- `_frozenbidict.py <https://github.com/jab/bidict/blob/master/bidict/_frozenbidict.py#L10>`__
|
||||||
- `_mut.py <https://github.com/jab/bidict/blob/master/bidict/_mut.py#L10>`__
|
- `_mut.py <https://github.com/jab/bidict/blob/master/bidict/_mut.py#L10>`__
|
||||||
- `_bidict.py <https://github.com/jab/bidict/blob/master/bidict/_bidict.py#L10>`__
|
- `_bidict.py <https://github.com/jab/bidict/blob/master/bidict/_bidict.py#L10>`__
|
||||||
|
@ -74,7 +78,7 @@ Bidict shows how fundamental data structures
|
||||||
can be implemented in Python for important real-world usage,
|
can be implemented in Python for important real-world usage,
|
||||||
with practical concerns at top of mind.
|
with practical concerns at top of mind.
|
||||||
Come to catch sight of a real, live, industrial-strength linked list in the wild.
|
Come to catch sight of a real, live, industrial-strength linked list in the wild.
|
||||||
Stay for the rare, exotic bidirectional mappings breeds you'll rarely see at home.
|
Stay for the rare, exotic bidirectional mapping breeds you'll rarely see at home.
|
||||||
[#fn-data-struct]_
|
[#fn-data-struct]_
|
||||||
|
|
||||||
.. [#fn-data-struct] To give you a taste:
|
.. [#fn-data-struct] To give you a taste:
|
||||||
|
@ -188,6 +192,13 @@ Python surprises, gotchas, regrets
|
||||||
Hence :ref:`eq-order-insensitive` for ordered bidicts,
|
Hence :ref:`eq-order-insensitive` for ordered bidicts,
|
||||||
and their separate :meth:`~bidict.FrozenOrderedBidict.equals_order_sensitive` method.
|
and their separate :meth:`~bidict.FrozenOrderedBidict.equals_order_sensitive` method.
|
||||||
|
|
||||||
|
- If you define a custom :meth:`~object.__eq__` on a class,
|
||||||
|
it will *not* be used for ``!=`` comparisons on Python 2 automatically;
|
||||||
|
you must explicitly add an :meth:`~object.__ne__` implementation
|
||||||
|
that calls your :meth:`~object.__eq__` implementation.
|
||||||
|
If you don't, :meth:`object.__ne__` will be used instead,
|
||||||
|
which behaves like ``is not``. Python 3 thankfully fixes this.
|
||||||
|
|
||||||
|
|
||||||
Better memory usage through ``__slots__``
|
Better memory usage through ``__slots__``
|
||||||
=========================================
|
=========================================
|
||||||
|
@ -231,7 +242,7 @@ Here's a larger one:
|
||||||
>>> from collections import namedtuple
|
>>> from collections import namedtuple
|
||||||
>>> from itertools import count
|
>>> from itertools import count
|
||||||
|
|
||||||
>>> class Node(namedtuple('_Node', 'cost tiebreaker data parent')):
|
>>> class Node(namedtuple('_Node', 'cost tiebreaker data parent depth')):
|
||||||
... """Represent nodes in a graph traversal. Suitable for use with e.g. heapq."""
|
... """Represent nodes in a graph traversal. Suitable for use with e.g. heapq."""
|
||||||
...
|
...
|
||||||
... __slots__ = ()
|
... __slots__ = ()
|
||||||
|
@ -240,11 +251,8 @@ Here's a larger one:
|
||||||
... # Give call sites a cleaner API for creating new Nodes
|
... # Give call sites a cleaner API for creating new Nodes
|
||||||
... def __new__(cls, cost, data, parent=None):
|
... def __new__(cls, cost, data, parent=None):
|
||||||
... tiebreaker = next(cls._counter)
|
... tiebreaker = next(cls._counter)
|
||||||
... return super(Node, cls).__new__(cls, cost, tiebreaker, data, parent)
|
... depth = parent.depth + 1 if parent else 0
|
||||||
...
|
... return super(Node, cls).__new__(cls, cost, tiebreaker, data, parent, depth)
|
||||||
... @property
|
|
||||||
... def depth(self):
|
|
||||||
... return self.parent.depth + 1 if self.parent else 0
|
|
||||||
...
|
...
|
||||||
... def __repr__(self):
|
... def __repr__(self):
|
||||||
... return 'Node(cost={cost}, data={data!r})'.format(**self._asdict())
|
... return 'Node(cost={cost}, data={data!r})'.format(**self._asdict())
|
||||||
|
@ -305,6 +313,17 @@ How to deeply integrate with Python's :mod:`collections` and other built-in APIs
|
||||||
:class:`list` is a subclass of :class:`object`,
|
:class:`list` is a subclass of :class:`object`,
|
||||||
but :class:`list` is not a subclass of :class:`~collections.abc.Hashable`.
|
but :class:`list` is not a subclass of :class:`~collections.abc.Hashable`.
|
||||||
|
|
||||||
|
- What if we needed to add a second metaclass
|
||||||
|
in addition to :class:`~bidict.BidirectionalMapping`
|
||||||
|
(e.g. to conditionally implement an optimized version of some methods
|
||||||
|
based on the type of ``_fwmd_cls``,
|
||||||
|
as ``_delegating_mixins.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/.
|
||||||
|
|
||||||
- :class:`collections.abc.Mapping` and
|
- :class:`collections.abc.Mapping` and
|
||||||
:class:`collections.abc.MutableMapping`
|
:class:`collections.abc.MutableMapping`
|
||||||
don't implement :meth:`~abc.ABCMeta.__subclasshook__`,
|
don't implement :meth:`~abc.ABCMeta.__subclasshook__`,
|
||||||
|
@ -435,18 +454,10 @@ Portability
|
||||||
|
|
||||||
- Python 2 vs. Python 3
|
- Python 2 vs. Python 3
|
||||||
|
|
||||||
- Mostly :class:`dict` API changes,
|
- As affects bidict, mostly :class:`dict` API changes,
|
||||||
but also functions like :func:`zip`, :func:`map`, :func:`filter`, etc.
|
but also functions like :func:`zip`, :func:`map`, :func:`filter`, etc.
|
||||||
|
|
||||||
- If you define a custom :meth:`~object.__eq__` on a class,
|
- See the :meth:`~object.__ne__` gotcha for Python 2 above.
|
||||||
it will *not* be used for ``!=`` comparisons on Python 2 automatically;
|
|
||||||
you must explicitly add an :meth:`~object.__ne__` implementation
|
|
||||||
that calls your :meth:`~object.__eq__` implementation.
|
|
||||||
If you don't, :meth:`object.__ne__` will be used instead,
|
|
||||||
which behaves like ``is not``.
|
|
||||||
GOTCHA alert!
|
|
||||||
|
|
||||||
Python 3 thankfully fixes this.
|
|
||||||
|
|
||||||
- Borrowing methods from other classes:
|
- Borrowing methods from other classes:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue