-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMetaclasses.py
More file actions
224 lines (160 loc) · 8.16 KB
/
Metaclasses.py
File metadata and controls
224 lines (160 loc) · 8.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
########## METACLASSES ##########
# a metaclass:
# - is a class whose instances are classes
# - allows for the customization of class instantiation
# - redirect class instantiations to dedicated logic, contained in metaclasses
# - is applied when class definitions are read to create classes, well before classes are instantiated
# use cases:
# - logging
# - registering classes at creation time
# - interface checking
# - automatically adding new methods
# - automatically adding new variables
### Class reminder
## important attributes
# .__name__ ==> inherent for classes, contains the name of the class
# .__class__ ==> inherent for both classes and instances, contains information about the class to which a class instance belongs
# .__bases__ ==> inherent for classes, tuple containing information about the base classes of a class
# .__dict__ ==> inherent for both classes and instances, contains a dictionary of the object's attributes
class Dog:
pass
dog = Dog()
print('"dog" is an object of class named:', Dog.__name__)
# "dog" is an object of class named: Dog
print('class "Dog" is an instance of:', Dog.__class__)
# class "Dog" is an instance of: <class 'type'>
print('instance "dog" is an instance of:', dog.__class__)
# instance "dog" is an instance of: <class '__main__.Dog'>
print('class "Dog" is ', Dog.__bases__)
# class "Dog" is (<class 'object'>,)
print('class "Dog" attributes:', Dog.__dict__)
# class "Dog" attributes: {'__module__': '__main__', '__firstlineno__': 1,
# '__static_attributes__': (), '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}
print('object "dog" attributes:', dog.__dict__)
# object "dog" attributes: {}
## .__class__ == type()
# The same information stored in __class__could be retrieved by calling a type() function with one argument:
for element in (1, 'a', True):
print(element, 'is', element.__class__, type(element))
# 1 is <class 'int'> <class 'int'>
# a is <class 'str'> <class 'str'>
# True is <class 'bool'> <class 'bool'>
### Creating Class with type(,,)
Dog = type('Dog', (), {})
print('The class name is:', Dog.__name__)
# The class name is: Dog
print('The class is an instance of:', Dog.__class__)
# The class is an instance of: <class 'type'>
print('The class is based on:', Dog.__bases__)
# The class is based on: (<class 'object'>,)
print('The class attributes are:', Dog.__dict__)
# The class attributes are: {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>, '__doc__': None}
### Metaclasses
# Anything in Python is a n "object" including classes
# Metaclasses are classes of classe: they define how to create and structurate classes
# The default metaclass is "type" as seen above
def bark(self): # => a function \
print('Woof, woof') # \____ bark() and "Animal" class will be parents of new class
# /
class Animal: # => a class /
def feed(self):
print('It is feeding time!')
Dog = type('Dog', (Animal, ), {'age':0, 'bark':bark}) # instance creation, indeed a class
# class constructor ==> type(class name, parents tuple, attributes dictionary)
# 'Dog' (Animal, ) {'age':0, 'bark':bark}
# type() is the MetaClass of Dog
print('The class name is:', Dog.__name__)
# Dog
print('The class is an instance of:', Dog.__class__)
# <class 'type'>
print('The class is based on:', Dog.__bases__)
# (<class '__main__.Animal'>,)
print('The class attributes are:', Dog.__dict__)
# # The class attributes are: {'age': 0, 'bark': <function bark at 0x000001F026EB1440>,
# '__module__': '__main__', '__doc__': None}
doggy = Dog()
doggy.feed()
# It is feeding time!
doggy.bark()
# Woof, woof
## This is the equivalent of this below:
class Dog(Animal):
age = 0
def bark(self):
print('Woof, woof')
# This show that:
# Classes are objects, created by MetaClasses, by default type
# We can create Classes dynamiquely
# type has 2 roles :
# - builtin function to know an object type : type(obj)
# - Class constructor : type(name, parents, attributes)
### Create your own MetaClass
class My_Meta(type): # we create a Class inheriting from type, so My_Meta is aMetaClass as well
def __new__(mcs, name, bases, dictionary): # mcs = My_Meta, name = "will be" My_Object, bases = () as not any inheritance here, dictionary ) dict of Class attributes
obj = super().__new__(mcs, name, bases, dictionary) # is the same than type(name, bases, dictionary)
obj.custom_attribute = 'Added by My_Meta' # we add a static attribute
return obj # we return the so created Class
class My_Object(metaclass=My_Meta): # will use MetaClass My_MEta to build instance, the My_Object Class
pass # empty Class but will be populated by My_Meta MetaClass
print(My_Object.__dict__)
# {'__module__': '__main__', '__firstlineno__': 7, '__static_attributes__': (),
# '__dict__': <attribute '__dict__' of 'My_Object' objects>,
# '__weakref__': <attribute '__weakref__' of 'My_Object' objects>, '__doc__': None,
# 'custom_attribute': 'Added by My_Meta'} ==> # 'custom_attribute' is here !
print(My_Object.custom_attribute)
# 'Added by My_Meta'
obj = My_Object()
print(obj.custom_attribute)
# 'Added by My_Meta'
## __new__
class Example:
def __init__(self, value):
print("3. __init__ invoked - OBJECT INITIALISATION")
self.value = value
print(f"4. Objet initialised with value = {self.value}")
def __new__(cls, *args, **kwargs): # cls = Example, *args/**kwargs = any arguments passed
print("1. __new__ invoked - OBJECT CREATION")
instance = super().__new__(cls) # must return an new instance
print(f"2. Object created: {instance}")
return instance # if returns None, __init__ won't be executed
obj = Example("Hello")
# 1. __new__ invoked - OBJECT CREATION
# 2. Object created: <__main__.Example object at 0x...>
# 3. __init__ invoked - OBJECT INITIALISATION
# 4. Objet initialised with value = Hello
## build a metaclass responsible for completing classes with a method (if missing) to ensure that all your classes are equipped with a method
def greetings(self): # basic function
print('Just a greeting function, but it could be something more serious like a check sum') # prints the Output
class My_Meta(type): # Creates a MetaClass
def __new__(mcs, name, bases, dictionary):
if 'greetings' not in dictionary: # checks if "greetings" attribute (function) is NOT in the Object's (a Class here) dictionary
dictionary['greetings'] = greetings # if so, we add an item => {"greetins";greetings} , key,value, value being the function greetings()
obj = super().__new__(mcs, name, bases, dictionary)
return obj
class My_Class1(metaclass=My_Meta):
pass
class My_Class2(metaclass=My_Meta):
def greetings(self):
print('We are ready to greet you!')
myobj1 = My_Class1()
print (My_Class1.__dict__)
# {'__module__': '__main__', '__firstlineno__': 11, '__static_attributes__': (),
# 'greetings': <function greetings at 0x000001BCBACC1440>, ==> 'greetings' has been added ! AFTER __static_attributes__
# '__dict__': <attribute '__dict__' of 'My_Class1' objects>,
# '__weakref__': <attribute '__weakref__' of 'My_Class1' objects>, '__doc__': None}
print (myobj1.__dict__)
# {}
myobj1.greetings()
# Just a greeting function, but it could be something more serious like a check sum
myobj2 = My_Class2()
print (My_Class2.__dict__)
# {'__module__': '__main__', '__firstlineno__': 14,
# 'greetings': <function My_Class2.greetings at 0x00000188C3190C20>, ==> already present, BEFORE __static_attributes__
# '__static_attributes__': (), '__dict__': <attribute '__dict__' of 'My_Class2' objects>,
# '__weakref__': <attribute '__weakref__' of 'My_Class2' objects>, '__doc__': None}
print (myobj2.__dict__)
# {}
myobj2.greetings()
# We are ready to greet you!