Package: items
Attributes are the most straightforward and convenient to access composite data
in many situations.item.nameis neater, more readable, and more concise
than the indexing styleitem['name']typical of dictionaries. Having
attribute access often is the difference between being able to easily
de-reference a component ofitemdirectly and deciding to store that
attribute in a completely separate variable for clarity (item_name =item['name']).In traversing data structures from XML, JSON, and other typically-nested data
sources, concise direct access can clean up code considerably.Itemsitemstherefore providesItem, a convenient attribute-accessibledictsubclass,
plus helper functions.itemize, for example, helps iterate through a sequence of dictionaries, as often found
in JSON processing: Each record is handed back as anItemrather than a Pythondict.A typical progression would be from:foritemindata:item_name=item['name']# ...print(item_name)tofromitemsimportitemizeforiteminitemize(data):# ...print(item.name)To process a sequence wholesale, returning alist:fromitemsimportitemize_allitemize_all(data)If you’re iterating over a sequence of tuples (or lists) rather than
dictionaries, you can still useitemizeby providing the field names
you wnat assigned.parser=...foriteminitemize(parser,fields='prefix token value'):ifitem.prefixisNoneanditem.token=='start_array':...Here each result returned byparser(typically a Python generator)
is converted from a tuple (or list) into anItem.  Now you have several values
conveniently packaged in a name-accessible way without having to create
a separatenamedtuplefor this result type, and without any need for
tuple positional indexing.You can even do this for a scalar sequence:for item in itemize(‘aeiou’, fields=’vowel’):item.value = 20 if item.vowel == ‘e’ else 15Beyond graceful handling of single-valued sequences, this example demonstrates
the mutability of eachItem.namedtuplesare grand as return types,
but they cannot be easily extended or annotated by subsequent processing…a
common requirement for many algorithms.Diving DeeperItemsubclassescollections.OrderedDict, so keys are ordered the same
as when your program first encountered them. The performance overhead of
ordered mappings is minimal in most development contexts, especially in
exploratory and data-cleanup tasks. Whatever overhead there is is more than
made up for by the programming and debugging clarity of not having keys occur
in seemingly randomized order.Items are also permissive, in a way thatdictand its variants usually
are not: If you accessitem.some_attributewhere the attribute does
not exist, you donotraise aKeyError, unlike typical
Python dictionaries. Instead you getEmpty, a designated, false-y
value similar to, but distinct from,None. This is convenient for
processing data which is irregular or not uniformly filled-in, because you do
not need the constant “guard conditions”–ifstatements ortry/except KeyErrorblocks–to protect against cases where this data
value or that is missing. UsingEmptyinstead ofNonepreserves your
ability to useNonein cases where it’s semantically important. For
example, in parsing JSON,Noneis returned from JSON’snullvalue.Emptyobjects are infinitely dereferenceable. No matter how many levels of
indirection, they always just hand back themselves–the same gentle “nothing
here, no exceptions raised” behavior. You can also iterate over anEmpty–it will simply iterate zero times. This neatly avoids the commonTypeError: 'NoneType' object is not iterableerror messages in instances
where a value can be a list–orNoneif the list is not present.fromitemsimportEmptye=Emptyasserte[1].method().there[33][0].no.attributes[99].hereisEmptyforxinEmpty:print('hey!')# never prints, because no such iterations occurFor more on the background ofEmpty, see thenulltypemodule. A typical use would be:foriteminitemize(data):ifitem.name:process(item)Items that lack names are simply not processed.The more nested, complex, and irregular your data structures, the
more valuable this becomes.Serialization and DeserializationBe careful importing data. Popular Python modules for reading JSON, YAML, and
other formats do not believe mappings are (or should be) ordered. Historically
and officially, they’re not, no matter how ordered they look, no matter that
other languages such as JavaScript take a different approach, and no matter how
many Stack Overflow questions demonstrate that ordered input and output is
strongly and broadly desired. Therefore stock input/output modules can cause
dislocation as data is parsed. Take steps to return ordered mappings from them.# YAML module that will load into OrderedDict instances, which can then# be easily converted to Item instances; based on default PyYAMLimportoyamlasyamldata=itemize_all(yaml.load(rawyaml))# modified call to json.load or json.loads to preserve order by instantiating# Item instances rather than dictimportjsondata=json.loads(rawjson,object_pairs_hook=Item)CyclesNot currently organized for handling cyclic data structures. Those do not
appear in processing JSON, XML, and other common data formats, but might
be a nice future extension.InstallationTo install or upgrade to the latest version:pip install -U itemsSometimes Python installations have different names forpip(e.g.pip,pip2, andpip3), and on systems with multiple versions of Python, whichpipgoes with which Python interpreter can become confusing. In those
cases, try runningpipas a module of the Python version you want to
install under. This can reduce conflicts and confusion:python3.6 -m pip install -U itemsOn Unix, Linux, and macOS you may need to prefix these withsudoto authorize
installation. In environments without super-user privileges, you may want to
usepip’s--useroption, to install only for a single user, rather
than system-wide.TestingIf you wish to run the module tests locally, you’ll need to installpytestandtox.  For full testing, you will also needpytest-covandcoverage. Then run one of these commands:tox                # normal run - speed optimized
tox -e py37        # run for a specific version only
tox -c toxcov.ini  # run full coverage testsNotesDoes not work on Python 2. Should work on Python 3 prior to 3.6, but not guaranteed.
Testing more difficult given different dictionary ordering rules prior to 3.6.
Since items intended as forward-looking module, not currently worth energy to retrofit
to archaic dialects.