Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Object-Oriented Programming, Lecture notes of Object Oriented Programming

In Python, a magic method is a special method used to make an object behave like a built-in data type. Magic methods begins and ends with two underscores, like ...

Typology: Lecture notes

2021/2022

Uploaded on 09/27/2022

kiras
kiras 🇬🇧

4.7

(21)

293 documents

Partial preview of the text

Download Object-Oriented Programming and more Lecture notes Object Oriented Programming in PDF only on Docsity! Lab 4 Object-Oriented Programming Lab Objective: Python is a class-based language. A class is a blueprint for an object that binds together specified variables and routines. Creating and using custom classes is often a good way to clean and speed up a program. In this lab we learn how to define and use Python classes. In subsequents labs, we will create customized classes often for use in algorithms. Python Classes A Python class is a code block that defines a custom object and determines its be- havior. The class key word defines and names a new class. Other statements follow, indented below the class name, to determine the behavior of objects instantiated by the class. A class needs a method called a constructor that is called whenever the class instantiates a new object. The constructor specifies the initial state of the object. In Python, a class’s constructor is always named __init__(). For example, the following code defines a class for storing information about backpacks. class Backpack(object): """A Backpack object class. Has a name and a list of contents. Attributes: name (str): the name of the backpack's owner. contents (list): the contents of the backpack. """ def __init__(self, name): # This function is the constructor. """Set the name and initialize an empty contents list. Inputs: name (str): the name of the backpack's owner. Returns: A Backpack object wth no contents. """ self.name = name # Initialize some attributes. self.contents = [] 51 52 Lab 4. Object Oriented Programming This Backpack class has two attributes: name and contents. Attributes are variables stored within the class object. In the body of the class definition, attributes are accessed via the name self. This name refers to the object internally once it has been created. Instantiation The class code block above only defines a blueprint for backpack objects. To create a backpack object, we “call” the class like a function. An object created by a class is called an instance of the class. It is also said that a class instantiates objects. Classes may be imported in the same way as modules. In the following code, we import the Backpack class and instantiate a Backpack object. # Import the Backpack class and instantiate an object called 'my_backpack'. >>> from oop import Backpack >>> my_backpack = Backpack("Fred") # Access the object's attributes with a period and the attribute name. >>> my_backpack.name 'Fred' >>> my_backpack.contents [] # The object's attributes can be modified dynamically. >>> my_backpack.name = "George" >>> print(my_backpack.name) George Note Many programming languages distinguish between public and private attributes. In Python, all attributes are automatically public. However, an attribute can be hidden from the user in IPython by starting the name with an underscore. Methods In addition to storing variables as attributes, classes can have functions attached to them. A function that belongs to a specific class is called a method. Below we define two simple methods in the Backpack class. class Backpack(object): # ... def put(self, item): """Add 'item' to the backpack's list of contents.""" self.contents.append(item) def take(self, item): """Remove 'item' from the backpack's list of contents.""" self.contents.remove(item) 55 Since Knapsack inherits from Backpack, a knapsack object is a backpack object. All methods defined in the Backpack class are available to instances of the Knapsack class. For example, the dump() method is available even though it is not defined explicitly in the Knapsack class. >>> from oop import Knapsack >>> my_knapsack = Knapsack("Brady", "brown") >>> isinstance(my_knapsack, Backpack) # A Knapsack is a Backpack. True # The put() and take() method now require the knapsack to be open. >>> my_knapsack.put('compass') I'm closed! # Open the knapsack and put in some items. >>> my_knapsack.closed = False >>> my_knapsack.put("compass") >>> my_knapsack.put("pocket knife") >>> my_knapsack.contents ['compass', 'pocket knife'] # The dump method is inherited from the Backpack class, and # can be used even though it is not defined in the Knapsack class. >>> my_knapsack.dump() >>> my_knapsack.contents [] Problem 2. Create a Jetpack class that inherits from the Backpack class. 1. Overwrite the constructor so that in addition to a name, color, and maximum size, it also accepts an amount of fuel. Change the default value of max_size to 2, and set the default value of fuel to 10. Store the fuel as an attribute. 2. Add a fly() method that accepts an amount of fuel to be burned and decrements the fuel attribute by that amount. If the user tries to burn more fuel than remains, print “Not enough fuel!” and do not decrement the fuel. 3. Overwrite the dump() method so that both the contents and the fuel tank are emptied. Magic Methods In Python, a magic method is a special method used to make an object behave like a built-in data type. Magic methods begins and ends with two underscores, like the constructor __init__(). Every Python object is automatically endowed with several magic methods, but they are normally hidden from IPython’s object introspection because they begin with an underscore. To see an object’s magic methods, type an underscore before pressing tab. 56 Lab 4. Object Oriented Programming In [1]: run oop.py # Load the names from oop.py. In [2]: b = Backpack("Oscar", "green") In [3]: b. # Press 'tab' to see standard methods and attributes. b.name b.contents b.put b.take In [4]: b.put? Signature: b.put(item) Docstring: Add 'item' to the backpack's content list. File: ~/Downloads/Packs.py Type: instancemethod In [5]: b.__ # Now press 'tab' to see magic methods. b.__add__ b.__getattribute__ b.__reduce_ex__ b.__class__ b.__hash__ b.__repr__ b.__delattr__ b.__init__ b.__setattr__ b.__dict__ b.__lt__ b.__sizeof__ b.__doc__ b.__module__ b.__str__ b.__eq__ b.__new__ b.__subclasshook__ b.__format__ b.__reduce__ b.__weakref__ In [6]: b? Type: Backpack File: ~/Downloads/Packs.py Docstring: A Backpack object class. Has a name and a list of contents. Attributes: name (str): the name of the backpack's owner. contents (list): the contents of the backpack. Init docstring: Set the name and initialize an empty contents list. Inputs: name (str): the name of the backpack's owner. Returns: A backpack object wth no contents. Note In all of the preceding examples, the comments enclosed by sets of three double quotes are the object’s docstring, stored as the __doc__ attribute. A good docstring typically includes a summary of the class or function, information about the inputs and returns, and other notes. Modules also have a __doc__ attribute for describing the purpose of the file. Writing detailed docstrings is critical so that others can utilize your code correctly (and so that you don’t forget how to use your own code!). Now, suppose we wanted to add two backpacks together. How should addition be defined for backpacks? A simple option is to add the number of contents. Then if backpack A has 3 items and backpack B has 5 items, A+B should return 8. 57 class Backpack(object): # ... def __add__(self, other): """Add the number of contents of each Backpack.""" return len(self.contents) + len(other.contents) Using the + binary operator on two Backpack objects calls the class’s __add__() method. The object on the left side of the + is passed in to __add__() as self and the object on the right side of the + is passed in as other. >>> pack1 = Backpack("Rose", "red") >>> pack2 = Backpack("Carly", "cyan") # Put some items in the backpacks. >>> pack1.put("textbook") >>> pack2.put("water bottle") >>> pack2.put("snacks") # Now add the backpacks like numbers >>> pack1 + pack2 # Equivalent to pack1.__add__(pack2). 3 Subtraction, multiplication, division, and other standard operations may be similary defined with their corresponding magic methods (see Table 4.1). Comparisons Magic methods also facilitate object comparisons. For example, the __lt__() method corresponds to the < operator. Suppose one backpack is considered “less” than another if it has fewer items in its list of contents. class Backpack(object) # ... def __lt__(self, other): """Compare two backpacks. If 'self' has fewer contents than 'other', return True. Otherwise, return False. """ return len(self.contents) < len(other.contents) Now using the < binary operator on two Backpack objects calls __lt__(). As with addition, the object on the left side of the < operator is passed to __lt__() as self, and the object on the right is passed in as other. >>> pack1 = Backpack("Maggy", "magenta") >>> pack2 = Backpack("Yolanda", "yellow") >>> pack1.put('book') >>> pack2.put('water bottle') >>> pack1 < pack2 False >>> pack2.put('pencils') >>> pack1 < pack2 # Equivalent to pack1.__lt__(pack2). True 60 Lab 4. Object Oriented Programming Additional Material Static Attributes Attributes that are accessed through self are called instance attributes because they are bound to a particular instance of the class. In contrast, a static attribute is one that is shared between all instances of the class. To make an attribute static, declare it inside of the class block but outside of any of the class’s functions, and do not use self. Since the attribute is not tied to a specific instance of the class, it should be accessed or changed via the class name. For example, suppose our Backpacks all have the same brand name. class Backpack(object): # ... brand = "Adidas" Changing the brand name changes it on every backpack instance. >>> pack1 = Backpack("Bill", "blue") >>> pack2 = Backpack("William", "white") >>> print pack1.brand, pack2.brand Adidas Adidas # Change the brand name for the class to change it for all class instances. >>> print Backpack.brand Adidas >>> Backpack.brand = "Nike" >>> print pack1.brand, pack2.brand Nike Nike Static Methods Individual class methods can also be static. A static method can have no dependence on the attributes of individual instances of the class, so there can be no references to self inside the body of the method and self is not listed as an argument in the function definition. Thus only static attributes and other static methods are available within the body of a static method. Include the tag @staticmethod above the function definition to designate a method as static. class Backpack(object): # ... @staticmethod def origin(): print "Manufactured by " + Backpack.brand + ", inc." # We can call static methods before instantiating the class. Backpack.origin() Manufactured by Nike, inc. # The method can also be accessed through class instances. >>> pack = Backpack("Larry", "lime") >>> pack.origin() Manufactured by Nike, inc. 61 To practice these principles, consider adding a static attribute to the Backpack class to serve as a counter for a unique ID. In the constructor for the Backpack class, add an instance variable called self.ID. Set this ID based on the static ID variable, then increment the static ID so that the next Backpack object will have a new ID. More Magic Methods Explore the following magic methods and consider how they could be implemented for the Backpack class. Method Operation Trigger Function __repr__() Object representation repr() __nonzero__() Truth value bool() __len__() Object length or size len() __getitem__() Indexing and slicing self[index] __setitem__() Assignment via indexing self[index] = x __iter__() Iteration over the object iter() __reversed__() Reverse iteration over the object reversed() __contains__() Membership testing in See https://docs.python.org/2/reference/datamodel.html for more details and documentation on all magic methods. Hashing A hash value is an integer that identifies an object. The built-in hash() function calculates an object’s hash value by calling its __hash__() magic method. In Python, the built-in set and dict structures use hash values to store and retrieve objects in memory quickly. Thus if an object is unhashable, it cannot be put in a set or be used as a key in a dictionary. # Get the hash value of a hashable object. >>> hash("math") -8321016616855971138 # Create a dictionary and attempt to get its hash value. >>> example = {"math": 320} >>> hash(example) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unhashable type: 'dict' If the __hash__() method is not defined, the default hash value is the object’s memory address (accessible via the built-in function id()) divided by 16, rounded down to the nearest integer. However, two objects that compare as equal via the __eq__() magic method must have the same hash value. A simple __hash__() method for the Backpack class that conforms to this rule and returns an integer might be the following. 62 Lab 4. Object Oriented Programming class Backpack(object): # ... def __hash__(self): return hash(self.name) ^ hash(self.color) ^ hash(len(self.contents)) The caret operator ^ is a bitwise XOR (exclusive or). The bitwise AND operator & and the bitwise OR operator | are also good choices to use. Achtung! If a class does not have an __eq__() method it should not have a __hash__() method either. Furthermore, if a class defines __eq__() but not __hash__(), its instances may be usable in hashed collections like sets and dictionaries (because of the default hash value), but two instances that compare as equal may or may not be recognized as distinct in the collection.