the Descriptor pattern (new wrinkle in 3.6+)
What I like about staying the course on the 3.x track (versus adhering to the 2.7 terminus of the 2.x line), is each new version is an opportunity to catch up on features that have joined over several versions. My capacity to catch up inevitably lags the actual path of Python's evolution, not a tragedy, an opportunity. For example, I've recently pushed a lot more into Descriptor territory and in my last class for adults, spent time with a Python source code implementation of the property callable. As a built-in, it's written in C, however what it does may be modeled in pure Python. You may be familiar with this passage. Here's a Jupyter Notebook: https://github.com/4dsolutions/Python5/blob/master/Descriptors%20and%20Prope... The Property class is what's critical. I then use it to decorate code in other modules, proving that the pure Python class and the builtin do the same work. I've shared my "Prop Circle" before (since adding circumference, the logical next step): """ Created on Thu Oct 20 15:43:14 2016 Modified Wed Dec 7, 2016 @author: Kirby Urner Related reading: https://mail.python.org/pipermail/edu-sig/2016-October/011548.html """ from model_property import Property as property import math class Circle: """setting either the radius or area attribute sets the other as a dependent value. Initialized with radius only, unit circle by default. """ def __init__(self, radius = 1): self.radius = radius @property def area(self): return self._area @property def radius(self): return self._radius @property def circumference(self): return self._circum @circumference.setter def circumference(self, value): self.radius = value / (2 * math.pi) @area.setter def area(self, value): self._area = value self._radius = math.sqrt(self._area / math.pi) self._circum = 2 * math.pi * self._radius @radius.setter def radius(self, value): self._radius = value self._area = math.pi * (self._radius ** 2) self._circum = 2 * math.pi * self._radius def __repr__(self): return "Circle(radius = {})".format(self.radius) # Circle may be imported from prop_circle without all this noise. if __name__ == "__main__": the_circle = Circle(5) print("the_circle:", the_circle) print("Area: ", the_circle.area) the_circle.area = 50 print("Radius when Area=50:", the_circle.radius) I'm also experimenting with new namepaces for terminology, though in general I'm quite happy with the Python lingo. A Descriptor is this miniature unit of bookkeeping, wherein reading and writing operations, also being named at birth, register within the guts of the thing, triggering __set__ and __get__ (and now __set_name__). What if I use the word "Clerk" for "Descriptor" in some passages, and picture something Monty Pythonesque from a Charles Dickens like milieu. For a specific Lesson Plan, such imagery might reinforce comprehension of design pattern possibilities. A class (type) may wish to contract out come of its attributes to this professional bookkeeping class, called a Clerk, actually a Descriptor, that has reprogrammable behavior around setting and getting a value. Introduce these to your own classes as class-level inclusions if you wish. Compose, don't subclass. Don't worry though: when these clerk-monitored attributes are actually used by your instances, that instance will be a known object, passed in to the Clerk, and usable as part of a key to keep values specific to where they came from right down to the instance level. IF that's your goal. Blog post that's more casual, with some screen shots showing source code. http://controlroom.blogspot.com/2016/12/back-on-that-python-train.html Kirby
participants (1)
-
kirby urner