Everyone loves Python’s dictionaries; they’re fast, easy to create and quite handy for a range of reasons. However, there are times that ["typing"]["out"]["all"]["those"] extra quotes and brackets seems excessive. Wouldn’t it be nicer to access.them.like.class.methods?
Say hello to box.

from box import Box
movie_data = {
"movies": {
"Spaceballs": {
"imdb_stars": 7.1,
"rating": "PG",
"length": 96,
"Director": "Mel Brooks",
"Stars": [{"name": "Mel Brooks", "imdb": "nm0000316", "role": "President Skroob"},
{"name": "John Candy","imdb": "nm0001006", "role": "Barf"},
{"name": "Rick Moranis", "imdb": "nm0001548", "role": "Dark Helmet"}
]
},
"Robin Hood: Men in Tights": {
"imdb_stars": 6.7,
"rating": "PG-13",
"length": 104,
"Director": "Mel Brooks",
"Stars": [
{"name": "Cary Elwes", "imdb": "nm0000144", "role": "Robin Hood"},
{"name": "Richard Lewis", "imdb": "nm0507659", "role": "Prince John"},
{"name": "Roger Rees", "imdb": "nm0715953", "role": "Sheriff of Rottingham"},
{"name": "Amy Yasbeck", "imdb": "nm0001865", "role": "Marian"}
]
}
}
}
my_box = Box(movie_data)
my_box.movies.Spaceballs.rating
'PG'
my_box.movies.Spaceballs.Stars[0].name
'Mel Brooks'
my_box.movies.Spaceballs.Stars[0]
# <Box: {'name': 'Mel Brooks', 'imdb': 'nm0000316', 'role': 'President Skroob'}>
Box is a creation I made over three years ago, originally in the reusables code base named Namespace, inspired by JavaScript Object access methods.
Install is super simple:
pip install python-box
Or just grab the file box.py directly from the github project.
Every Box is usable as a drop in replacement to dictionaries in 99%* of cases. And every time you add a dictionary or list to a Box object, they become Box (subclass of dict) or BoxList (subclass of list) objects as well.
type(my_box)
# box.Box
assert isinstance(my_box, dict)
type(my_box.movies.Spaceballs.Stars)
# box.BoxList
assert isinstance(my_box.movies.Spaceballs.Stars, list)
my_box.movies.Spaceballs.Stars[0].additional_info = {'Birth name': 'Melvin Kaminsky', 'Birthday': "05/28/1926"}
my_box.movies.Spaceballs.Stars[0].additional_info
# <Box: {'Birth name': 'Melvin Kaminsky', 'Birthday': '05/28/1926'}>
At any level you can change a Box object back into a standard dictionary.
my_box.movies.Spaceballs.to_dict()
{'Director': 'Mel Brooks',
'Stars': [
{'additional_info': {'Birth name': 'Melvin Kaminsky', 'Birthday': '05/28/1926'},
'imdb': 'nm0000316',
'name': 'Mel Brooks',
'role': 'President Skroob'},
{'imdb': 'nm0001006', 'name': 'John Candy', 'role': 'Barf'},
{'imdb': 'nm0001548', 'name': 'Rick Moranis', 'role': 'Dark Helmet'},
{'imdb': 'nm0000597', 'name': 'Bill Pullman', 'role': 'Lone Starr'}],
'imdb_stars': 7.1,
'length': 96,
'rating': 'PG'}
You can also run to_list() on lists in the Box to return them to a standard list, with all inner Box and BoxList objects transformed back to normal.
Box also has built in functions for dealing with json and yaml**.
my_box.movies.Spaceballs.to_json()
# {
# "imdb_stars": 7.1,
# "rating": "PG",
# "length": 96,
# "Director": "Mel Brooks",
# "Stars": [
# ...
my_box.movies.Spaceballs.to_yaml()
# Director: Mel Brooks
# imdb_stars: 7.1
# length: 96
# rating: PG
# Stars:
# - imdb: nm0000316
# name: Mel Brooks
# role: President Skroob
# ...
Calling a Box object will return it’s keys. It’s also possible to access the attributes the standard dictionary method, which is required for keys that are numeric or have spaces.
my_box.movies()
# ('Spaceballs', 'Robin Hood: Men in Tights')
my_box.movies['Robin Hood: Men in Tights']
# <Box: {'imdb_stars': 6.7, 'rating': 'PG-13', 'length': 104, ...
Unlike addict it does not act as a default dictionary, so you will get built-in errors if you try to access something that isn’t there.
my_box.tv_shows # Traceback (most recent call last): # ... # AttributeError: tv_shows
Another power previously mentioned is that you can add dictionaries into lists and they will automatically be converted into Box objects.
my_box.movies.Spaceballs.Stars.append(
{"name": "Bill Pullman", "imdb": "nm0000597", "role": "Lone Starr"})
my_box.moves.Spaceballs.Stars[-1].name
'Bill Pullman'
It also protects itself from having its functions overwritten accidentally.
my_box.to_dict = '3' # AttributeError: Key name 'to_dict' is protected
Box is also a substitute for the Namespace used by argparse, making it super easy to convert incoming arguments to a dict if wanted. This allows incoming arguments to be easily passed to function arguments.
import argparse
from box import Box
parser = argparse.ArgumentParser()
parser.add_argument('floats', metavar='N', type=float, nargs='+')
parser.add_argument("-v", "--verbosity", action="count", default=0)
args = parser.parse_args(['1', '2', '3', '-vv'], namespace=Box())
print(args.to_dict())
{'floats': [1.0, 2.0, 3.0], 'verbosity': 2}
def example_func(floats, verbosity):
print(verbosity)
example_func(**args)
2
If you have any questions, suggestions or feedback, please open a github issue and let me know!
Hope you enjoy!
Caveats
* Based off nothing but pure guess and personal experience. Only time drop in replacement doesn’t work is when converting or dumping. So make sure do use first for those cases.
** If you don’t have PyYAML installed, the to_yaml function will not be available.