|
| 1 | +''' |
| 2 | +Circuitous, LLC - |
| 3 | +An Advanced Circle Analytics Company |
| 4 | +Summary: Toolset for New-Style Classes |
| 5 | +1. Inherit from object(). |
| 6 | +2. Instance variables for information unique to an instance. |
| 7 | +3. Class variables for data shared among all instances. |
| 8 | +4. Regular methods need "self" to operate on instance data. |
| 9 | +5. Class methods implement alternative constructors. They need "cls" |
| 10 | + so they can create subclass instances as well. |
| 11 | +6. Static methods attach functions to class. They don't need |
| 12 | + either "self" or "cls". |
| 13 | + Static methods improve discoverability and require context to be specified. |
| 14 | +7. A property() lets getter and setter methods be invoked automatically by |
| 15 | + attribute access. This allows Python classes to freely expose their |
| 16 | + instance variables. |
| 17 | +8. The "__slots__" variable implements the Flyweight Design Pattern by |
| 18 | + suppressing instance dictionaries |
| 19 | +9. The __method() is a class local reference, making sure the method refers |
| 20 | + to this class's method, not its childrens'. |
| 21 | +10. Thread local calls use the double underscore. Gives subclasses the |
| 22 | + freedom to override methods without breaking other methods. |
| 23 | +''' |
| 24 | + |
| 25 | +import math |
| 26 | + |
| 27 | +class Circle(object): # new-style class |
| 28 | + 'An advanced circle analytics toolkit' |
| 29 | + |
| 30 | + # flyweight design pattern suppresses the instance dictionary |
| 31 | + __slots__ = ['diameter'] |
| 32 | + version = '0.7' # class variable |
| 33 | + |
| 34 | + def __init__(self, radius): |
| 35 | + # init is not a constructor. It's job is to initialize the instance variables |
| 36 | + self.radius = radius # instance variables |
| 37 | + |
| 38 | + @property # convert dotted access to method calls |
| 39 | + def radius(self): |
| 40 | + return self.diameter / 2.0 |
| 41 | + |
| 42 | + @radius.setter |
| 43 | + def radius(self, radius): |
| 44 | + 'Radius of a circle' |
| 45 | + self.diameter = radius * 2.0 |
| 46 | + |
| 47 | + def area(self): # regular methods have 'self' as first argument |
| 48 | + # Given that the perimeter method is modified and used in a subclass, we use the double under to create a class local reference to ensure modified perimeter from subclass is not used |
| 49 | + p = self.__perimeter() |
| 50 | + r = p / math.pi / 2.0 |
| 51 | + return math.pi * r** 2.0 |
| 52 | + |
| 53 | + def perimeter(self): |
| 54 | + return 2.0 * math.pi * self.radius |
| 55 | + |
| 56 | + @classmethod # Alternative constructor |
| 57 | + def from_bbd(cls, bbd): # use `cls` petermeter to support subclassing |
| 58 | + 'Construct a circle from a bounding box diagonal' |
| 59 | + radius = bbd / 2.0 / math.sqrt(2.0) |
| 60 | + return cls(radius) # return `cls(radius)` instead of `Circle(radius)` so subclasses are not suppressed |
| 61 | + |
| 62 | + @staticmethod # Attach functions to classes - works without instantiating the class |
| 63 | + def angle_to_grade(angle): |
| 64 | + # A standard method would require creating a new instance just to call a function which needs no 'self' variables or any information about the instance of a `Circle` |
| 65 | + # Adding this function inside the Circle class improves discoverability and ensures the function is used in the appropriate context |
| 66 | + 'Convert angle in degree to a percentage grade' |
| 67 | + return math.tan(math.radians(angle)) * 100.0 # variables used here do not require information about the instance |
| 68 | + |
| 69 | + __perimeter = perimeter # Class local reference |
| 70 | + |
| 71 | + |
| 72 | +class Tire(Circle): |
| 73 | + 'Tires are circles with an odometer corrected perimeter' |
| 74 | + |
| 75 | + def perimeter(self): |
| 76 | + 'Circumference corrected for the rubber' |
| 77 | + return Circle.perimeter(self) * 1.25 |
| 78 | + |
| 79 | +if __name__ == "__main__": |
| 80 | + std = Circle(2) |
| 81 | + print('Radius of the Circle:', std.radius) |
| 82 | + print('Area of the Circle:', std.area()) |
| 83 | + print('Perimeter of the Circle:', std.perimeter()) |
| 84 | + |
| 85 | + print() |
| 86 | + new = Circle.from_bbd(25) |
| 87 | + print('Radius of the Circle:', new.radius) |
| 88 | + print('Area of the Circle:', new.area()) |
| 89 | + print('Perimeter of the Circle:', new.perimeter()) |
| 90 | + |
| 91 | + print() |
| 92 | + t = Tire.from_bbd(25) |
| 93 | + print('Radius of the Circle:', t.radius) |
| 94 | + print('Area of the Circle:', t.area()) |
| 95 | + print('Perimeter of the Circle:', t.perimeter()) # This does not work if `classmethod` returns the class instance rather than `cls` |
| 96 | + |
| 97 | + print() |
| 98 | + t = Tire(15) |
| 99 | + print('Radius of the Circle:', t.radius) |
| 100 | + print('Area of the Circle:', t.area()) |
| 101 | + print('Perimeter of the Circle:', t.perimeter()) |
| 102 | + |
| 103 | + print() |
| 104 | + grade = Circle.angle_to_grade(30) |
| 105 | + print('Angle:', 30) |
| 106 | + print('Grade:', grade) |
| 107 | + print('Type:', type(grade)) |
0 commit comments