paul@44 | 1 | Micropython: A minimal implementation of Python for constrained devices
|
paul@44 | 2 |
|
paul@153 | 3 | * "Full strength" Python:
|
paul@153 | 4 | * Introspection, interactivity, PEP/reference-compliance
|
paul@153 | 5 | * CPython, Jython, IronPython, PyPy, CLPython, etc.
|
paul@153 | 6 | * "Reduced strength" Python:
|
paul@153 | 7 | * Remove "luxury" semantics
|
paul@153 | 8 | * Shed Skin, RPython, etc.
|
paul@44 | 9 |
|
paul@44 | 10 | Motivations
|
paul@44 | 11 |
|
paul@44 | 12 | * Run Python programs in "small" devices
|
paul@94 | 13 | * Small programs plus few executed instructions
|
paul@94 | 14 | * Avoid expensive library code
|
paul@94 | 15 | (small footprint, lots of executed instructions)
|
paul@44 | 16 |
|
paul@44 | 17 | Python's flexibility comes at a cost
|
paul@44 | 18 |
|
paul@44 | 19 | * Modules, classes, instances are all objects
|
paul@44 | 20 | * Freedom to modify most objects at any time
|
paul@44 | 21 | * Difficult to predict eventual behaviour before execution
|
paul@44 | 22 | * Difficult to generate optimisations when compiling
|
paul@44 | 23 |
|
paul@94 | 24 | Not all things are dynamic/equal in Python
|
paul@94 | 25 |
|
paul@94 | 26 | * Locals, modules and classes are special in some way
|
paul@94 | 27 | * Locals don't tend to change unilaterally
|
paul@153 | 28 | * Parameter lists don't tend to change if fully specified
|
paul@94 | 29 | * Modules usually retain their identity
|
paul@94 | 30 | * Classes differ from objects (despite metaclasses)
|
paul@94 | 31 |
|
paul@44 | 32 | Attribute access
|
paul@44 | 33 |
|
paul@44 | 34 | * Must do a full lookup every time:
|
paul@44 | 35 | * Check instance dictionary
|
paul@44 | 36 | * Check class, superclasses
|
paul@44 | 37 | * Potentially lots of code
|
paul@44 | 38 | * Lots of executed instructions
|
paul@44 | 39 |
|
paul@62 | 40 | Improving attribute access performance
|
paul@62 | 41 |
|
paul@62 | 42 | * Achieve faster access using a different representation
|
paul@62 | 43 | * Must define attributes of objects in advance
|
paul@62 | 44 | * Restriction: modules, classes, instances are "closed"
|
paul@62 | 45 | * Evaluate the limitations: are they too disruptive?
|
paul@62 | 46 |
|
paul@62 | 47 | Consequences of revised attribute access
|
paul@62 | 48 |
|
paul@94 | 49 | * Cannot extend the range of attributes on objects of existing classes
|
paul@62 | 50 | * Further optimisations:
|
paul@62 | 51 | * Restriction: attempt to control modification of attributes
|
paul@62 | 52 | * Result: further optimisation of accesses
|
paul@62 | 53 |
|
paul@62 | 54 | Invocations
|
paul@62 | 55 |
|
paul@62 | 56 | * Target checking
|
paul@62 | 57 | * Number of arguments vs. number of parameters
|
paul@62 | 58 | * Keyword parameter resolution
|
paul@62 | 59 | * Defaults
|
paul@62 | 60 |
|
paul@44 | 61 | Other costly operations
|
paul@44 | 62 |
|
paul@44 | 63 | * Binary operators
|
paul@44 | 64 | * Comparisons
|
paul@44 | 65 |
|
paul@44 | 66 | Examples of rarely used/unnecessary flexibility
|
paul@44 | 67 |
|
paul@44 | 68 | * Can subclass dynamically:
|
paul@44 | 69 |
|
paul@44 | 70 | for cls in A, B:
|
paul@44 | 71 | class C(cls):
|
paul@44 | 72 | pass
|
paul@44 | 73 |
|
paul@44 | 74 | * Yet most people would relate to "class C"
|
paul@44 | 75 | * Not "the class currently known as C"
|
paul@44 | 76 |
|
paul@44 | 77 | Examples of occasionally used flexibility
|
paul@44 | 78 |
|
paul@44 | 79 | * Can evaluate classes and functions in dynamic contexts:
|
paul@44 | 80 |
|
paul@44 | 81 | if platform == 'posix':
|
paul@44 | 82 | class C:
|
paul@44 | 83 | ...
|
paul@44 | 84 | else:
|
paul@44 | 85 | class C:
|
paul@44 | 86 | ...
|
paul@44 | 87 |
|
paul@44 | 88 | * Seen quite often in the standard library with functions
|
paul@44 | 89 | * Dynamism used for configuration purposes
|
paul@44 | 90 |
|
paul@44 | 91 | Distinguish between "good" and "bad" dynamism
|
paul@44 | 92 |
|
paul@44 | 93 | * Dynamic class preparation
|
paul@44 | 94 | * Run-time choice of classes
|
paul@44 | 95 | * Assigning attributes to modules
|
paul@44 | 96 |
|
paul@44 | 97 | Run-time configuration
|
paul@44 | 98 |
|
paul@44 | 99 | * The source of a lot of "bad" dynamism
|
paul@44 | 100 | * Comparable to, but more robust than...
|
paul@44 | 101 | * Class loading tricks in Java
|
paul@44 | 102 | * Dynamic library loading magic in C/C++
|
paul@44 | 103 | * Has a place, but perhaps not in compiled, embedded programs
|
paul@49 | 104 |
|
paul@49 | 105 | Micropython modules
|
paul@49 | 106 |
|
paul@49 | 107 | * Modules contain attributes as with normal Python
|
paul@49 | 108 | * Inside the module:
|
paul@49 | 109 | * Attributes can be accessed and set as globals
|
paul@49 | 110 | * Classes and functions define module attributes
|
paul@49 | 111 | * Outside the module:
|
paul@49 | 112 | * Attributes can be accessed but not set
|
paul@557 | 113 | * This can be relaxed with attribute usage observations
|
paul@49 | 114 | * Definition from within means more predictable content
|
paul@49 | 115 |
|
paul@49 | 116 | Micropython classes
|
paul@49 | 117 |
|
paul@49 | 118 | * Classes contain attributes and expose superclass attributes
|
paul@49 | 119 | * Inside the class:
|
paul@49 | 120 | * Attributes can be accessed and set in the class scope
|
paul@49 | 121 | * Functions define methods
|
paul@49 | 122 | * Outside the class:
|
paul@49 | 123 | * Attributes can be accessed but not set
|
paul@557 | 124 | * This can be relaxed with attribute usage observations
|
paul@49 | 125 | * Definition from within means more predictable content
|
paul@49 | 126 |
|
paul@49 | 127 | Micropython instances
|
paul@49 | 128 |
|
paul@49 | 129 | * Instances contain attributes and expose class attributes
|
paul@49 | 130 | * Instance attributes must not shadow class attributes
|
paul@49 | 131 | * The set of attributes is detected by scanning the __init__ method
|
paul@49 | 132 |
|
paul@49 | 133 | Rationale for restrictions
|
paul@49 | 134 |
|
paul@49 | 135 | * Construct efficient run-time representations
|
paul@49 | 136 | * Predictable content means that access can be optimised
|
paul@49 | 137 | * No shadowing means that only a single lookup is necessary
|
paul@53 | 138 |
|
paul@53 | 139 | References, attributes and values
|
paul@53 | 140 |
|
paul@53 | 141 | * Almost everything can be considered as a kind of attribute:
|
paul@53 | 142 | * Module, class, instance
|
paul@53 | 143 | * Local variable is the exception
|
paul@53 | 144 | * Acquired attributes are "values":
|
paul@53 | 145 | * An object being manipulated
|
paul@53 | 146 | * Its context
|
paul@53 | 147 | * Most kinds of values have no real context:
|
paul@53 | 148 | * Module and class attributes, locals
|
paul@53 | 149 | * The exception:
|
paul@53 | 150 | * Instance attributes
|
paul@266 | 151 |
|
paul@266 | 152 | Wild idea: isn't AttributeError just evidence of a bug in the program?
|
paul@266 | 153 |
|
paul@266 | 154 | * Note down all attributes used by a particular name during its lifetime
|
paul@266 | 155 | * Upon recording name usage, record the location of usage
|
paul@266 | 156 | * Use the set of attributes to constrain the range of types that can be used
|
paul@266 | 157 | without an AttributeError being raised
|
paul@266 | 158 | * Convert all name usage to specific attribute usage (on the range of types
|
paul@266 | 159 | found) for all attribute accesses
|
paul@266 | 160 |
|
paul@266 | 161 | Attributes on locals
|
paul@266 | 162 |
|
paul@266 | 163 | * Control-flow can make attribute tracking awkward:
|
paul@266 | 164 |
|
paul@266 | 165 | obj.x # obj must have x
|
paul@385 | 166 | if obj.p: # obj must have x, p
|
paul@385 | 167 | # (branch)
|
paul@266 | 168 | obj = ... # obj reset
|
paul@266 | 169 | obj.attr # obj must have attr
|
paul@385 | 170 | # (end of branch)
|
paul@266 | 171 | else: # (branch)
|
paul@385 | 172 | obj.name # obj must have x, p, name
|
paul@385 | 173 | # (end of branch)
|
paul@385 | 174 | # (merge branch ends)
|
paul@385 | 175 | # obj must have either attr or x, p, name
|
paul@266 | 176 |
|
paul@266 | 177 | Attributes on locals with loops
|
paul@266 | 178 |
|
paul@266 | 179 | * Loops complicate matters still further:
|
paul@266 | 180 |
|
paul@266 | 181 | obj.x # obj must have x
|
paul@385 | 182 | while obj.p: # obj must have x, p
|
paul@385 | 183 | # (branch)
|
paul@385 | 184 | obj.y # obj must have x, p, y
|
paul@266 | 185 | obj = ... # obj reset
|
paul@266 | 186 | obj.z # obj must have z
|
paul@385 | 187 | # (re-test)
|
paul@385 | 188 | # obj must have z, p
|
paul@385 | 189 | # (end of branch)
|
paul@385 | 190 | # (null branch - no else)
|
paul@385 | 191 | # (merge branch ends)
|
paul@385 | 192 | # obj must have either z, p (from loop) or x, p (from null branch)
|
paul@266 | 193 |
|
paul@266 | 194 | Attributes on attributes
|
paul@266 | 195 |
|
paul@266 | 196 | * Classes and modules should preserve single interpretations of attributes
|
paul@266 | 197 | * Attributes on such attributes should accumulate on the targets
|
paul@266 | 198 | * Tracking attributes on such targets is only useful for unknown targets
|
paul@266 | 199 | * Instances cannot be relied upon to refer to a coherent set of targets
|