Skip to content

Traps for New Players

Alice Zoë Bevan–McGregor edited this page Jul 1, 2020 · 28 revisions

These are issues that have arisen and been discussed, collected together and polished up for collective benefit. Almost a "frequently asked questions", but more problem-driven.

Contents

  1. The data coming in appears missing or mangled. TL;DR

The data coming in appears missing or mangled.

A common pattern when processing large amounts of data is to "glob" all keyword arguments into a dictionary for processing within the endpoint. This can be a useful shortcut when describing an entire form structure using an argument list specification is tedious or excessive, when you truly accept an unrestricted potential set of key-value pairs, or for when you are utilizing a third-party tool for form processing and just need "all of the submitted data at once" to process.

The pattern in its simplest functional form appears as:

def endpoint(**data):
	...  # Do something with `data`.

This will collect all (named) keyword arguments into a dictionary, see the example function declarations from the tutorial for demonstrations of use.

Dictionary Interactions

The common trap comes from a frequent misunderstanding as to how to interact with dictionaries (and mappings more generally) in Python, especially for developers coming from languages such as JavaScript where there is essentially no differentiation between an object (in relation to method of access) and a dictionary or mapping. Use of obj as a temporary variable name, actually containing a dictionary, is a common warning sign. This can lead to an attempt to utilize object introspection and manipulation tools against a vastly different fundamental type:

def endpoint(**data):
	for k in dir(data):
		getattr(data, k)

Despite not really doing anything, this demonstrates the signature of the problem: use of dir() to enumerate the attributes of an object, and use of getattr() to retrieve the value of an attribute by name. Fundamental types (scalars and containers) in Python are dual-natured, so unless explicitly utilizing a dictionary-like object advertised as an "attribute access dictionary", data elements are not attributes.

Deeper into Python Datatypes

The constant 27 is the number twenty seven to humans, an instance of an integer containing the value twenty seven, and a label used to retrieve the integer value twenty seven. Many scalar types (integers, floats, so forth) are understandably "read-only", in that you can't redefine the value of two. Strings are also read-only structures; they are not mutable arrays of characters as in C. Lists are technically pre-allocated packed arrays, and dictionaries are, of course, hash maps.

"Objects" in Python are, in general, built on top of dictionaries. ("Turtles all the way down.") Most user-defined classes and instances will have a __dict__ attribute that is a dictionary mapping of values defined at that scope. The core types do not, as they are C-backed and not dynamically allocated. (For further reading, look into the difference between class __init__ and __new__, and __slots__.) As a result, a number of standardized protocols have arisen to implement higher-level functionality such as the syntax of dictionary and list indexing/dereferencing (__getitem__) and generalized iteration (__iter__).

This is all a very long-winded way of approaching while explaining the how and the why of it, the simple idea that as you can "iterate a list" to iterate the values contained within that container type, you can "iterate a dictionary" to iterate the keys contained within that dictionary:

Dictionary Iteration TL;DR

Python dictionaries are not JavaScript objects which permit simultaneous attribute-based and index-dereferenced access. Do not iterate attributes via dir(), iterate the dictionary itself, similar to the difference between for…in and for…of.

def endpoint(**data):
	for key in data:
		data[key]

Additionally there is a .keys() method returning an iterable view upon those keys, but simple iteration is usually optimal 95% of the time. See also: .values() and .items() for iterating the values themselves, or the combined set as a series of 2-tuples:

def endpoint(**data):
	for key, value in data.items():
		...  # Do something with the key and value.

As a side-effect of being iterable, you may pass a dictionary to the constructor of other iterable container types such as list(data), tuple(data), or set(data), in order to get a list, tuple, or literal set of the keys, as appropriate.


"Trap for new players" stolen without hesitation from EEVblog, as a fairlyfrequently utilized term.