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 &quot;ref&quot;.
  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 &quot;ref&quot;.
  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 &lt;private_attributes&gt;`</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 &quot;ref&quot;.
  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">&lt;=</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 &quot;ref&quot;.
 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(&quot;bla&quot;)
 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(&quot;foo&quot;)
 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 &lt;type 'sage.rings.integer.Integer'&gt;
 181 sage: e.__dict__
 182 &lt;dictproxy object at 0x...&gt;
 183 sage: e.__dict__.keys()
 184 ['__module__', '_reduction', '__doc__', '_sage_src_lines_']
 185 
 186 sage: id4 = SymmetricGroup(4).one()
 187 sage: type(id4)
 188 &lt;type 'sage.groups.perm_gps.permgroup_element.PermutationGroupElement'&gt;
 189 sage: id4.__dict__
 190 &lt;dictproxy object at 0x...&gt;
 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 &gt; 0
 250            self._size = float(size)
 251            self._content = float(0.0)
 252        def __repr__(self):
 253            if self._content == 0.0:
 254                return &quot;An empty glass of size %s&quot;%(self._size)
 255            else:
 256                return &quot;A glass of size %s cl containing %s cl of water&quot;%(
 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 &quot;clean&quot; if self.is_clean() else &quot;dirty&quot;
 345        def __repr__(self):
 346            return &quot;An unspecified %s dish&quot;%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 &quot;A %s spoon&quot;%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 &quot;Building a spoon&quot;
 471 ...           super(Spoon, self).__init__()
 472 ...       def __repr__(self):
 473 ...           return &quot;A %s spoon&quot;%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 &gt; 0
 516            self._size = float(size)
 517            self.wash()
 518        def __repr__(self):
 519            if self._content == 0.0:
 520                return &quot;An empty glass of size %s&quot;%(self._size)
 521            else:
 522                return &quot;A glass of size %s cl containing %s cl of %s&quot;%(
 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 = &quot;water&quot;):
 529            if not self.is_clean():
 530                raise ValueError, &quot;Don't want to fill a dirty glass&quot;
 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 &lt;= 0.0:
 540                raise ValueError, &quot;amount must be positive&quot;
 541            elif amount &gt; self._content:
 542                raise ValueError, &quot;not enough beverage in the glass&quot;
 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(&quot;beer&quot;)
 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(&quot;orange juice&quot;)
 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(&quot;orange juice&quot;)
 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.