\[ [Index](index.md) | [Exercise 7.3](ex7_3.md) | [Exercise 7.5](ex7_5.md) \] # Exercise 7.4 *Objectives:* - Learn about the low-level steps involved in creating a class *Files Modified:* `validate.py`, `structure.py` In this exercise, we look at the mechanics of how classes are actually created. ## (a) Class creation Recall, from earlier exercises, we defined a simple class `Stock` that looked like this: ```python class Stock: def __init__(self,name,shares,price): self.name = name self.shares = shares self.price = price def cost(self): return self.shares*self.price def sell(self,nshares): self.shares -= nshares ``` What we're going to do here is create the class manually. Start out by just defining the methods as normal Python functions. ```python >>> def __init__(self,name,shares,price): self.name = name self.shares = shares self.price = price >>> def cost(self): return self.shares*self.price >>> def sell(self,nshares): self.shares -= nshares >>> ``` Next, make a methods dictionary: ```python >>> methods = { '__init__' : __init__, 'cost' : cost, 'sell' : sell } >>> ``` Finally, create the `Stock` class object: ```python >>> Stock = type('Stock',(object,),methods) >>> s = Stock('GOOG',100,490.10) >>> s.name 'GOOG' >>> s.cost() 49010.0 >>> s.sell(25) >>> s.shares 75 >>> ``` Congratulations, you just created a class. A class is really nothing more than a name, a tuple of base classes, and a dictionary holding all of the class contents. `type()` is a constructor that creates a class for you if you supply these three parts. ## (b) Typed structures In the `structure.py` file, define the following function: ```python # structure.py ... def typed_structure(clsname, **validators): cls = type(clsname, (Structure,), validators) return cls ``` This function is somewhat similar to the `namedtuple()` function in that it creates a class. Try it out: ```python >>> from validate import String, PositiveInteger, PositiveFloat >>> from structure import typed_structure >>> Stock = typed_structure('Stock', name=String(), shares=PositiveInteger(), price=PositiveFloat()) >>> s = Stock('GOOG', 100, 490.1) >>> s.name 'GOOG' >>> s Stock('GOOG', 100, 490.1) >>> ``` You might find the seams of your head starting to pull apart about now. ## (c) Making a lot of classes There are other situations where direct usage of the `type()` constructor might be advantageous. Consider this bit of code: ```python # validate.py ... class Typed(Validator): expected_type = object @classmethod def check(cls, value): if not isinstance(value, cls.expected_type): raise TypeError(f'expected {cls.expected_type}') super().check(value) class Integer(Typed): expected_type = int class Float(Typed): expected_type = float class String(Typed): expected_type = str ... ``` Wow is the last part of that annoying and repetitive. Change it to use a table of desired type classes like this: ```python # validate.py ... _typed_classes = [ ('Integer', int), ('Float', float), ('String', str) ] globals().update((name, type(name, (Typed,), {'expected_type':ty})) for name, ty in _typed_classes) ``` Now, if you want to have more type classes, you just add them to the table: ```python _typed_classes = [ ('Integer', int), ('Float', float), ('Complex', complex), ('Decimal', decimal.Decimal), ('List', list), ('Bool', bool), ('String', str) ] ``` Admit it, that's kind of cool and saves a lot of typing (at the keyboard). \[ [Solution](soln7_4.md) | [Index](index.md) | [Exercise 7.3](ex7_3.md) | [Exercise 7.5](ex7_5.md) \] ---- `>>>` Advanced Python Mastery `...` A course by [dabeaz](https://www.dabeaz.com) `...` Copyright 2007-2023 ![](https://i.creativecommons.org/l/by-sa/4.0/88x31.png). This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)