# HG changeset patch # User Paul Boddie # Date 1203812836 -3600 # Node ID e1b70bf99b49b0256307d00215f7141286d5827a # Parent 3cc8cd3d0a1453d9441e3f286d43fea8b2f87e12 Introduced attribute position re-use for class attributes as well as instance attributes, improving the underlying mechanism. Renamed the NamespaceDict to_list method to attributes_as_list, operating only on the specific namespace defined locally on instances rather than on an arbitrary namespace dictionary. Added a finalise_attributes method to NamespaceDict in order to ensure that attributes are fully defined. Added more test data. Added some more rationale material. diff -r 3cc8cd3d0a14 -r e1b70bf99b49 docs/rationale.txt --- a/docs/rationale.txt Sat Feb 23 21:25:03 2008 +0100 +++ b/docs/rationale.txt Sun Feb 24 01:27:16 2008 +0100 @@ -69,3 +69,35 @@ * Class loading tricks in Java * Dynamic library loading magic in C/C++ * Has a place, but perhaps not in compiled, embedded programs + +Micropython modules + + * Modules contain attributes as with normal Python + * Inside the module: + * Attributes can be accessed and set as globals + * Classes and functions define module attributes + * Outside the module: + * Attributes can be accessed but not set + * Definition from within means more predictable content + +Micropython classes + + * Classes contain attributes and expose superclass attributes + * Inside the class: + * Attributes can be accessed and set in the class scope + * Functions define methods + * Outside the class: + * Attributes can be accessed but not set + * Definition from within means more predictable content + +Micropython instances + + * Instances contain attributes and expose class attributes + * Instance attributes must not shadow class attributes + * The set of attributes is detected by scanning the __init__ method + +Rationale for restrictions + + * Construct efficient run-time representations + * Predictable content means that access can be optimised + * No shadowing means that only a single lookup is necessary diff -r 3cc8cd3d0a14 -r e1b70bf99b49 micropython/__init__.py --- a/micropython/__init__.py Sat Feb 23 21:25:03 2008 +0100 +++ b/micropython/__init__.py Sun Feb 24 01:27:16 2008 +0100 @@ -116,7 +116,7 @@ # Append module attributes to the image. attributes = module.module_attributes() - image += module.to_list(attributes) + image += module.attributes_as_list() pos += len(attributes.keys()) # Append classes and functions to the image. @@ -136,7 +136,7 @@ # Append class attributes to the image. attributes = obj.class_attributes() - image += module.to_list(attributes) + image += obj.attributes_as_list() pos += len(attributes.keys()) # Class-level code is generated separately. diff -r 3cc8cd3d0a14 -r e1b70bf99b49 micropython/inspect.py --- a/micropython/inspect.py Sat Feb 23 21:25:03 2008 +0100 +++ b/micropython/inspect.py Sun Feb 24 01:27:16 2008 +0100 @@ -108,7 +108,6 @@ self.namespace = {} self.globals = set() self.global_namespace = global_namespace - self.attr_position = 0 def __getitem__(self, name): return self.namespace[name] @@ -157,8 +156,7 @@ "The underlying set operation associating 'name' with 'value'." if not self.namespace.has_key(name): - self.namespace[name] = Attr(self.attr_position, self, name, value) - self.attr_position += 1 + self.namespace[name] = Attr(None, self, name, value) return self.namespace[name] def __delitem__(self, name): @@ -188,12 +186,22 @@ else: return None - def to_list(self, d): - l = [None] * len(d.keys()) - for attr in d.values(): + def attributes_as_list(self): + self.finalise_attributes() + l = [None] * len(self.keys()) + for attr in self.values(): l[attr.position] = attr return l + def finalise_attributes(self): + + "Make sure all attributes are fully defined." + + # The default action is to assign attribute positions sequentially. + + for i, attr in enumerate(self.values()): + attr.position = i + class Naming: "A mix-in providing naming conveniences." @@ -284,9 +292,9 @@ self.bases = [] self.instattr = set() # instance attributes - self.instattr_relocated = set() # instance attributes which do not have - # the same position as those of the same - # name in some superclasses + self.relocated = set() # attributes which do not have the same + # position as those of the same name in + # some superclasses # Caches. @@ -308,6 +316,15 @@ else: return "Class(%r, %r)" % (self.name, self.parent_name) + def finalise_attributes(self): + + "Make sure that all attributes are fully defined." + + self.finalise_class_attributes() + self.finalise_instance_attributes() + + # Class-specific methods. + def add_base(self, base): self.bases.append(base) @@ -322,6 +339,7 @@ "Return class attributes provided by this class only." + self.finalise_class_attributes() return self def all_class_attribute_names(self): @@ -336,19 +354,46 @@ "Return all class attributes, indicating the class which provides them." + self.finalise_class_attributes() + return self.all_classattr + + def finalise_class_attributes(self): + + "Make sure that the class attributes are fully defined." + if self.all_classattr is None: self.all_classattr = {} + clsattr = {} + + # Record provisional position information for attributes of this + # class. + + for name in self.class_attributes().keys(): + clsattr[name] = set() # position not yet defined reversed_bases = self.bases[:] reversed_bases.reverse() + + # For the bases in reverse order, acquire class attribute details. + for cls in reversed_bases: - self.all_classattr.update(cls.all_class_attributes()) + for name, attr in cls.all_class_attributes().items(): + self.all_classattr[name] = attr - # Record attributes provided by this class, along with their - # positions. + # Record previous attribute information. + + if clsattr.has_key(name): + clsattr[name].add(attr.position) + + # Record class attributes provided by this class and its bases, + # along with their positions. self.all_classattr.update(self.class_attributes()) + if clsattr: + for i, name in enumerate(self._get_position_list(clsattr)): + self.all_classattr[name].position = i + return self.all_classattr def instance_attribute_names(self): @@ -363,10 +408,23 @@ "Return instance-only attributes for instances of this class." + self.finalise_instance_attributes() + return self.all_instattr + + def finalise_instance_attributes(self): + + "Make sure that the instance attributes are fully defined." + if self.all_instattr is None: self.all_instattr = {} instattr = {} + # Record provisional position information for attributes of this + # instance. + + for name in self.instattr: + instattr[name] = set() # position not yet defined + reversed_bases = self.bases[:] reversed_bases.reverse() @@ -375,16 +433,11 @@ for cls in reversed_bases: for name, attr in cls.instance_attributes().items(): - if not instattr.has_key(name): - instattr[name] = set() - instattr[name].add(attr.position) + + # Record previous attribute information. - # Record instance attributes provided by this class and its bases, - # along with their positions. - - for name in self.instattr: - if not instattr.has_key(name): - instattr[name] = set([-1]) # position not yet defined + if instattr.has_key(name): + instattr[name].add(attr.position) # Cache the attributes by converting the positioned attributes into # a dictionary. @@ -392,48 +445,63 @@ if not instattr: self.all_instattr = {} else: - self.all_instattr = dict(self._get_position_list(instattr)) + self.all_instattr = self._get_attributes(instattr) self.all_instattr_names = self.all_instattr.keys() return self.all_instattr - def _get_position_list(self, instattr): + def _get_position_list(self, positions): """ - Return the instance attributes, 'instattr', as a list indicating the - positions of the attributes in the final instance structure. + Return a list of attribute names for the given 'positions' mapping from + names to positions, indicating the positions of the attributes in the + final instance structure. """ - positions = instattr.items() - instarray = [None] * len(positions) + position_items = positions.items() + namearray = [None] * len(position_items) # Get the positions in ascending order of list size, with lists # of the same size ordered according to their smallest position # value. - positions.sort(self._cmp_positions) - #print self.name, positions + position_items.sort(self._cmp_positions) # Get the names in position order. held = [] - for name, pos in positions: + for name, pos in position_items: pos = list(pos) - if pos[0] != -1 and instarray[pos[0]] is None: - instarray[pos[0]] = name, Attr(pos[0], None, name) + if pos and namearray[pos[0]] is None: + namearray[pos[0]] = name else: - if pos[0] != -1 or len(pos) > 1: - self.instattr_relocated.add(name) + if pos: + self.relocated.add(name) held.append((name, pos)) - for i, attr in enumerate(instarray): + for i, attr in enumerate(namearray): if attr is None: name, pos = held.pop() - instarray[i] = name, Attr(i, None, name) + namearray[i] = name + + #print self.name, positions + #print "->", namearray + return namearray + + def _get_attributes(self, positions): - return instarray + """ + For the given 'positions' mapping from names to positions, return a + dictionary mapping names to Attr instances incorporating information + about their positions in the final instance structure. + """ + + d = {} + for i, name in enumerate(self._get_position_list(positions)): + d[name] = Attr(i, None, None, name) + return d def _cmp_positions(self, a, b): @@ -445,6 +513,8 @@ return -1 elif len(list_a) > len(list_b): return 1 + elif not list_a: + return 0 else: return cmp(min(list_a), min(list_b)) diff -r 3cc8cd3d0a14 -r e1b70bf99b49 tests/subclass.py --- a/tests/subclass.py Sat Feb 23 21:25:03 2008 +0100 +++ b/tests/subclass.py Sun Feb 24 01:27:16 2008 +0100 @@ -12,13 +12,16 @@ pass class C(A, B): + def a(self): + pass + + def b(self): + pass + def __init__(self, x, y): self.x = x self.y = y - def a(self): - pass - class D: def __init__(self, y): self.y = y