Lab: Classes
Due: Wednesday, October 28 at 11:59pm (one week after it is assigned) Worth: 4 points
The final project is a Twitter clone. Its users and tweets are bundles of data with related operations, which classes can represent. In this lab, you will build User and Tweet classes. The final project will create these objects from database rows and render them on web pages.
Starter repository: github.com/rtealwitter/lab-classes
Start
Select Use this template to create an independent repository named lab-classes under your account, then clone that copy. Open the lab_classes.py file it contains.
From dict to Class
The reading represented a tweet as a dictionary:
tweet = {'username': 'rtealwitter', 'text': 'my first tweet', 'likes': 0}The dictionary contains the data, but its operations live elsewhere, and Python identifies it only as a dictionary. A class defines a distinct type and keeps operations such as liking, unliking, and listing hashtags with the data they use.
Both classes in this lab start with a constructor that gives each new instance its initial data. The constructor is the method named __init__, which Python runs the moment you build an object:
class Tweet:
def __init__(self, author, text):
self.author = author
self.text = text
self.likes = 0self is the instance being built, which Python supplies automatically. The three assignments give every tweet an author, text, and zero likes. Use the same pattern for both classes: set each object’s initial data in __init__.
Building a User
Start with User, the simpler of the two. A user has a username and a follower count, and the count changes over time, so the class needs methods that mutate the instance.
Open lab_classes.py and fill in the User class so that:
The doctests specify the behavior, including the floor at zero:
>>> u = User('alice')
>>> u.follow()
>>> u.follow()
>>> u.unfollow()
>>> u.followers
1
>>> u.unfollow()
>>> u.unfollow()
>>> u.followers
0Repeated unfollow calls leave a count of zero at zero. This check prevents an invalid follower count.
Building a Tweet
Fill in Tweet so that:
The hashtags method uses string operations you already know. Split the text into words and select those that start with #:
>>> Tweet('alice', 'learning #python and #oop today').hashtags()
['python', 'oop']
>>> Tweet('bob', 'just a normal tweet').hashtags()
[]Remove the leading # from each result. A tweet with no tags returns an empty list.
Making Objects Printable and Comparable
By default, printing an object shows its type and memory address:
>>> print(User('alice'))
<__main__.User object at 0x10c3f9d90>Two dunder methods (double-underscore methods, like __init__) customize this built-in behavior. __str__ controls what print shows, and __eq__ controls what == means. Fill them in so that:
If the Twitter clone loads the same account twice from the database, the resulting objects should compare equal.
>>> User('alice') == User('alice')
TrueDuck Typing
Python often uses an object’s available operations instead of checking its declared type. A function that shows a post cares only that its argument has a .text:
def show(post):
print(post.text)Nothing in show says “this must be a Tweet.” It runs on a Tweet, Comment, or Message as long as the object has a .text attribute. This is duck typing: Python uses the operations an object supports rather than its declared type.
When to Use Classes
Classes are often overused. A function does not need to be wrapped in a class unless it operates on data that belongs with it.
Edsger Dijkstra once wrote, “Object-oriented programming is an exceptionally bad idea which could only have originated in California.”
Use a class when data and behavior belong together. A User combines a username with user operations, and a Tweet combines text with tweet operations. Those relationships justify the two classes in this lab.
Submit
This lab is auto-graded. Open lab_classes.py, fill in one method body at a time, and run the doctests:
$ python3 -m doctest lab_classes.pyNo output means every test passed. When a test fails, the command prints the example with its Expected and Got values; add -v to display passing tests too. Fix each failure and rerun the tests.
Commit and sync lab_classes.py, then on Gradescope choose GitHub and submit your lab-classes repository and the branch containing your commit.