Initial commit
This commit is contained in:
81
Exercises/ex7_5.md
Normal file
81
Exercises/ex7_5.md
Normal file
@@ -0,0 +1,81 @@
|
||||
\[ [Index](index.md) | [Exercise 7.4](ex7_4.md) | [Exercise 7.6](ex7_6.md) \]
|
||||
|
||||
# Exercise 7.5
|
||||
|
||||
*Objectives:*
|
||||
|
||||
- Create your first metaclass
|
||||
|
||||
*Files Created:* `mymeta.py`
|
||||
|
||||
## (a) Create your first metaclass
|
||||
|
||||
Create a file called `mymeta.py`
|
||||
and put the following code in it (from the slides):
|
||||
|
||||
```python
|
||||
# mymeta.py
|
||||
|
||||
class mytype(type):
|
||||
@staticmethod
|
||||
def __new__(meta, name, bases, __dict__):
|
||||
print("Creating class :", name)
|
||||
print("Base classes :", bases)
|
||||
print("Attributes :", list(__dict__))
|
||||
return super().__new__(meta, name, bases, __dict__)
|
||||
|
||||
class myobject(metaclass=mytype):
|
||||
pass
|
||||
```
|
||||
|
||||
Once you've done this, define a class that inherits from
|
||||
`myobject` instead of object. For example:
|
||||
|
||||
```python
|
||||
class Stock(myobject):
|
||||
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
|
||||
```
|
||||
|
||||
Try running your code and creating instances of `Stock`. See
|
||||
what happens. You should see the print statements from your
|
||||
`mytype` running once when the `Stock` class is
|
||||
defined.
|
||||
|
||||
What happens if you inherit from `Stock`?
|
||||
|
||||
```python
|
||||
class MyStock(Stock):
|
||||
pass
|
||||
```
|
||||
|
||||
You should still see your metaclass at work. Metaclasses are "sticky" in that they
|
||||
get applied across an entire inheritance hierarchy.
|
||||
|
||||
**Discussion**
|
||||
|
||||
Why would you want to do something like this?
|
||||
The main power of a metaclass is that it gives a programmer the ability
|
||||
to capture details about classes just prior to their creation. For
|
||||
example, in the `__new__()` method, you are given all of the
|
||||
basic details including the name of the class, base classes, and
|
||||
methods data. If you inspect this data, you can perform various
|
||||
types of diagnostic checks. If you're more daring, you can modify the
|
||||
data and change what gets placed in the class definition when it is
|
||||
created. Needless to say, there are many opportunities for horrible
|
||||
diabolical evil.
|
||||
|
||||
\[ [Solution](soln7_5.md) | [Index](index.md) | [Exercise 7.4](ex7_4.md) | [Exercise 7.6](ex7_6.md) \]
|
||||
|
||||
----
|
||||
`>>>` Advanced Python Mastery
|
||||
`...` A course by [dabeaz](https://www.dabeaz.com)
|
||||
`...` Copyright 2007-2023
|
||||
|
||||
. This work is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)
|
||||
Reference in New Issue
Block a user