5 Intro to OOP

Class

Defining a class creates a new datatype. An object is an instance of a class (everything in python).

Attributes

An object whose datatype is of our class is called an instance of a class. For example 3 is an instance of the class int.

Each instance can hold more than just a singe piece of data, each of these pieces is called an instance attribute. You can think of this as nouns that describe the object.

Example of a Tweet as a class.

from datetime import date

class Tweet: 
	""""A tweet, like in Twitter.
	=== Attributes === 
	userid: the id of the user who wrote the tweet.
	created_at: the date the tweet was written. 
	content: the contents of the tweet.
	likes: the number of likes this tweet has received.
"""

# Attribute types, do not create instance variable and could be removed but is good practice and understood by automated tools
# client code
	userid: str 
	created_at: date
	content: str
	likes: int

	def __init__(self, who: str, when: date, what: str) -> None:
		self.userid = who
		self.created_at = when 
		self.content = what 
		self.likes = 0
	

*putting a _ in front of an attribute makes it "private"

Creating an instance: tweet = Tweet()
But in order to create and initialize instance attributes for a class we need to use the method __init__

self automatically receives the id of the instance that is to be initialized.

>>> t1 = Tweet('Giovanna', date(2022, 1, 18), 'Hello')

![|273](/img/user/Introduction to CS/attachments/Pasted image 20250112130225.webp)

and after the return Tweet.__init__ we have:
![|274](/img/user/Introduction to CS/attachments/Pasted image 20250112130620.webp)

we can access the attributes by using dot notation like t1.created_at

Methods

Functions inside the class, like the verbs related to the object. The benefit is that unlike a function, it doesn't require imports like a regular function; it is already associated with the class.

 class Tweet:
    ...

    def like(self, n: int) -> None:
        """Record the fact that <self> received <n> likes.

        Precondition: n >= 0
        """
        self.likes += n
tweet.like(10)

dot notation already passes self.

Can be called two ways, for example for strings

word = 'supercalifragilisticexpealidocious'
word.count('i')
# or 
str.count(word, 'i')

Special Methods

see: Magic Methods

Magic Methods (built-in methods)

Explanation of str and repr

  • __str__: This method is used to define a user-friendly string representation of an object, which is what you would see when using print() or str() on the object. It is meant to be readable and understandable for humans.

  • __repr__: This is what you get when you call repr() on an object or when you simply type the object in the interactive interpreter.

Python Magic Methods Classification (Dunder Methods)

1. Binary Operators

Operator Magic Method
+ __add__
- __sub__
* __mul__
** __pow__
// __floordiv__
/ __truediv__

2. Unary Operators

Operator Magic Method
- (negation) __neg__
abs() __abs__
~ (bitwise NOT) __invert__

3. Comparison Operators

Operator Magic Method
< __lt__
<= __le__
== __eq__
!= __ne__
> __gt__
>= __ge__

4. Iteration & Collection Protocols

Functionality Magic Method
Make object iterable __iter__
Define iteration __next__
Membership test (x in obj) __contains__
Indexing (obj[i]) __getitem__
Assigning values (obj[i] = x) __setitem__
Deleting elements (del obj[i]) __delitem__
Get length (len(obj)) __len__

5. String Representation & Display

Functionality Magic Method
User-friendly string (str(obj)) __str__
Debug representation (repr(obj)) __repr__
Custom formatting (format(obj)) __format__
Convert to bytes (bytes(obj)) __bytes__

6. Object Lifecycle (Creation & Destruction)

Functionality Magic Method
Object initialization __init__
Object creation (before init) __new__
Destructor (cleanup before deletion) __del__

7. Attribute Access & Management

Functionality Magic Method
Access missing attributes __getattr__
Set attributes (obj.attr = x) __setattr__
Delete attributes (del obj.attr) __delattr__
Customize dir(obj) output __dir__
Intercept all attribute access __getattribute__

8. Callable & Operator Overloading

Functionality Magic Method
Make object callable (obj()) __call__
Convert to boolean (bool(obj)) __bool__

9. Context Management (with statements)

Functionality Magic Method
Enter context (with obj:) __enter__
Exit context (cleanup) __exit__

Representation Invariants

Making sure that rules are consistent and documented. A representation invariant is a property of the instance attributes that every instance of a class must satisfy. There are documented in the doctring.

Documentation:

class Tweet:
    """A tweet, like in Twitter.

    === Attributes ===
    userid: the id of the user who wrote the tweet.
    created_at: the date the tweet was written.
    content: the contents of the tweet.
    likes: the number of likes this tweet has received.

    === Representation Invariants ===
    - len(self.content) <= 280
    """
    # Attribute types
    userid: str
    created_at: date
    content: str
    likes: int

Enforcement
Assume that all representation invariants are satisfied but also make sure that at the end of the method it is still satisfied. So it is a precondition and a postcondition. However, you can temporarily violate it in the body of the code.

You can use initializer arguments to further enforce like:

def __init__(self, who: str, when: date, what: str) -> None:
    """Initialize a new Tweet.

    If <what> is longer than 280 chars, only first 280 chars are stored.
	Precondition: len(what)<= 280 # here is one option ************

    >>> t = Tweet('Rukhsana', date(2017, 9, 16), 'Hey!')
    >>> t.userid
    'Rukhsana'
    >>> t.created_at
    datetime.date(2017, 9, 16)
    >>> t.content
    'Hey!'
    >>> t.likes
    0
    """
    self.userid = who
    self.created_at = when
    self.content = what[:280] # or here ************
    self.likes = 0

However nothing fully prevents client code from violating representation invariants.

Designing Classes

Thus far there are 3 elements:

  1. instance attributes (data)
  2. methods (operations)
  3. representation invariants (properties)

Design Recipe:
link

Main themes are, design before coding and information hiding (separation from the public interface).

Private-ness:

In python naming convention, anything considered private starts with a leading underscore. The client should not access or change the value. Python has a "we're all adults philosophy" so unlike other languages, editing the variable might not raise an error.

Class Composition

It's common for an instance of a class to have and attribute that refers to another class. For example, in Twitter, there may be a user class and a tweet class.