-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathmod_04_new_and_metaclasses.py
199 lines (144 loc) · 5.9 KB
/
mod_04_new_and_metaclasses.py
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
#-*- coding: utf-8 -*-
###
# MODULE 04: creation and instantiation: __new__ and metaclasses
###
class VerboseCreator(object):
def __new__(cls, *args, **kwargs):
print "__new__", cls, args, kwargs
res = super(VerboseCreator, cls).__new__(cls, *args, **kwargs)
print "type:", type(res)
return res
def __init__(self, *args, **kwargs):
print "__init__", self, args, kwargs
super(VerboseCreator, self).__init__(*args, **kwargs)
verb_inst = VerboseCreator()
#===============================================================================
# - __new__ is a special static method called to create a new instance of the class
# - No need to be defined as static
# - Called when you 'call' the class
# - It takes the class as first argument followed by all object constructor arguments
# - It returns the new instance
# - Later __init__ will be called on that instance
# - When calling __new__ with super, the class must be provided as first parameter
#===============================================================================
class VerboseCreatorDict(VerboseCreator, dict):
pass
verb_dict_inst = VerboseCreatorDict({'a': 1, 'b': 2})
print verb_dict_inst
# A more real example
class MySingleton(object):
def __new__(cls, *args, **kwargs):
if not hasattr(cls, '_inst'):
cls._inst = super(MySingleton, cls).__new__(cls, *args, **kwargs)
return cls._inst
inst_1 = MySingleton()
inst_2 = MySingleton()
print id(inst_1), 'vs.', id(inst_2)
# Another example
class RoundedFloat(float):
def __new__(cls, *args, **kwargs):
if args:
args = [round(args[0], 2)]
return super(RoundedFloat, cls).__new__(cls, *args, **kwargs)
print RoundedFloat(7.12345)
#===============================================================================
# Common uses:
# - Singleton pattern, although the most recommended implementation is using a module
# - Factory pattern
# - When subclassing immutable types, to customize the instance creation
# - In custom metaclasses in order to customise class creation
#===============================================================================
# Metaclasses? What's that?
print type(verb_dict_inst)
print type(type(verb_dict_inst))
print type(VerboseCreatorDict)
#===============================================================================
# - The metaclass is the type of a class.
# - By default new-style classes are constructed using type().
# - A class definition is read into a separate namespace and the value of class name
# is bound to the result of calling type(name, bases, dict)
#
# More info: http://docs.python.org/2/reference/datamodel.html#customizing-class-creation
#===============================================================================
# A simple example
class MyVerboseMetaclass(type):
def __new__(mcs, name, bases, attrs):
print "NEW CLASS:"
print " - metaclass:", mcs
print " - name:", name
print " - bases:", bases
print " - attrs:", attrs
new_class = super(MyVerboseMetaclass, mcs).__new__(mcs, name, bases, attrs)
return new_class
# Let's use this metaclass. Pay attention
class MyMetaclassedClass(object):
__metaclass__ = MyVerboseMetaclass
class_attrib = "class attrib value"
def method(self, *args, **kwargs):
return True
# Notice how the metaclass acts on import time, when creating the metaclassed class
print MyMetaclassedClass
print type(MyMetaclassedClass)
# Let's see a real example
# Let's implement a logging decorator
def _logging_decorator(func):
def logging_wrapper(*args, **kwargs):
res = func(*args, **kwargs)
if not res:
print "CALL FAILED:", args, kwargs,
return res
return logging_wrapper
# Let's define a metaclass using it
from inspect import isfunction
def _decorate_attrs(attrs):
decorated_attrs = {}
for name, func in attrs.items():
if isfunction(func):
func = _logging_decorator(func)
decorated_attrs[name] = func
return decorated_attrs
class LoggingMetaclass(type):
def __new__(mcs, name, bases, attrs):
# print "NEW", mcs, name, bases, attrs
new_class = super(LoggingMetaclass, mcs).__new__(mcs, name, bases, _decorate_attrs(attrs))
return new_class
# Let's use this metaclass
class TrueFalseClass(object):
__metaclass__ = LoggingMetaclass
class_attrib = "class attrib value"
def ret_false(self, *args, **kwargs):
return False
def ret_true(self, *args, **kwargs):
return True
tf_inst = TrueFalseClass()
tf_inst.ret_true()
tf_inst.ret_false()
tf_inst.ret_false(1, "xyz", arg1=7)
#===============================================================================
# Metaclasses usage:
# - Modifying the class dictionary prior to the class being created
# - Returning an instance of another class like a factory pattern
# - Really useful to modify classes (e.g. decorate classes methods) dynamically:
# - Logging
# - Timing or profiling
# - Caching
# - ...
#
# - It can be specified system-wide with a global variable '__metaclass__'
#===============================================================================
#===============================================================================
# __new__
# - Intercepts instances creation
# - Executed on instantiation time
# - Affects instances one by one
#
# __metaclass__
# - Intercepts class creation
# - Executed on import time
# - Affects all instances at once
#===============================================================================
#===============================================================================
# More info:
# - http://docs.python.org/2/reference/datamodel.html#customizing-class-creation
# - http://www.voidspace.org.uk/python/articles/metaclasses.shtml
#===============================================================================