Lecture 20 Operators and Abstraction
Announcements for Today Two Weeks Out! Assignments • Prelim, Nov 21 st at 7:30 • A4 graded by Saturday § Will cover survey next week § Same rooms as last time § Day after A6 is due • A5 also graded by Saturday § Returned via Gradescope • Material up to Nov. 12 § Similar policies to A2 § Study guide this weekend • Need to be working on A6 § Recursion + Loops + Classes § Finish Image this weekend • Conflict with Prelim? § Finish Filter by next Thurs § Prelim 2 Conflict on CMS § Best way to study for exam § SDS students must submit! 11/7/19 Operators and Abstraction 2
Case Study: Fractions class Fraction(object): • Want to add a new type """Instance is a fraction n/d""" § Values are fractions: ½ , ¾ # INSTANCE ATTRIBUTES: § Operations are standard # _numerator: an int multiply, divide, etc. # _denominator: an int > 0 § Example : ½ * ¾ = ⅜ • Can do this with a class def __init__(self,n=0,d=1): § Values are fraction objects """Init: makes a Fraction""" § Operations are methods self._numerator = n self._denominator = d • Example : frac1.py 11/7/19 Operators and Abstraction 3
Case Study: Fractions class Fraction(object): • Want to add a new type """Instance is a fraction n/d""" § Values are fractions: ½ , ¾ # INSTANCE ATTRIBUTES: § Operations are standard Reminder : Hide # _numerator: an int multiply, divide, etc. attributes, use # _denominator: an int > 0 § Example : ½ * ¾ = ⅜ getters/setters • Can do this with a class def __init__(self,n=0,d=1): § Values are fraction objects """Init: makes a Fraction""" § Operations are methods self._numerator = n self._denominator = d • Example : frac1.py 11/7/19 Operators and Abstraction 4
Problem: Doing Math is Unwieldy What We Want What We Get 1 2 + 1 3 + 1 4 ∗ 5 >>> p = Fraction(1,2) >>> q = Fraction(1,3) 4 >>> r = Fraction(1,4) >>> s = Fraction(5,4) >>> (p.add(q.add(r))).mult(s) This is confusing! 11/7/19 Operators and Abstraction 5
Problem: Doing Math is Unwieldy What We Want What We Get 1 2 + 1 3 + 1 4 ∗ 5 >>> p = Fraction(1,2) >>> q = Fraction(1,3) 4 >>> r = Fraction(1,4) >>> s = Fraction(5,4) Why not use the >>> (p.add(q.add(r))).mult(s) standard Python math operations? This is confusing! 11/7/19 Operators and Abstraction 6
Special Methods in Python class Point3(object): • Have seen three so far """Instances are points in 3D space""" § __init__ for initializer … § __str__ for str() def __init__(self,x=0,y=0,z=0): § __repr__ for repr() """Initializer: makes new Point3""" • Start/end with 2 underscores … § This is standard in Python def __str__(self,q): § Used in all special methods """Returns: string with contents""” § Also for special attributes … • We can overload operators def __repr__(self,q): § Give new meaning to +, *, - """Returns: unambiguous string""” … 11/7/19 Operators and Abstraction 7
Operator Overloading • Many operators in Python a special symbols § + , - , / , * , ** for mathematics § == , != , < , > for comparisons • The meaning of these symbols depends on type § 1 + 2 vs 'Hello' + 'World' § 1 < 2 vs 'Hello' < 'World' • Our new type might want to use these symbols § We overload them to support our new type 11/7/19 Operators and Abstraction 8
Returning to Fractions What We Want Operator Overloading 1 2 + 1 3 + 1 4 ∗ 5 • Python has methods that correspond to built-in ops 4 § __ add__ corresponds to + § __mul__ corresponds to * § __eq__ corresponds to == Why not use the § Not implemented by default standard Python • To overload operators you math operations? implement these methods 11/7/19 Operators and Abstraction 9
Operator Overloading: Multiplication class Fraction(object): >>> p = Fraction(1,2) """Instance is a fraction n/d""" >>> q = Fraction(3,4) # _numerator: an int >>> r = p*q # _denominator: an int > 0 def __mul__(self,q): Python """Returns: Product of self, q converts to Makes a new Fraction; does not modify contents of self or q >>> r = p.__mul__(q) Precondition: q a Fraction""" assert type(q) == Fraction Operator overloading uses top= self._numerator*q._numerator bot= self._denominator*q._denominator method in object on left. return Fraction(top,bot) 11/7/19 Operators and Abstraction 10
Operator Overloading: Addition class Fraction(object): >>> p = Fraction(1,2) """Instance is a fraction n/d""” >>> q = Fraction(3,4) # _numerator: an int >>> r = p+q # _denominator: an int > 0 def __add__(self,q): Python """Returns: Sum of self, q converts to Makes a new Fraction Precondition: q a Fraction""" >>> r = p.__add__(q) assert type(q) == Fraction bot= self._denominator*q._denominator Operator overloading uses top= (self._numerator*q._denominator+ self._denominator*q._numerator) method in object on left. return Fraction(top,bot) 11/7/19 Operators and Abstraction 11
Comparing Objects for Equality class Fraction(object): • Earlier in course, we saw == """Instance is a fraction n/d""" compare object contents # _numerator: an int § This is not the default # _denominator: an int > 0 § Default : folder names • Must implement __eq__ def __eq__(self,q): """Returns: True if self, q equal, § Operator overloading! False if not, or q not a Fraction""" § Not limited to simple if type(q) != Fraction: attribute comparison return False § Ex : cross multiplying left = self._numerator*q._denominator rght = self._denominator*q._numerator 4 1 2 4 return left == rght 2 4 11/7/19 Operators and Abstraction 12
is Versus == • p is q evaluates to False • p == q evaluates to True § Compares folder names § But only because method __eq__ compares contents § Cannot change this id2 id2 id3 id3 p q Point Point x 2.2 x 2.2 y 5.4 y 5.4 z 6.7 z 6.7 Always use (x is None) not (x == None) 11/7/19 Operators and Abstraction 13
Structure of a Proper Python Class class Fraction(object): Docstring describing class """Instance is a fraction n/d""" Attributes are all hidden # _numerator: an int # _denominator: an int > 0 def getNumerator(self): Getters and Setters. """Returns: Numerator of Fraction""" … def __init__(self,n=0,d=1): Initializer for the class. """Initializer: makes a Fraction""" Defaults for parameters. … def __add__(self,q): Python operator overloading """Returns: Sum of self, q""" … def normalize(self): Normal method definitions """Puts Fraction in reduced form""" … 11/7/19 Operators and Abstraction 14
Recall: Overloading Multiplication class Fraction(object): >>> p = Fraction(1,2) """Instance is a fraction n/d""" >>> q = 2 # an int # _numerator: an int >>> r = p*q # _denominator: an int > 0 def __mul__(self,q): Python """Returns: Product of self, q converts to Makes a new Fraction; does not modify contents of self or q >>> r = p.__mul__(q) # ERROR Precondition: q a Fraction""" assert type(q) == Fraction Can only multiply fractions. top = self._numerator*q._numerator bot= self._denominator*q._denominator But ints “make sense” too. return Fraction(top,bot) 11/7/19 Operators and Abstraction 15
Solution: Look at Argument Type class Fraction(object): • Overloading use left type … § p*q => p.__mul__(q) def __mul__(self,q): § Done for us automatically """Returns: Product of self, q Precondition: q a Fraction or int""" § Looks in class definition if type(q) == Fraction: • What about type on right ? return self._mulFrac(q) § Have to handle ourselves elif type(q) == int: return self._mulInt(q) • Can implement with ifs … § Write helper for each type def _mulInt(self,q): # Hidden method return Fraction(self._numerator*q, § Check type in method self._denominator) § Send to appropriate helper 11/7/19 Operators and Abstraction 16
A Better Multiplication class Fraction(object): >>> p = Fraction(1,2) … >>> q = 2 # an int def __mul__(self,q): >>> r = p*q """Returns: Product of self, q Precondition: q a Fraction or int""" Python if type(q) == Fraction: converts to return self._mulFrac(q) elif type(q) == int: return self._mulInt(q) >>> r = p.__mul__(q) # OK! … def _mulInt(self,q): # Hidden method See frac3.py for a full return Fraction(self._numerator*q, example of this method self._denominator) 11/7/19 Operators and Abstraction 17
Recommend
More recommend