Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Overview of python-mapbind

Build Status

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)

The 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):
    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] == [9001] * 5


"Can't I just do locals().update(data)?"

That only works at the global/module scope. Once you're inside of a function, 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.

Supported Versions

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

original git repository:


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