comparison source/javascript-objects.rst @ 94:454054afcc0a europython-2011

$ cp -piv ../python-classes-tutorial/source/javascript-objects.rst source/ # and some other changes
author Jonathan Fine <jfine@pytex.org>
date Sun, 19 Jun 2011 20:24:03 +0100
parents
children 4e6cfa066d1a
comparison
equal deleted inserted replaced
93:5abfbcfc01f6 94:454054afcc0a
1 JavaScript objects
2 ==================
3
4 Like Python classes
5 -------------------
6
7 In JavaScript all objects are part of an inheritance tree. The
8 **create** function adds a node to the inheritance tree.
9
10 .. code-block:: javascript
11
12 // A JavaScript object.
13 js> root = {}
14
15 // Prototype inheritance.
16 js> create = function (obj) {
17 var f = function () {return this;};
18 f.prototype = obj;
19 return new f;
20 }
21
22 js> a = create(root)
23 js> b = create(a)
24
25 js> a.name = 5
26 js> a.name
27 5
28 js> b.name
29 5
30
31 In Python classes inherit in the same way.
32
33 .. doctest::
34
35 >>> root = type # Most classes are instance of type.
36 >>> class a(root): pass
37 >>> class b(a): pass # Class inheritance.
38
39 >>> a.name = 5 # Just like JavaScript.
40 >>> a.name
41 5
42 >>> b.name
43 5
44
45 class explanation
46 ^^^^^^^^^^^^^^^^^
47
48 In Python we can subclass anything whose type is **type** (or a
49 subclass of type). A subclass (and its instances) inherits properties
50 from the super-class.
51
52 >>> type(root) == type(a) == type(b) == type
53 True
54
55
56
57 Custom item methods
58 -------------------
59
60 In JavaScript attribute and item access are the same.
61
62 .. code-block:: javascript
63
64 js> a = create(root)
65
66 js> a.name = 5
67 js> a['name']
68 5
69
70 js> a['key'] = 6
71 js> a.key
72 6
73
74 js> a[1] = 6
75 js> a['1']
76 6
77
78 In Python we can defined our own item methods. (The programmer owns
79 the dot.)
80
81 .. doctest::
82
83 >>> class A(object):
84 ...
85 ... def __getitem__(self, key):
86 ... return getattr(self, str(key))
87 ... def __setitem__(self, key, value):
88 ... return setattr(self, str(key), value)
89
90 >>> a = A()
91 >>> a.name = 5
92
93 >>> a['name']
94 5
95
96 >>> a['key'] = 6
97 >>> a.key
98 6
99
100 >>> a[1] = 6
101 >>> a['1']
102 6
103
104 Because **type(a)** is **A**, which has the special item methods, we
105 get the special item behaviour.
106
107 .. doctest::
108
109 >>> type(a) is A
110 True
111
112
113 On metaclass
114 ------------
115
116 Using previous definition, we cannot subclass **a** to create **b**.
117
118 .. doctest::
119
120 >>> class b(a): pass
121 Traceback (most recent call last):
122 class b(a): pass
123 TypeError: Error when calling the metaclass bases
124 object.__new__() takes no parameters
125
126 This is because **a** is not a type. The solution involves Python
127 metaclasses (an advanced topic).
128
129 .. doctest::
130
131 >>> isinstance(a, type)
132 False
133
134
135 metaclass construction
136 ^^^^^^^^^^^^^^^^^^^^^^
137 We will subclass type, not object, and add to it the special item
138 methods.
139
140
141 .. doctest::
142
143 >>> class ObjectType(type):
144 ...
145 ... def __getitem__(self, key):
146 ... return getattr(self, str(key))
147 ...
148 ... def __setitem__(self, key, value):
149 ... return setattr(self, str(key), value)
150
151 Here is a fancy way of calling **ObjectType**.
152
153 .. doctest::
154
155 >>> class root(object):
156 ... __metaclass__ = ObjectType
157
158
159 Here is a more direct (and equivalent) construction (create an
160 instance of **ObjectType**, whose instances are objects).
161
162 .. doctest::
163
164 >>> root = ObjectType('root', (object,), {})
165 >>> isinstance(root(), object)
166 True
167
168 metaclass demonstration
169 ^^^^^^^^^^^^^^^^^^^^^^^
170 .. doctest::
171
172 >>> class a(root): pass
173 >>> class b(a): pass
174
175 >>> a.name = 5
176 >>> a.name
177 5
178 >>> b.name
179 5
180 >>> a['name']
181 5
182 >>> b['name']
183 5
184
185 >>> a[1] = 6
186 >>> a['1']
187 6
188
189
190 metaclass explanation
191 ^^^^^^^^^^^^^^^^^^^^^
192
193 Because **type(root)** is a subclass of type we can subclass root.
194
195 .. doctest::
196
197 >>> issubclass(type(root), type)
198 True
199
200 Because the **type(root)** is **ObjectType**, which has special item
201 methods, we get the special item behaviour.
202
203 .. doctest::
204
205 >>> type(root) == type(a) == type(b) == ObjectType
206 True
207
208
209 Never instantiated
210 ------------------
211
212 We can't call JavaScript objects (unless they are a function). But
213 **create** creates ordinary JavaScript objects.
214
215 .. code-block:: javascript
216
217 js> a = create(root)
218 js> a(1, 2, 3)
219 TypeError: a is not a function
220
221
222 We will monkey-patch the previous Python class, to provide custom
223 behaviour when called.
224
225 .. doctest::
226
227 >>> def raise_not_a_function(obj, *argv, **kwargs):
228 ... raise TypeError, obj.__name__ + ' is not a function'
229
230 >>> ObjectType.__call__ = raise_not_a_function
231
232 >>> a(1, 2, 3)
233 Traceback (most recent call last):
234 a(1, 2, 3)
235 TypeError: a is not a function
236
237
238 Conclusion
239 ----------
240
241 JavaScript objects are like Python classes (because they inherit like
242 Python classes).
243
244 For JavaScript attribute and item access are the same. This is
245 achieved in Python by providing custom item methods.
246
247 In Python the custom item methods must be placed on the type of the
248 object (or a superclass of its type).
249
250 Ordinary JavaScript objects are not functions and cannot be called. A
251 Python class can be called (to create an instance of the object). But
252 we can override this behaviour by supplying a custom method for call.
253
254 To summarize:
255 ..
256
257 **JavaScript objects are like Python classes with custom item
258 methods (on the metaclass) that are never instantiated.**
259
260 It's worth saying again:
261
262 ..
263
264 **JavaScript objects are like Python classes with custom item
265 methods (on the metaclass) that are never instantiated.**
266
267
268