Attachment 'tutorial-objects-and-classes.txt'
Download 1 Tutorial: Objects and Classes in Python and Sage
2
3 <span id="tutorial-objects-and-classes"></span>
4
5
6 </pre><p>This tutorial is an introduction to object oriented programming in Python and
7 Sage. It requires basic knowledges on imperative/procedural programming (the
8 most common programming style) that is conditional instructions, loops,
9 functions (see <a href="#id1"><span class="problematic" id="id2">:ref:`tutorial-programming-python`</span></a>), but now further knowledge
10 about objects and classes is assumed. It is designed as an alternating
11 sequence of formal introduction and exercises. <a href="#id3"><span class="problematic" id="id4">:ref:`solutions`</span></a> are given at
12 the end.</p>
13
14 <p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">tutorial-objects-and-classes.rst</tt>, line 9); <em><a href="#id2">backlink</a></em></p>
15 Unknown interpreted text role "ref".
16
17 <p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">tutorial-objects-and-classes.rst</tt>, line 9); <em><a href="#id4">backlink</a></em></p>
18 Unknown interpreted text role "ref".
19
20 <h1>Object oriented programming paradigm</h1>
21 <p>The object oriented programming paradigm relies on the two following
22 fundamental rules:</p>
23 <ol class="arabic simple">
24 <li>Any thing of the real (or mathematical) world which needs to be manipulated
25 by the computer is modeled by an <strong>object</strong>.</li>
26 <li>Each object is an <strong>instance</strong> of some <strong>class</strong>.</li>
27 </ol>
28 <p>At this point, those two rules are a little meaningless, so let's give some
29 more or less precise definition of the terms:</p>
30 <hr class="docutils">
31 <dl class="docutils">
32 <dt><strong>object</strong></dt>
33 <dd>a <strong>portion of memory</strong> which contains the information needed to model
34 the real world thing.</dd>
35 <dt><strong>class</strong></dt>
36 <dd>defines the <strong>data structure</strong> used to store the objects which are instance
37 of the class together with their <strong>behavior</strong>.</dd>
38 </dl>
39 <hr class="docutils">
40 <p>Let's start with some examples: We consider the vector space over $QQ$ whose
41 basis is indexed by permutations, and a particular element in it:</p>
42
43 {{{id=0|
44 F = CombinatorialFreeModule(QQ, Permutations())
45 el = 3*F([1,3,2])+ F([1,2,3])
46 el
47 ///
48 B[[1, 2, 3]] + 3*B[[1, 3, 2]]
49 }}}
50
51 <p>In python, everything is an object so there isn't any difference between types
52 and classes. On can get the class of the object <tt class="docutils literal"><span class="pre">el</span></tt> by:</p>
53 <!-- link -->
54
55 {{{id=1|
56 type(el)
57 ///
58 <class 'sage.combinat.free_module.CombinatorialFreeModule_with_category.element_class'>
59 }}}
60
61 <p>As such, this is not very informative. We'll go back to it later. The data
62 associated to objects are stored in so called <strong>attributes</strong>. They are
63 accessed through the syntax <tt class="docutils literal"><span class="pre">obj.attributes_name</span></tt>:</p>
64 <!-- link -->
65
66 {{{id=2|
67 el._monomial_coefficients
68 ///
69 {[1, 2, 3]: 1, [1, 3, 2]: 3}
70 }}}
71
72 <p>Modifying the attribute modifies the objects:</p>
73 <!-- link -->
74
75 {{{id=3|
76 el._monomial_coefficients[Permutation([3,2,1])] = 1/2
77 el
78 ///
79 B[[1, 2, 3]] + 3*B[[1, 3, 2]] + 1/2*B[[3, 2, 1]]
80 }}}
81
82 <p class="first admonition-title">Warning</p>
83 <p>as a user, you are <em>not</em> supposed to do that by yourself (see
84 note on <a href="#id5"><span class="problematic" id="id6">:ref:`private attributes <private_attributes>`</span></a> below).</p>
85
86 <p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">tutorial-objects-and-classes.rst</tt>, line 84); <em><a href="#id6">backlink</a></em></p>
87 Unknown interpreted text role "ref".
88
89 <p>As an element of a vector space <tt class="docutils literal"><span class="pre">el</span></tt> has a particular behavior:</p>
90 <!-- link -->
91
92 {{{id=4|
93 2*el
94 ///
95 2*B[[1, 2, 3]] + 6*B[[1, 3, 2]] + B[[3, 2, 1]]
96 }}}
97
98 {{{id=5|
99 el.support()
100 ///
101 [[1, 2, 3], [1, 3, 2], [3, 2, 1]]
102 }}}
103
104 {{{id=6|
105 el.coefficient([1, 2, 3])
106 ///
107 1
108 }}}
109
110 <p>The behavior is defined through <strong>methods</strong> (<tt class="docutils literal"><span class="pre">support,</span> <span class="pre">coefficient</span></tt>). Note
111 that this is true, even for equality, printing or mathematical operations. For
112 example the call <tt class="docutils literal"><span class="pre">a</span> <span class="pre">==</span> <span class="pre">b</span></tt> actually is translated to the method call
113 <tt class="docutils literal"><span class="pre">a.__eq__(b)</span></tt>. The names of those special methods which are usually called
114 through operators are fixed by the Python language and are of the form
115 <tt class="docutils literal"><span class="pre">__name__</span></tt>. Example include <tt class="docutils literal"><span class="pre">__eq__</span></tt>, <tt class="docutils literal"><span class="pre">__le__</span></tt> for operators <tt class="docutils literal"><span class="pre">==</span></tt> and
116 <tt class="docutils literal"><span class="pre"><=</span></tt>, <tt class="docutils literal"><span class="pre">__repr__</span></tt> (see <a href="#id7"><span class="problematic" id="id8">:ref:`sage_specifics`</span></a>) for printing, <tt class="docutils literal"><span class="pre">__add__</span></tt> and
117 <tt class="docutils literal"><span class="pre">__mult__</span></tt> for operators <tt class="docutils literal"><span class="pre">+</span></tt> and <tt class="docutils literal"><span class="pre">*</span></tt> (see
118 <a class="reference external" href="http://docs.python.org/library/">http://docs.python.org/library/</a>) for a complete list:</p>
119
120 <p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">tutorial-objects-and-classes.rst</tt>, line 100); <em><a href="#id8">backlink</a></em></p>
121 Unknown interpreted text role "ref".
122 <!-- link -->
123
124 {{{id=7|
125 el.__eq__(F([1,3,2]))
126 ///
127 False
128 }}}
129
130 {{{id=8|
131 el.__repr__()
132 ///
133 'B[[1, 2, 3]] + 3*B[[1, 3, 2]] + 1/2*B[[3, 2, 1]]'
134 }}}
135
136 {{{id=9|
137 el.__mul__(2)
138 ///
139 2*B[[1, 2, 3]] + 6*B[[1, 3, 2]] + B[[3, 2, 1]]
140 }}}
141
142 <p>Some particular actions allows to modify the data structure of <tt class="docutils literal"><span class="pre">el</span></tt>:</p>
143 <!-- link -->
144
145 {{{id=10|
146 el.rename("bla")
147 el
148 ///
149 bla
150 }}}
151
152 <p class="first admonition-title">Note</p>
153 <p>The class is stored in a particular attribute called <tt class="docutils literal"><span class="pre">__class__</span></tt> the
154 normal attribute are stored in a dictionary called <tt class="docutils literal"><span class="pre">__dict__</span></tt>:</p>
155 <!-- link -->
156
157 {{{id=11|
158 F = CombinatorialFreeModule(QQ, Permutations())
159 el = 3*F([1,3,2])+ F([1,2,3])
160 el.rename("foo")
161 el.__class__
162 ///
163 <class 'sage.combinat.free_module.CombinatorialFreeModule_with_category.element_class'>
164 }}}
165
166 {{{id=12|
167 el.__dict__
168 ///
169 {'_monomial_coefficients': {[1, 2, 3]: 1, [1, 3, 2]: 3}, '__custom_name': 'foo'}
170 }}}
171
172 <p>Lots of sage objects are not Python objects but compiled Cython
173 objects. Python sees them as builtin objects and you don't have access to
174 the data structure. Examples include integers and permutation group
175 elements:</p>
176 <!-- link -->
177 <pre class="last literal-block">
178 sage: e = Integer(9)
179 sage: type(e)
180 <type 'sage.rings.integer.Integer'>
181 sage: e.__dict__
182 <dictproxy object at 0x...>
183 sage: e.__dict__.keys()
184 ['__module__', '_reduction', '__doc__', '_sage_src_lines_']
185
186 sage: id4 = SymmetricGroup(4).one()
187 sage: type(id4)
188 <type 'sage.groups.perm_gps.permgroup_element.PermutationGroupElement'>
189 sage: id4.__dict__
190 <dictproxy object at 0x...>
191 </pre>
192
193
194 <p class="first admonition-title">Note</p>
195 <p>Each objects corresponds to a portion of memory called its <strong>identity</strong> in
196 python. You can get the identity using <tt class="docutils literal"><span class="pre">id</span></tt>:</p>
197 <!-- link -->
198
199 {{{id=13|
200 el = Integer(9)
201 id(el) # random
202 ///
203 139813642977744
204 }}}
205
206 {{{id=14|
207 el1 = el; id(el1) == id(el)
208 ///
209 True
210 }}}
211
212 {{{id=15|
213 el1 is el
214 ///
215 True
216 }}}
217
218 <p>This is different from mathematical identity:</p>
219 <!-- link -->
220 <pre class="last literal-block">
221 sage: el2 = Integer(9)
222 sage: el2 == el1
223 True
224 sage: el2 is el1
225 False
226 sage: id(el2) == id(el)
227 False
228 </pre>
229
230
231 <h2>Summary</h2>
232 <p>To define some object, you first have to write a <strong>class</strong>. The class will
233 defines the methods and the attributes of the object.</p>
234 <dl class="docutils">
235 <dt><strong>method</strong></dt>
236 <dd>particular kind of function associated with an object used to get
237 information about the object or to manipulate it.</dd>
238 <dt><strong>attribute</strong></dt>
239 <dd>variables where the info about the object are stored;</dd>
240 </dl>
241
242
243 <h2>An example: glass of beverage in a restaurant</h2>
244 <p>Let's write a small class about glasses in a restaurant:</p>
245
246 {{{id=16|
247 class Glass(object):
248 def __init__(self, size):
249 assert size > 0
250 self._size = float(size)
251 self._content = float(0.0)
252 def __repr__(self):
253 if self._content == 0.0:
254 return "An empty glass of size %s"%(self._size)
255 else:
256 return "A glass of size %s cl containing %s cl of water"%(
257 self._size, self._content)
258 def fill(self):
259 self._content = self._size
260 def empty(self):
261 self._content = float(0.0)
262 ///
263 }}}
264
265 <p>Let's create a small glass:</p>
266 <!-- link -->
267
268 {{{id=17|
269 myGlass = Glass(10); myGlass
270 ///
271 An empty glass of size 10.0
272 }}}
273
274 {{{id=18|
275 myGlass.fill(); myGlass
276 ///
277 A glass of size 10.0 cl containing 10.0 cl of water
278 }}}
279
280 {{{id=19|
281 myGlass.empty(); myGlass
282 ///
283 An empty glass of size 10.0
284 }}}
285
286 <p>Some comments:</p>
287 <ol class="arabic simple">
288 <li>The method <tt class="docutils literal"><span class="pre">__init__</span></tt> is used to initialize the object, it is used by the
289 so called <strong>constructor</strong> of the class that is executed when calling <tt class="docutils literal"><span class="pre">Glass(10)</span></tt>.</li>
290 <li>The method <tt class="docutils literal"><span class="pre">__repr__</span></tt> is supposed to return a string which is used to
291 print the object.</li>
292 </ol>
293
294 <p class="first admonition-title">Note</p>
295 <p><strong>Private Attributes</strong></p>
296 <ul class="last simple" id="private-attributes">
297 <li>most of the time, the user should not change directly the
298 attribute of an object. Those attributes are called <strong>private</strong>. Since
299 there is no mechanism to ensure privacy in python, the usage is to prefix
300 the name by an underscore.</li>
301 <li>as a consequence attribute access is only made through methods.</li>
302 <li>methods which are only for internal use are also prefixed with an
303 underscore.</li>
304 </ul>
305
306
307
308 <h2>Exercises</h2>
309 <ol class="arabic simple">
310 <li>add a method <tt class="docutils literal"><span class="pre">is_empty</span></tt> which returns true if a glass is empty.</li>
311 <li>define a method <tt class="docutils literal"><span class="pre">drink</span></tt> with a parameter <tt class="docutils literal"><span class="pre">amount</span></tt> which allows to
312 partially drink the water in the glass. Raise an error if one asks to
313 drink more water than there is in the glass or a negative amount of
314 water.</li>
315 <li>Allows the glass to be filled with wine, beer or other beverage. The method
316 <tt class="docutils literal"><span class="pre">fill</span></tt> should accept a parameter <tt class="docutils literal"><span class="pre">beverage</span></tt>. The beverage is stored in
317 an attribute <tt class="docutils literal"><span class="pre">_beverage</span></tt>. Update the method <tt class="docutils literal"><span class="pre">__repr__</span></tt> accordingly.</li>
318 <li>Add an attribute <tt class="docutils literal"><span class="pre">_clean</span></tt> and methods <tt class="docutils literal"><span class="pre">is_clean</span></tt> and <tt class="docutils literal"><span class="pre">wash</span></tt>. At the
319 creation a glass is clean, as soon as it's filled it becomes dirty, and must
320 be washed to become clean again.</li>
321 <li>Test everything.</li>
322 <li>Make sure that everything is tested.</li>
323 <li>Test everything again.</li>
324 </ol>
325
326
327
328 <h1>Inheritance</h1>
329 <p>The problem: object of <strong>different</strong> classes may share a <strong>common behavior</strong>.</p>
330 <p>For example, if one wants to deal now with different dishes (forks, spoons
331 ...) then there is common behavior (becoming dirty and being washed). So the
332 different classes associated to the different kinds of dishes should have the
333 same <tt class="docutils literal"><span class="pre">clean</span></tt>, <tt class="docutils literal"><span class="pre">is_clean</span></tt> and <tt class="docutils literal"><span class="pre">wash</span></tt> methods. But copying and pasting
334 code is bad and evil ! This is done by having a base class which factorizes
335 the common behavior:</p>
336
337 {{{id=20|
338 class AbstractDish(object):
339 def __init__(self):
340 self._clean = True
341 def is_clean(self):
342 return self._clean
343 def state(self):
344 return "clean" if self.is_clean() else "dirty"
345 def __repr__(self):
346 return "An unspecified %s dish"%self.state()
347 def _make_dirty(self):
348 self._clean = False
349 def wash(self):
350 self._clean = True
351 ///
352 }}}
353
354 <p>Now one can reuse this behavior within a class <tt class="docutils literal"><span class="pre">Spoon</span></tt>:</p>
355 <!-- link -->
356
357 {{{id=21|
358 class Spoon(AbstractDish):
359 def __repr__(self):
360 return "A %s spoon"%self.state()
361 def eat_with(self):
362 self._make_dirty()
363 ///
364 }}}
365
366 <p>Let's tests it:</p>
367 <!-- link -->
368
369 {{{id=22|
370 s = Spoon(); s
371 ///
372 A clean spoon
373 }}}
374
375 {{{id=23|
376 s.is_clean()
377 ///
378 True
379 }}}
380
381 {{{id=24|
382 s.eat_with(); s
383 ///
384 A dirty spoon
385 }}}
386
387 {{{id=25|
388 s.is_clean()
389 ///
390 False
391 }}}
392
393 {{{id=26|
394 s.wash(); s
395 ///
396 A clean spoon
397 }}}
398
399 <h2>Summary</h2>
400 <ol class="arabic">
401 <li><p class="first">Any class can reuse the behavior of another class. One says that the
402 subclass <strong>inherits</strong> from the superclass or that it <strong>derives</strong> from it.</p>
403 </li>
404 <li><p class="first">Any instance of the subclass is also an instance its superclass:</p>
405 <!-- link -->
406
407 {{{id=27|
408 type(s)
409 ///
410 <class '__main__.Spoon'>
411 }}}
412
413 {{{id=28|
414 isinstance(s, Spoon)
415 ///
416 True
417 }}}
418
419 {{{id=29|
420 isinstance(s, AbstractDish)
421 ///
422 True
423 }}}
424
425 </li>
426 <li><p class="first">If a subclass redefines a method, then it replaces the former one. One says
427 that the subclass <strong>overloads</strong> the method. One can nevertheless explicitly
428 call the hidden superclass method.</p>
429 <!-- link -->
430
431 {{{id=30|
432 s.__repr__()
433 ///
434 'A clean spoon'
435 }}}
436
437 {{{id=31|
438 Spoon.__repr__(s)
439 ///
440 'A clean spoon'
441 }}}
442
443 {{{id=32|
444 AbstractDish.__repr__(s)
445 ///
446 'An unspecified clean dish'
447 }}}
448
449 </li>
450 </ol>
451
452 <p class="first admonition-title">Note</p>
453 <p><strong>Advanced superclass method call</strong></p>
454 <p>Sometimes one wants to call an overloaded method without knowing in which
455 class it is defined. On use the <tt class="docutils literal"><span class="pre">super</span></tt> operator</p>
456 <!-- link -->
457
458 {{{id=33|
459 super(Spoon, s).__repr__()
460 ///
461 'An unspecified clean dish'
462 }}}
463
464 <p>A very common usage of this construct is to call the __init__ method of the
465 super classes:</p>
466 <!-- link -->
467 <pre class="last literal-block">
468 sage: class Spoon(AbstractDish):
469 ... def __init__(self):
470 ... print "Building a spoon"
471 ... super(Spoon, self).__init__()
472 ... def __repr__(self):
473 ... return "A %s spoon"%self.state()
474 ... def eat_with(self):
475 ... self.make_dirty()
476 sage: s = Spoon()
477 Building a spoon
478 sage: s
479 A clean spoon
480 </pre>
481
482
483
484 <h2>Exercises</h2>
485 <ol class="arabic simple">
486 <li>Modify the class <tt class="docutils literal"><span class="pre">Glasses</span></tt> so that it inherits from <tt class="docutils literal"><span class="pre">Dish</span></tt>.</li>
487 <li>Write a class <tt class="docutils literal"><span class="pre">Plate</span></tt> whose instance can contain any meals together with
488 a class <tt class="docutils literal"><span class="pre">Fork</span></tt>. Avoid at much as possible code duplication (hint:
489 you can write a factorized class <tt class="docutils literal"><span class="pre">ContainerDish</span></tt>).</li>
490 <li>Test everything.</li>
491 </ol>
492
493
494
495 <span id="sage-specifics"></span><h1>Sage specifics about classes</h1>
496 <p>Compared to Python, Sage has its particular way to handles objects:</p>
497 <ul class="simple">
498 <li>Any classes for mathematical objects in Sage should inherits from
499 <tt class="docutils literal"><span class="pre">SageObject</span></tt> rather than from <tt class="docutils literal"><span class="pre">object</span></tt>.</li>
500 <li>Printing should be done through <tt class="docutils literal"><span class="pre">_repr_</span></tt> instead of <tt class="docutils literal"><span class="pre">__repr__</span></tt> to allows
501 for renaming.</li>
502 <li>More generally, Sage specific special methods are usually named <tt class="docutils literal"><span class="pre">_meth_</span></tt>
503 rather than <tt class="docutils literal"><span class="pre">__meth__</span></tt>. For example, lots of classes implement <tt class="docutils literal"><span class="pre">_hash_</span></tt>
504 which is used and cached by <tt class="docutils literal"><span class="pre">__hash__</span></tt>.</li>
505 </ul>
506
507
508 <span id="solutions"></span><h1>Solutions to the exercises</h1>
509 <ol class="arabic">
510 <li><p class="first">Here is a solution to the first exercise:</p>
511
512 {{{id=34|
513 class Glass(object):
514 def __init__(self, size):
515 assert size > 0
516 self._size = float(size)
517 self.wash()
518 def __repr__(self):
519 if self._content == 0.0:
520 return "An empty glass of size %s"%(self._size)
521 else:
522 return "A glass of size %s cl containing %s cl of %s"%(
523 self._size, self._content, self._beverage)
524 def content(self):
525 return self._content
526 def beverage(self):
527 return self._beverage
528 def fill(self, beverage = "water"):
529 if not self.is_clean():
530 raise ValueError, "Don't want to fill a dirty glass"
531 self._clean = False
532 self._content = self._size
533 self._beverage = beverage
534 def empty(self):
535 self._content = float(0.0)
536 def is_empty(self):
537 return self._content == 0.0
538 def drink(self, amount):
539 if amount <= 0.0:
540 raise ValueError, "amount must be positive"
541 elif amount > self._content:
542 raise ValueError, "not enough beverage in the glass"
543 else:
544 self._content -= float(amount)
545 def is_clean(self):
546 return self._clean
547 def wash(self):
548 self._content = float(0.0)
549 self._beverage = None
550 self._clean = True
551 ///
552 }}}
553
554 </li>
555 <li><p class="first">Let's check that everything is working as expected:</p>
556 <!-- link -->
557
558 {{{id=35|
559 G = Glass(10.0)
560 G
561 ///
562 An empty glass of size 10.0
563 }}}
564
565 {{{id=36|
566 G.is_empty()
567 ///
568 True
569 }}}
570
571 {{{id=37|
572 G.drink(2)
573 ///
574 Traceback (most recent call last):
575 ValueError: not enough beverage in the glass
576 }}}
577
578 {{{id=38|
579 G.fill("beer")
580 G
581 ///
582 A glass of size 10.0 cl containing 10.0 cl of beer
583 }}}
584
585 {{{id=39|
586 G.is_empty()
587 ///
588 False
589 }}}
590
591 {{{id=40|
592 G.is_clean()
593 ///
594 False
595 }}}
596
597 {{{id=41|
598 G.drink(5.0)
599 G
600 ///
601 A glass of size 10.0 cl containing 5.0 cl of beer
602 }}}
603
604 {{{id=42|
605 G.is_empty()
606 ///
607 False
608 }}}
609
610 {{{id=43|
611 G.is_clean()
612 ///
613 False
614 }}}
615
616 {{{id=44|
617 G.drink(5)
618 G
619 ///
620 An empty glass of size 10.0
621 }}}
622
623 {{{id=45|
624 G.is_clean()
625 ///
626 False
627 }}}
628
629 {{{id=46|
630 G.fill("orange juice")
631 ///
632 Traceback (most recent call last):
633 ValueError: Don't want to fill a dirty glass
634 }}}
635
636 {{{id=47|
637 G.wash()
638 G
639 ///
640 An empty glass of size 10.0
641 }}}
642
643 {{{id=48|
644 G.fill("orange juice")
645 G
646 ///
647 A glass of size 10.0 cl containing 10.0 cl of orange juice
648 }}}
649
650 </li>
651 </ol>
652 <p>Here is the solution to the second exercice:</p>
653 <p>TODO !!!!</p>
654 <p>That all folks !</p>
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.