Overview of python-mapbind
Functions that inspect their calling frame to figure out how you were going to bind their results, and returns values appropriate for the bindings.
A friend gave me the idea, and I wanted to see if it was possible. Spoilers: it totally was.
Consider the following
# if you find yourself having to write code like this a = data["a"] b = data["b"] c = data["c"] # or like this a, b, c = map(data.get, ("a", "b", "c")) # then stop it. Use mapbind instead: from mapbind import mapbind a, b, c = mapbind(data)
mapbind function looks at its calling frame's bytecode, figures
out that the result is going to be used in an unpacking assignment,
finds the names of the variables the assignment would bind to, and
returns an iterator for the values of the given items in data with
matching keys. Supercool.
Also included are
objbind which will find attributes on an object,
funbind which will call a function for each binding name, and
takebind which will return the right amount of values from a
sequence to fulfill the count of bindings.
from mapbind import objbind # objbind will notice that its assignment is to the variables named "a", # "b", and "c" and will subsequently fetch attributes with those names # from some_object and return a 3-tuple of their values. a, b, c = objbind(some_object) assert a == some_object.a assert b == some_object.b assert c == some_object.c
from mapbind import funbind accu =  def accumulator(name): accu.append(name) return "|%s|" % name # funbind will notice that its assignment is to the variables named "a", # "b", and "c" and will subsequently invoke accumulator with those strings # in turn, returning a 3-tuple of their values. a, b, c = funbind(accumulator) assert accu == ["a", "b", "c"] assert a == "|a|" assert b == "|b|" assert c == "|c|"
from mapbind import takebind # Even though a range is huge, takebind will snag exactly as many items # from it as you're going to assign to. seq = range(0, 100) a, b = takebind(seq) assert a == 0 assert b == 1 # And if you have too small of a range, you can tell takebind how you'd # like it to pad out any missing assignments seq = range(0, 2) a, b, c, d, e, f, g = takebind(seq, 9001) assert a == 0 assert b == 1 assert [c, d, e, f, g] ==  * 5
"Can't I just do
That only works at the global/module scope. Once you're inside of a
locals() is nothing but a snapshot of the underlying fast,
free, and cell variables in a call frame.
What Doesn't It Do
Mapbind's purpose is specific to unpacking for variable assignment. Not for item assignment, not for member assignment. All of the following will result in an exception being raised.
# You only want one member? Then just get it directly, no need for # mapbind. This is an error. a = mapbind(my_data) # this kind of nesting makes zero sense in the context of mapbind, so # don't do it. If you do, it's an error. a, (b, c) = mapbind(my_data) # copying from one map to another is something you can do already, no # need for mapbind, so this is an error. x["a"], x["b"], x["c"] = mapbind(my_data) # we don't support this because the assignment operations are # interleaved with expressions to load their owning object. x.a, y.b, z.c = mapbind(my_data)
It is trivial to write a function that will update a map or an object's attributes from a map given a list of keys. There's no reason for mapbind to support that use case. Mapbind is specifically for situations where the list of keys can be determined entirely from the storage variable names.
This has been tested as working on the following versions and implementations of Python
- Python 2.6, 2.7
- Python 3.3, 3.4, 3.5, 3.6, 3.7
- PyPy, PyPy3
It is implemented entirely in Python (no native extensions). It has no runtime dependencies outside of itself and those provided as part of the standard Python environment, though to run the unit tests you'll need setuptools.
author: Christopher O'Brien email@example.com
original git repository: https://github.com/obriencj/python-mapbind
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org/licenses/.