19.4. OOP Method

  • Methods are functions in the class

  • Prevents copy-paste code

  • Improves readability

  • Improves refactoring

  • Decomposes bigger problem into smaller chunks

  • At definition - self should always be a first parameter

  • At call - self is not passed as an argument (Python will do that)

method

Functions in the class which takes instance as first argument (self)

self

Instance on which method was called.

19.4.1. Define

  • At definition - self should always be a first parameter

  • Name self is just a convention. You can use any name you want, but it is recommended to use self.

>>> class User:
...     def login(self):
...         print('ok')

Name self is just a convention. You can use any name you want, but it is recommended to use self.

>>> class User:
...     def login(this):  # this is not recommended
...         print('ok')

19.4.2. Call

  • At call - self is not passed as an argument (Python will do that)

  • Methods must be called on an instance

  • Methods cannot be called on a class

  • In future you will learn about static-methods and class-methods

>>> class User:
...     def login(self):
...         print('ok')

Methods cannot be called directly on a class:

>>> User.login()
Traceback (most recent call last):
TypeError: User.login() missing 1 required positional argument: 'self'

Methods must be called on an instance:

>>> alice = User()
>>> alice.login()
ok

In future you will learn about static-methods and class-methods

19.4.3. Return

  • Return works the same as in functions

>>> class User:
...     def login(self):
...         return 'ok'
...
>>>
>>> alice = User()
>>> result = alice.login()
>>>
>>> print(result)
ok

19.4.4. Parameters

  • At definition - self should always be a first parameter

  • Later you will learn more advanced things like static methods etc.

  • Parameter - Receiving variable used within the function

  • Works exactly the same as in functions

  • Parameters could be required or default

Required parameter:

  • Necessary to call that function

  • Specified at leftmost side

Default parameter:

  • Optional to call that function

  • Default value will be overridden if specified at a call time

  • Specified at rightmost side

No parameters:

>>> class User:
...     def login(self):
...         ...

Required parameters:

>>> class User:
...     def login(self, username, password):
...         ...

Default parameters:

>>> class User:
...     def login(self, username=None, password=None):
...         ...

Required and Default Parameters:

>>> class User:
...     def login(self, username, password=None):
...         ...

19.4.5. Arguments

  • At call - self is not passed as an argument (Python will do that)

  • Works exactly the same as in functions

  • Positional arguments

  • Keyword arguments

  • Positional and keyword arguments

SetUp:

>>> class User:
...     def login(self, username, password=None):
...         ...
>>>
>>> alice = User()

Positional arguments:

>>> alice.login('alice')
>>> alice.login('alice', 'secret')

Keyword arguments (order does not matter):

>>> alice.login(username='alice', password='secret')
>>> alice.login(password='secret', username='alice')

Positional and keyword arguments:

>>> alice.login('alice', password='secret')

19.4.6. Raise

  • Return works the same as in functions

>>> class User:
...     def login(self):
...         raise PermissionError('Login forbidden')
>>>
>>>
>>> alice = User()
>>>
>>> alice.login()
Traceback (most recent call last):
PermissionError: Login forbidden

19.4.7. Catch

  • Return works the same as in functions

>>> class User:
...     def login(self):
...         raise PermissionError('Login forbidden')
>>>
>>>
>>> alice = User()
>>>
>>> try:
...     alice.login()
... except PermissionError as e:
...     print(f'Error: {e}')
...
Error: Login forbidden

19.4.8. Overloading

  • In Python you cannot overload methods

  • Last defined method will override previous definitions

>>> class User:
...     def login(self, username, password):
...         print('Authenticating with username and password')
...
...     def login(self, username, token):
...         print('Authenticating with username and token')
>>>
>>>
>>> alice = User()
>>>
>>> alice.login('alice', password='secret')
Traceback (most recent call last):
TypeError: User.login() got an unexpected keyword argument 'password'
>>>
>>> alice.login('alice', token='e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4')
Authenticating with username and token

19.4.9. Recap

  • Never print in a class

19.4.10. Use Case - 1

Unrelated functions:

>>> def add(a, b):
...     return a + b
>>>
>>> def sub(a, b):
...     return a - b
>>>
>>> def mul(a, b):
...     return a * b
>>>
>>> def div(a, b):
...     return a / b

Class serves as a namespace:

>>> class Calculator:
...     def add(self, a, b):
...         return a + b
...
...     def sub(self, a, b):
...         return a - b
...
...     def mul(self, a, b):
...         return a * b
...
...     def div(self, a, b):
...         return a / b

19.4.11. Use Case - 2

>>> class Calculator:
...     def add(self, a, b):
...         return a + b
...
...     def sub(self, a, b):
...         return a - b
...
...     def mul(self, a, b):
...         return a * b
...
...     def div(self, a, b):
...         return a / b
>>>
>>>
>>> calc = Calculator()
>>> calc.add(a=1, b=2)
3

19.4.12. Use Case - 3

Unrelated functions:

>>> def say_hello():
...     return 'hello'
>>>
>>> def say_goodbye():
...     return 'goodbye'
>>>
>>> def login(username, password):
...     return 'User logged-in'
>>>
>>> def logout():
...     return 'User logged-out'

Class serves as a namespace:

>>> class User:
...     def say_hello(self):
...         return 'hello'
...
...     def say_goodbye():
...         return 'goodbye'
...
...     def login(self, username, password):
...         return 'User logged-in'
...
...     def logout(self):
...         return 'User logged-out'

19.4.13. Use Case - 4

>>> class List:
...     def append(self, item):
...         ...
...
...     def remove(self, item):
...         ...
>>> users = List()
>>>
>>> users.append('alice')
>>> users.append('bob')
>>> users.append('carol')
>>>
>>> users.remove('carol')
>>> colors = List()
>>>
>>> colors.append('red')
>>> colors.append('green')
>>> colors.append('blue')
>>>
>>> colors.remove('red')

19.4.14. Use Case - 5

>>> class User:
...     def login(self, username, password):
...         if username == 'alice' and password == 'secret':
...             print('ok')
...         else:
...             raise PermissionError
...
>>>
>>>
>>> alice = User()
>>>
>>> alice.login()
Traceback (most recent call last):
TypeError: User.login() missing 2 required positional arguments: 'username' and 'password'
>>>
>>> alice.login('invalid', 'invalid')
Traceback (most recent call last):
PermissionError
>>>
>>> alice.login('alice', 'invalid')
Traceback (most recent call last):
PermissionError
>>>
>>> alice.login('invalid', 'alice')
Traceback (most recent call last):
PermissionError
>>>
>>> alice.login('alice', 'secret')
ok
>>>
>>> alice.login(username='alice', password='secret')
ok

19.4.15. Assignments

# %% About
# - Name: OOP Method One
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify class `User`
# 2. Define method `login` in class `User`
# 3. Method prints 'User logged-in'
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasę `User`
# 2. Zdefiniuj metodę `login` w klasie `User`
# 3. Metoda wypisuje 'User logged-in'
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Expected
# >>> alice = User()
#
# >>> alice.login()
# User logged-in

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0

>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'

>>> from inspect import isclass, ismethod

>>> assert isclass(User), \
'Object `User` has an invalid type; expected: `class`.'

>>> alice = User()
>>> assert ismethod(alice.login)

>>> alice.login()
User logged-in
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports

# %% Types
from typing import Callable
User: type
login: Callable[[object], None]

# %% Data

# %% Result
class User:
    ...

# %% About
# - Name: OOP Method Two
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify class `User`
# 2. Add new method `logout` in class `User`
# 3. Method prints 'User logged-out'
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasę `User`
# 2. Dodaj nową metodę `logout` w klasie `User`
# 3. Metoda wypisuje 'User logged-out'
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Expected
# >>> alice = User()
#
# >>> alice.login()
# User logged-in
#
# >>> alice.logout()
# User logged-out

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0

>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'

>>> from inspect import isclass, ismethod

>>> assert isclass(User), \
'Object `User` has an invalid type; expected: `class`.'

>>> alice = User()
>>> assert ismethod(alice.login)
>>> assert ismethod(alice.logout)

>>> alice.login()
User logged-in

>>> alice.logout()
User logged-out
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports

# %% Types
from typing import Callable
User: type
login: Callable[[object], None]
logout: Callable[[object], None]

# %% Data

# %% Result
class User:
    def login(self):
        print('User logged-in')

# %% About
# - Name: OOP Method Parameters
# - Difficulty: easy
# - Lines: 4
# - Minutes: 3

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify class `User`
# 2. Modify method `login` in class `User`:
#    - Method takes `username` and `password` as arguments
#    - Method checks if username is 'alice' and password is 'secret'
#    - If true: method prints 'User logged-in'
#    - If false: raise PermissionError with 'Invalid username or password'
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj klasę `User`
# 2. Zmodyfikuj metodę `login` w klasie `User`:
#    - Metoda przyjmuje `username` i `password` jako argumenty
#    - Metoda sprawdza czy username to 'alice' i password to 'secret'
#    - Jeżeli prawda: metoda wypisuje 'User logged-in'
#    - Jeżeli fałsz: rzuć PermissionError z 'Invalid username or password'
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Expected
# TODO: Write expected result

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0

>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'

>>> from inspect import isclass, ismethod

>>> assert isclass(User), \
'Object `User` has an invalid type; expected: `class`.'

>>> alice = User()
>>> assert ismethod(alice.login)
>>> assert ismethod(alice.logout)

>>> alice.login('alice', 'secret')
User logged-in

>>> alice.login(username='alice', password='secret')
User logged-in

>>> alice.login(username='alice', password='invalid')
Traceback (most recent call last):
PermissionError: Invalid username or password

>>> alice.login(username='invalid', password='secret')
Traceback (most recent call last):
PermissionError: Invalid username or password

>>> alice.login()
Traceback (most recent call last):
TypeError: User.login() missing 2 required positional arguments: 'username' and 'password'

>>> alice.logout()
User logged-out
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -f -v myfile.py`

# %% Imports

# %% Types
from typing import Callable
User: type
login: Callable[[object], None|Exception]
logout: Callable[[object], None]

# %% Data

# %% Result
class User:
    def login(self):
        print('User logged-in')

    def logout(self):
        print('User logged-out')