Repository: arvimal/oop_with_python
Branch: master
Commit: 2ed3706e1117
Files: 53
Total size: 102.6 KB
Directory structure:
gitextract_3szx01a3/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── dependabot.yml
│ └── workflows/
│ └── codeql-analysis.yml
├── 01-classes/
│ └── .gitkeep
├── 02-instances/
│ └── .gitkeep
├── 03-class_attributes/
│ ├── 06-class-attributes-1.py
│ ├── 07-class-attributes-2.py
│ └── 08-class-instance-attributes-1.py
├── 04-init_constructor/
│ ├── 04-init_constructor-1.py
│ └── 05-init_constructor-2.py
├── 05-encapsulation/
│ ├── 01-encapsulation-1.py
│ ├── 02-encapsulation-2.py
│ └── 03-encapsulation-3.py
├── 06-inheritance/
│ ├── 09-inheritance-1.py
│ ├── 10-inheritance-2.py
│ ├── 13-inheriting-init-constructor-1.py
│ ├── 14-multiple-inheritance-1.py
│ ├── 15-multiple-inheritance-2.py
│ └── 16-multiple-inheritance-3.py
├── 07-multiple_inheritance/
│ └── .gitkeep
├── 08-mro/
│ └── .gitkeep
├── 09-polymorphism/
│ ├── 11-polymorphism-1.py
│ └── 12-polymorphism-2.py
├── 10-instance_methods/
│ ├── 17-instance_methods-1.py
│ └── 18-instance_methods-2.py
├── 11-class_methods/
│ ├── 27-classmethod-1.py
│ └── 28-classmethod-2.py
├── 12-static_methods/
│ ├── 29-staticmethod-1.py
│ └── 30-staticmethod-2.py
├── 13-decorators/
│ ├── 19-decorators-1.py
│ ├── 20-decorators-2.py
│ ├── 21-decorators-3.py
│ ├── 22-decorators-4.py
│ ├── 23-decorators-5.py
│ ├── 24-decorators-6.py
│ ├── 25-decorators-7.py
│ └── 26-class-decorators.py
├── 14-magic_methods/
│ ├── 31-magicmethods-1.py
│ └── 32-magicmethods-2.py
├── 15-abstract_base_classes/
│ ├── 34-abstractclasses-1.py
│ ├── 35-abstractclasses-2.py
│ └── 36-abstractclasses-3.py
├── 16-method_overloading/
│ ├── 37-method-overloading-1.py
│ ├── 38-method-overloading-2.py
│ └── 39-method-overloading-3.py
├── 17-super/
│ ├── 40-super-1.py
│ ├── 41-super-2.py
│ └── 42-super-3.py
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
└── SECURITY.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '27 12 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
================================================
FILE: 01-classes/.gitkeep
================================================
================================================
FILE: 02-instances/.gitkeep
================================================
================================================
FILE: 03-class_attributes/06-class-attributes-1.py
================================================
#!/usr/bin/env python2.7
# 06-class-attributes-1.py
# Here we define an attribute under the class `YourClass`
# as well as an attribute within the function.
# The attribute defined in the class is called `class attributes`
# and the attribute defined in the function is called `instance attributes`.
class YourClass(object):
classy = 10
def set_val(self):
self.insty = 100
dd = YourClass()
dd.classy # This will fetch the class attribute 10.
dd.set_val()
dd.insty # This will fetch the instance attribute 100.
# Once `dd` is instantiated, we can access both the class and instance
# attributes, ie.. dd.classy and dd.insty.
================================================
FILE: 03-class_attributes/07-class-attributes-2.py
================================================
#!/usr/bin/env python
# 07-class-attributes-2.py
# The code below shows two important points:
# a) A class attribute can be overridden in an instance, even
# though it is bad due to breaking Encapsulation.
# b) There is a lookup path for attributes in Python. The first being
# the method defined within the class, and then the class above it.
# We are overriding the 'classy' class attribute in the instance 'dd'.
# When it's overridden, the python interpreter reads the overridden value.
# But once the new value is deleted with 'del', the overridden value is no longer
# present in the instance, and hence the lookup goes a level above and gets it from
# the class.
class YourClass(object):
classy = "class value"
dd = YourClass()
print(dd.classy) # < This should return the string "class value"
dd.classy = "Instance value"
print(dd.classy) # This should return the string "Instance value"
# This will delete the value set for 'dd.classy' in the instance.
del dd.classy
# Since the overriding attribute was deleted, this will print 'class value'.
print(dd.classy)
================================================
FILE: 03-class_attributes/08-class-instance-attributes-1.py
================================================
#!/usr/bin/env python
# 08-class-instance-attributes-1.py
# This code shows that an Instance can access it's own
# attributes as well as Class attributes.
# We have a class attribute named 'count', and we add 1 to
# it each time we create an instance. This can help count the
# number of instances at the time of instantiation.
class InstanceCounter(object):
count = 0
def __init__(self, val):
self.val = val
InstanceCounter.count += 1
def set_val(self, newval):
self.val = newval
def get_val(self):
print(self.val)
def get_count(self):
print(InstanceCounter.count)
a = InstanceCounter(5)
b = InstanceCounter(10)
c = InstanceCounter(15)
for obj in (a, b, c):
print("value of obj: %s" % obj.get_val())
print("Count : %s" % obj.get_count())
================================================
FILE: 04-init_constructor/04-init_constructor-1.py
================================================
#!/usr/bin/env python
# 04-init_constructor.py
# __init__() is a constructor method which helps to
# set initial values while instatiating a class.
# __init__() will get called with the attributes set in __init__(),
# when a class is instantiated.
# The '__' before and after the method name denotes that
# the method is private. It's called private or magic methods
# since it's called internally and automatically.
class MyNum(object):
def __init__(self):
print("Calling the __init__() constructor!\n")
self.val = 0
def increment(self):
self.val = self.val + 1
print(self.val)
dd = MyNum()
dd.increment() # will print 1
dd.increment() # will print 2
================================================
FILE: 04-init_constructor/05-init_constructor-2.py
================================================
#!/usr/bin/env python
# 05-init_constructor-2.py
# We add a test in the __init__() constructor to check
# if 'value' is an int or not.
class MyNum(object):
def __init__(self, value):
try:
value = int(value)
except ValueError:
value = 0
self.value = value
def increment(self):
self.value = self.value + 1
print(self.value)
a = MyNum(10)
a.increment() # This should print 11
a.increment() # This should print 12
================================================
FILE: 05-encapsulation/01-encapsulation-1.py
================================================
#!/usr/bin/env python
# encapsulation-1.py
# Encapsulation means to preserve data in classes using methods
# Here, we're setting the 'val' attribute through 'set_val()'.
# See the next example, `encapsulation-2.py` for more info
# In this example, we have two methods, `set_val` and `get_val`.
# The first one sets the `val` value while the second one
# prints/returns the value.
class MyClass(object):
def set_val(self, val):
self.value = val
def get_val(self):
# print(self.value)
return self.value
a = MyClass()
b = MyClass()
a.set_val(10)
b.set_val(100)
a.get_val()
b.get_val()
# NOTE: If you run this code, it won't print anything to the screen.
# This is because, even if we're calling `a.get_val()` and `b.get_val()`,
# the `get_val()` function doesn't contain a `print()` function.
# If we want to get the output printed to screen, we should do any of
# the following:
# a) Either replace `return self.value` with `print(self.value)`
# or add a print statement **above** `return` as `print(self.value)`.
# b) Remove `return(self.value)` and replace it with `print(self.value)`
================================================
FILE: 05-encapsulation/02-encapsulation-2.py
================================================
#!/usr/bin/env python
# encapsulation-2.py
# This example builds on top of `encapsulation-1.py`.
# Here we see how we can set values in methods without
# going through the method itself, ie.. how we can break
# encapsulation.
# NOTE: BREAKING ENCAPSULATION IS BAD.
class MyClass(object):
def set_val(self, val):
self.value = val
def get_val(self):
print(self.value)
a = MyClass()
b = MyClass()
a.set_val(10)
b.set_val(1000)
a.value = 100 # <== Overriding `set_value` directly
# <== ie.. Breaking encapsulation
a.get_val()
b.get_val()
================================================
FILE: 05-encapsulation/03-encapsulation-3.py
================================================
#!/usr/bin/env python
# 03-encapsulation-3.py
# Here we look at another example, where we have three methods
# set_val(), get_val(), and increment_val().
# set_val() helps to set a value, get_val() prints the value,
# and increment_val() increments the set value by 1.
# We can still break encapsulation here by calling 'self.value'
# directly in an instance, which is **BAD**.
# set_val() forces us to input an integer, ie.. what the code wants
# to work properly. Here, it's possible to break the encapsulation by
# calling 'self.val` directly, which will cause unexpected results later.
# In this example, the code is written to enforce an intger as input, if we
# don't break encapsulation and go through the gateway 'set_val()'
#
class MyInteger(object):
def set_val(self, val):
try:
val = int(val)
except ValueError:
return
self.val = val
def get_val(self):
print(self.val)
def increment_val(self):
self.val = self.val + 1
print(self.val)
a = MyInteger()
a.set_val(10)
a.get_val()
a.increment_val()
print("\n")
# Trying to break encapsulation in a new instance with an int
c = MyInteger()
c.val = 15
c.get_val()
c.increment_val()
print("\n")
# Trying to break encapsulation in a new instance with a str
b = MyInteger()
b.val = "MyString" # <== Breaking encapsulation, works fine
b.get_val() # <== Prints the val set by breaking encap
b.increment_val() # This will fail, since str + int wont work
print("\n")
================================================
FILE: 06-inheritance/09-inheritance-1.py
================================================
#!/usr/bin/env python
# 09-inheritance-1.py
# The code below shows how a class can inherit from another class.
# We have two classes, `Date` and `Time`. Here `Time` inherits from
# `Date`.
# Any class inheriting from another class (also called a Parent class)
# inherits the methods and attributes from the Parent class.
# Hence, any instances created from the class `Time` can access
# the methods defined in the parent class `Date`.
class Date(object):
def get_date(self):
print("2016-05-14")
class Time(Date):
def get_time(self):
print("07:00:00")
# Creating an instance from `Date`
dt = Date()
dt.get_date() # Accesing the `get_date()` method of `Date`
print("--------")
# Creating an instance from `Time`.
tm = Time()
tm.get_time() # Accessing the `get_time()` method from `Time`.
# Accessing the `get_date() which is defined in the parent class `Date`.
tm.get_date()
================================================
FILE: 06-inheritance/10-inheritance-2.py
================================================
#!/usr/bin/env python
# 10-inheritance-2.py
# The code below shows another example of inheritance
# Dog and Cat are two classes which inherits from Animal.
# This an instance created from Dog or Cat can access the methods
# in the Animal class, ie.. eat().
# The instance of 'Dog' can access the methods of the Dog class
# and it's parent class 'Animal'.
# The instance of 'Cat' can access the methods of the Cat class
# and it's parent class 'Animal'.
# But the instance created from 'Cat' cannot access the attributes
# within the 'Dog' class, and vice versa.
class Animal(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print("%s is eating %s" % (self.name, food))
class Dog(Animal):
def fetch(self, thing):
print("%s goes after the %s" % (self.name, thing))
class Cat(Animal):
def swatstring(self):
print("%s shred the string!" % self.name)
d = Dog("Roger")
c = Cat("Fluffy")
d.fetch("paper")
d.eat("dog food")
print("--------")
c.eat("cat food")
c.swatstring()
# The below methods would fail, since the instances doesn't have
# have access to the other class.
c.fetch("frizbee")
d.swatstring()
================================================
FILE: 06-inheritance/13-inheriting-init-constructor-1.py
================================================
#!/usr/bin/env python
# 13-inheriting-init-constructor-1.py
# This is a normal inheritance example from which we build
# the next example. Make sure to read and understand the
# next example '14-inheriting-init-constructor-2.py'.
class Animal(object):
def __init__(self, name):
self.name = name
class Dog(Animal):
def fetch(self, thing):
print("%s goes after the %s" % (self.name, thing))
d = Dog("Roger")
print("The dog's name is", d.name)
d.fetch("frizbee")
================================================
FILE: 06-inheritance/14-multiple-inheritance-1.py
================================================
#!/usr/bin/env python
# 14-multiple-inheritance-1.py
# Python supports multiple inheritance and uses a depth-first order
# when searching for methods.
# This search pattern is call MRO (Method Resolution Order)
# This is the first example, which shows the lookup of a common
# function named 'dothis()', which we'll continue in other examples.
# As per the MRO output, it starts in class D, then B, A, and lastly C.
# Both A and C contains 'dothis()'. Let's trace how the lookup happens.
# As per the MRO output, it starts in class D, then B, A, and lastly C.
# class `A` defines 'dothis()' and the search ends there. It doesn't go to C.
# The MRO will show the full resolution path even if the full path is
# not traversed.
# The method lookup flow in this case is : D -> B -> A -> C
class A(object):
def dothis(self):
print("doing this in A")
class B(A):
pass
class C(object):
def dothis(self):
print("doing this in C")
class D(B, C):
pass
d_instance = D()
d_instance.dothis() # <== This should print from class A.
print("\nPrint the Method Resolution Order")
print(D.mro())
================================================
FILE: 06-inheritance/15-multiple-inheritance-2.py
================================================
#!/usr/bin/env python
# 15-multiple-inheritance-2.py
# Python supports multiple inheritance
# It uses a depth-first order when searching for methods.
# This search pattern is call MRO (Method Resolution Order)
# This is a second example, which shows the lookup of 'dothis()'.
# Both A and C contains 'dothis()'. Let's trace how the lookup happens.
# As per the MRO output using depth-first search,
# it starts in class D, then B, A, and lastly C.
# Here we're looking for 'dothis()' which is defined in class `C`.
# The lookup goes from D -> B -> A -> C.
# Since class `A` doesn't have `dothis()`, the lookup goes back to class `C`
# and finds it there.
class A(object):
def dothat(self):
print("Doing this in A")
class B(A):
pass
class C(object):
def dothis(self):
print("\nDoing this in C")
class D(B, C):
"""Multiple Inheritance,
D inheriting from both B and C"""
pass
d_instance = D()
d_instance.dothis()
print("\nPrint the Method Resolution Order")
print(D.mro())
================================================
FILE: 06-inheritance/16-multiple-inheritance-3.py
================================================
#!/usr/bin/env python
# 16-multiple-inheritance-3.py
# Python supports multiple inheritance
# and uses a depth-first order when searching for methods.
# This search pattern is call MRO (Method Resolution Order)
# Example for "Diamond Shape" inheritance
# Lookup can get complicated when multiple classes inherit
# from multiple parent classes.
# In order to avoid ambiguity while doing a lookup for a method
# in various classes, from Python 2.3, the MRO lookup order has an
# additional feature.
# It still does a depth-first lookup, but if the occurrence of a class
# happens multiple times in the MRO path, it removes the initial occurrence
# and keeps the latter.
# In the example below, class `D` inherits from `B` and `C`.
# And both `B` and `C` inherits from `A`.
# Both `A` and `C` has the method `dothis()`.
# We instantiate `D` and requests the 'dothis()' method.
# By default, the lookup should go D -> B -> A -> C -> A.
# But from Python 2.3, in order to reduce the lookup time,
# the MRO skips the classes which occur multiple times in the path.
# Hence the lookup will be D -> B -> C -> A.
class A(object):
def dothis(self):
print("doing this in A")
class B(A):
pass
class C(A):
def dothis(self):
print("doing this in C")
class D(B, C):
pass
d_instance = D()
d_instance.dothis()
print("\nPrint the Method Resolution Order")
print(D.mro())
================================================
FILE: 07-multiple_inheritance/.gitkeep
================================================
================================================
FILE: 08-mro/.gitkeep
================================================
================================================
FILE: 09-polymorphism/11-polymorphism-1.py
================================================
#!/usr/bin/env python
# 11-polymorphism-1.py
# Polymorphism means having the same interface/attributes in different
# classes.
# Polymorphism is the characteristic of being able to assign
# a different meaning or usage in different contexts.
# A not-so-clear/clean example is, different classes can have
# the same function name.
# Here, the class Dog and Cat has the same method named 'show_affection'
# Even if they are same, both does different actions in the instance.
#
# Since the order of the lookup is
# 'instance' -> 'class' -> 'parent class', even if the
# 'class' and 'parent class' has functions with the same name,
# the instance will only pick up the first hit,
# ie.. from the 'class' and won't go to the parent class.
class Animal(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print("{0} eats {1}".format(self.name, food))
class Dog(Animal):
def fetch(self, thing):
print("{0} goes after the {1}!".format(self.name, thing))
def show_affection(self):
print("{0} wags tail".format(self.name))
class Cat(Animal):
def swatstring(self):
print("{0} shreds more string".format(self.name))
def show_affection(self):
print("{0} purrs".format(self.name))
for a in (Dog("Rover"), Cat("Fluffy"), Cat("Lucky"), Dog("Scout")):
a.show_affection()
================================================
FILE: 09-polymorphism/12-polymorphism-2.py
================================================
#!/usr/bin/env python
# 12-polymorphism-2.py
# Another example for Polymorphism are the several inbuilt
# functions in Python. Take for example, the builtin function
# called 'len'.
# 'len' is available for almost all types, such as strings,
# ints, floats, dictionaries, lists, tuples etc..
# When len is called on a type, it actually calls the inbuilts
# private function 'len' on that type or __len__
# Every object type that supports 'len' will have a private
# 'len' function inbuilt.
# Hence, for example, a list type already has a 'len()'
# function inbuilt in the Python code, and when you run the
# len() function on the data type, it checks if the len
# private function is available for that type or not.
# If it is available, it runs that.
text = ["Hello", "Hola", "helo"]
print(len(text))
print(len("Hello"))
print(len({"a": 1, "b": 2, "c": 3}))
================================================
FILE: 10-instance_methods/17-instance_methods-1.py
================================================
#!/usr/bin/env python
# 17-instance_methods-1.py
# Instance methods are also known as Bound methods since the methods
# within a class are bound to the instance created from the class, via
# 'self'.
class A(object):
def method(*argv):
return argv
a = A()
print(a.method)
# The print() function will print the following :
# python 17-instance_methods-1.py
# <bound method A.method of <__main__.A object at 0x7fc91d83e790>>
# The output shows that 'method' is a bound method
================================================
FILE: 10-instance_methods/18-instance_methods-2.py
================================================
#!/usr/bin/env python3
# 18-instance_methods.py
# Instance methods are the normal way of accessing methods, seen in all
# classes till now. ie.. by instantiating instances from a class, and
# access the methods within the class. The usage of `self` is very
# important in instance methods due to `self` being a hook/handle to the
# instance itself, or the instance itself.
# We look into a previous example, once more, to understand `Instance methods`.
# We have an __init__() constructor, and three methods within the
# `InstanceCounter` class.
# Three instances a, b, and c are created from the class `InstanceCounter`.
# Since the methods defined in the class are accessed through the
# instances 'a', 'b', and 'c', these methods are called 'Instance
# methods'.
# Since the instance is bound to the methods defined in the class by the
# keyword `self`, we also call `Instance methods` as 'Bound methods'.
# In the code below, the instance is `obj` (the iterator) and we access
# each method as `obj.set_val()`, `obj.get_val()`, and `obj.get_count`.
class InstanceCounter(object):
count = 0
def __init__(self, val):
self.val = val
InstanceCounter.count += 1
def set_val(self, newval):
self.val = newval
def get_val(self):
return self.val
def get_count(self):
return InstanceCounter.count
a = InstanceCounter(5)
b = InstanceCounter(10)
c = InstanceCounter(15)
for obj in (a, b, c):
print("Value of object: %s" % (obj.get_val))
print("Count : %s " % (obj.get_count))
================================================
FILE: 11-class_methods/27-classmethod-1.py
================================================
#!/usr/bin/env python3
# 19-class_methods-1.py
# A classmethod is an inbuilt decorator which is called on functions via
# @classmethod.
# The @classmethod decorator marks the function/method as bound to the
# class and not to an instance.
# Remember that we used 'self' in a function within a class, which denoted
# the instance. In class methods, we use `cls` which denotes the class
# rather than the instance.
# The following example is a very simple explanation of class-methods.
# class_1() is a class method while class_2() is an instance method.
# Class methods can be accessed by the class as well as the instance.
# Instance methods can only be accessed by the Instance. That's why in this example, MyClass.class_2() will fail with an error.
# NOTE : For the class MyClass:
# MyClass is the class itself
# MyClass() is an instance
class MyClass(object):
@classmethod
def class_1(cls):
print("Class method 1")
def class_2(self):
print("Self/Instance method 1")
print("Calling the class `MyClass` directly without an instance:")
MyClass.class_1()
# MyClass.class_2()
# NOTE: You will want to comment `MyClass.class_2()` once you hit the `TypeError`
# to continue with the examples below.
print("\nCalling the instance `MyClass()`:")
MyClass().class_1()
MyClass().class_2()
================================================
FILE: 11-class_methods/28-classmethod-2.py
================================================
#!/usr/bin/env python
# 28-classmethod-2.py
# Reference: https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/
# Classmethods are decorators which are inbuilt in Python.
# We decorate a function as a classemethod using the decorator
# @classmethod.
# Class methods are used for functions which need not be
# called via an instance. Certain use cases may be:
# a) Creating instances take resources, hence the methods/functions
# which need necessarily
class MyClass(object):
count = 0
def __init__(self, val):
self.val = val
MyClass.count += 1
def set_val(self, newval):
self.val = newval
def get_val(self):
return self.val
@classmethod
def get_count(cls):
return cls.count
object_1 = MyClass(10)
print("\nValue of object : %s" % object_1.get_val())
print(MyClass.get_count())
object_2 = MyClass(20)
print("\nValue of object : %s" % object_2.get_val())
print(MyClass.get_count())
object_3 = MyClass(40)
print("\nValue of object : %s" % object_3.get_val())
print(MyClass.get_count())
================================================
FILE: 12-static_methods/29-staticmethod-1.py
================================================
#!/usr/bin/env python3
# 29-staticmethod-1.py
"""
# Refer https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/
# a) Static methods are functions methods which doesn’t need a binding
# to a class or an instance.
# b) Static methods, as well as Class methods, don’t require an instance
# to be called.
# c) Static methods doesn’t need self or cls as the first argument
# since it’s not bound to an instance or class.
# d) Static methods are normal functions, but within a class.
# e) Static methods are defined with the keyword @staticmethod
# above the function/method.
# f) Static methods are usually used to work on Class Attributes.
"""
class MyClass(object):
count = 0
def __init__(self, val):
self.val = self.filterint(val)
MyClass.count += 1
@staticmethod
def filterint(value):
if not isinstance(value, int):
print("Entered value is not an INT, value set to 0")
return 0
else:
return value
a = MyClass(5)
b = MyClass(10)
c = MyClass(15)
print(a.val)
print(b.val)
print(c.val)
print(a.filterint(100))
================================================
FILE: 12-static_methods/30-staticmethod-2.py
================================================
#!/usr/bin/env python
from __future__ import print_function
# 30-staticmethod-2.py
# Refer
# https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/
# Static methods are functions/methods which doesn’t need a binding to a class or an instance.
# Static methods, as well as Class methods, don’t require an instance to be called.
# Static methods doesn’t need self or cls as the first argument since it’s not bound to an instance or class.
# Static methods are normal functions, but within a class.
# Static methods are defined with the keyword @staticmethod above the function/method.
# Static methods are usually used to work on Class Attributes.
class MyClass(object):
# A class attribute
count = 0
def __init__(self, name):
print("An instance is created!")
self.name = name
MyClass.count += 1
# Our class method
@staticmethod
def status():
print("The total number of instances are ", MyClass.count)
print(MyClass.count)
my_func_1 = MyClass("MyClass 1")
my_func_2 = MyClass("MyClass 2")
my_func_3 = MyClass("MyClass 3")
MyClass.status()
print(MyClass.count)
================================================
FILE: 13-decorators/19-decorators-1.py
================================================
#!/usr/bin/env python
# 19-decorators-1.py
# Decorators, as simple as it gets :)
# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.
# By Jillian Munson - PyGotham 2014.
# https://www.youtube.com/watch?v=yW0cK3IxlHc
# Decorators are functions that compliment other functions,
# or in other words, modify a function or method.
# In the example below, we have a function named `decorated`.
# This function just prints "This happened".
# We have a decorator created named `inner_decorator()`.
# This decorator function has an function within, which
# does some operations (print stuff for simplicity) and then
# returns the return-value of the internal function.
# How does it work?
# a) The function `decorated()` gets called.
# b) Since the decorator `@my_decorator` is defined above
# `decorated()`, `my_decorator()` gets called.
# c) my_decorator() takes a function name as args, and hence `decorated()`
# gets passed as the arg.
# d) `my_decorator()` does it's job, and when it reaches `myfunction()`
# calls the actual function, ie.. decorated()
# e) Once the function `decorated()` is done, it gets back to `my_decorator()`.
# f) Hence, using a decorator can drastically change the behavior of the
# function you're actually executing.
def my_decorator(my_function): # <-- (4)
def inner_decorator(): # <-- (5)
print("This happened before!") # <-- (6)
my_function() # <-- (7)
print("This happens after ") # <-- (10)
print("This happened at the end!") # <-- (11)
return inner_decorator
# return None
@my_decorator # <-- (3)
def my_decorated(): # <-- (2) <-- (8)
print("This happened!") # <-- (9)
if __name__ == "__main__":
my_decorated() # <-- (1)
# This prints:
# # python 19-decorators-1.py
# This happened before!
# This happened!
# This happens after
# This happened at the end!
================================================
FILE: 13-decorators/20-decorators-2.py
================================================
#!/usr/bin/env python
# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.
# By Jillian Munson - PyGotham 2014.
# https://www.youtube.com/watch?v=yW0cK3IxlHc
# 20-decorators-2.py
# An updated version of 19-decorators-1.py
# This code snippet takes the previous example, and add a bit more information
# to the output.
import datetime
def my_decorator(inner):
def inner_decorator():
print(datetime.datetime.utcnow())
inner()
print(datetime.datetime.utcnow())
return inner_decorator
@my_decorator
def decorated():
print("This happened!")
if __name__ == "__main__":
decorated()
# This will print: (NOTE: The time will change of course :P)
# # python 20-decorators-2.py
# 2016-05-29 11:46:07.444330
# This happened!
# 2016-05-29 11:46:07.444367
================================================
FILE: 13-decorators/21-decorators-3.py
================================================
#!/usr/bin/env python
# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.
# By Jillian Munson - PyGotham 2014.
# https://www.youtube.com/watch?v=yW0cK3IxlHc
# This is an updated version of 20-decorators-2.py.
# Here, the `decorated()` function takes an argument
# and prints it back on terminal.
# When the decorator `@my_decorator` is called, it
# takes the function `decorated()` as its argument, and
# the argument of `decorated()` as the argument of `inner_decorator()`.
# Hence the arg `number` is passed to `num_copy`.
import datetime
def my_decorator(inner):
def inner_decorator(num_copy):
print(datetime.datetime.utcnow())
inner(int(num_copy) + 1)
print(datetime.datetime.utcnow())
return inner_decorator
@my_decorator
def decorated(number):
print("This happened : " + str(number))
if __name__ == "__main__":
decorated(5)
# This prints:
# python 21-decorators-3.py
# 2016-05-29 12:11:57.212125
# This happened : 6
# 2016-05-29 12:11:57.212168
================================================
FILE: 13-decorators/22-decorators-4.py
================================================
#!/usr/bin/env python
# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.
# By Jillian Munson - PyGotham 2014.
# https://www.youtube.com/watch?v=yW0cK3IxlHc
# 22-decorators-4.py
# This example builds on the previous decorator examples.
# The previous example, 21-decorators-3.py showed how to
# deal with one argument passed to the function.
# This example shows how we can deal with multiple args.
# Reminder : `args` is a list of arguments passed, while
# kwargs is a dictionary passed as arguments.
def decorator(inner):
def inner_decorator(*args, **kwargs):
print(args, kwargs)
return inner_decorator
@decorator
def decorated(string_args):
print("This happened : " + string_args)
if __name__ == "__main__":
decorated("Hello, how are you?")
# This prints :
# # python 22-decorators-4.py
# ('Hello, how are you?',)
================================================
FILE: 13-decorators/23-decorators-5.py
================================================
#!/usr/bin/env python
# 23-decorators-5.py
# Reference : https://www.youtube.com/watch?v=bxhuLgybIro
from __future__ import print_function
# 2. Decorator function
def handle_exceptions(func_name):
def inner(*args, **kwargs):
try:
return func_name(*args, **kwargs)
except Exception:
print("An exception was thrown : ", Exception)
return inner
# 1. Main function
@handle_exceptions
def divide(x, y):
return x / y
print(divide(8, 0))
================================================
FILE: 13-decorators/24-decorators-6.py
================================================
#!/usr/bin/env python
def decorator(inner):
def inner_decorator(*args, **kwargs):
print("This function takes " + str(len(args)) + " arguments")
inner(*args)
return inner_decorator
@decorator
def decorated(string_args):
print("This happened: " + str(string_args))
@decorator
def alsoDecorated(num1, num2):
print("Sum of " + str(num1) + "and" + str(num2) + ": " + str(num1 + num2))
if __name__ == "__main__":
decorated("Hello")
alsoDecorated(1, 2)
================================================
FILE: 13-decorators/25-decorators-7.py
================================================
#!/usr/bin/env python
# 25-decorators-7.py
# Reference https://www.youtube.com/watch?v=Slf1b3yUocc
# We have two functions, one which adds two numbers,
# and another which subtracts two numbers.
# We apply the decorator @double which takes in the
# functions that is called with the decorator, and doubles
# the output of the respective function.
def double(my_func):
def inner_func(a, b):
return 2 * my_func(a, b)
return inner_func
@double
def adder(a, b):
return a + b
@double
def subtractor(a, b):
return a - b
print(adder(10, 20))
print(subtractor(6, 1))
================================================
FILE: 13-decorators/26-class-decorators.py
================================================
#!/usr/bin/env python
# 26-class-decorators.py
# Reference : https://www.youtube.com/watch?v=Slf1b3yUocc
# Talk by Mike Burns
# Till the previous examples, we saw function decorators.
# But decorators can be applied to Classes as well.
# This example deals with class decorators.
# NOTE: If you are creating a decorator for a class, you'll it
# to return a Class.
# NOTE: Similarly, if you are creating a decorator for a function,
# you'll need it to return a function.
def honirific(cls):
class HonirificCls(cls):
def full_name(self):
return "Dr. " + super(HonirificCls, self).full_name()
return HonirificCls
@honirific
class Name(object):
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def full_name(self):
return " ".join([self.first_name, self.last_name])
result = Name("Vimal", "A.R").full_name()
print("Full name: {0}".format(result))
# This needs further check. Erroring out.
================================================
FILE: 14-magic_methods/31-magicmethods-1.py
================================================
#!/usr/bin/env python
# 30-magicmethods-1.py
# In the backend, python is mostly objects and method
# calls on objects.
# Here we see an example, where the `print()` function
# is just a call to the magic method `__repr__()`.
class PrintList(object):
def __init__(self, my_list):
self.mylist = my_list
def __repr__(self):
return str(self.mylist)
printlist = PrintList(["a", "b", "c"])
print(printlist.__repr__())
================================================
FILE: 14-magic_methods/32-magicmethods-2.py
================================================
#!/usr/bin/env python
# 31-magicmethods-2.py
# In the backend, python is mostly objects and method
# calls on objects.
# To read more on magic methods, refer :
# http://www.rafekettler.com/magicmethods.html
my_list_1 = ["a", "b", "c"]
my_list_2 = ["d", "e", "f"]
print("\nCalling the `+` builtin with both lists")
print(my_list_1 + my_list_2)
print("\nCalling `__add__()` with both lists")
print(my_list_1.__add__(my_list_2))
================================================
FILE: 15-abstract_base_classes/34-abstractclasses-1.py
================================================
#!/usr/bin/env python
# 34-abstractclasses-1.py
# This code snippet talks about Abstract Base Classes (abc).
# The `abc` module provides features to create
# Abstract Base Classes.
# To create an Abstract Base Class, set the `__metaclass__` magic method
# to `abc.ABCMeta`. This will mark the respective class as an Abstract
# Base Class.
# Now, in order to specify the methods which are to be enforced on the
# child classes, ie.. to create Abstract Methods, we use the decorator
# @abc.abstractmethod on the methods we need.
# The child class which inherits from an Abstract Base Class can implement
# methods of their own, but *should always* implement the methods defined in
# the parent ABC Class.
import abc
class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def set_val(self, val):
return
@abc.abstractmethod
def get_val(self):
return
# Abstract Base Class defined above ^^^
# Custom class inheriting from the above Abstract Base Class, below
class MyClass(My_ABC_Class):
def set_val(self, input):
self.val = input
def get_val(self):
print("\nCalling the get_val() method")
print("I'm part of the Abstract Methods defined in My_ABC_Class()")
return self.val
def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")
my_class = MyClass()
my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
================================================
FILE: 15-abstract_base_classes/35-abstractclasses-2.py
================================================
#!/usr/bin/env python
# 34-abstractclasses-1.py
# This code snippet talks about Abstract Base Classes (abc).
# The `abc` module provides features to create
# Abstract Base Classes.
# To create an Abstract Base Class, set the `__metaclass__` magic method
# to `abc.ABCMeta`. This will mark the respective class as an Abstract
# Base Class.
# Now, in order to specify the methods which are to be enforced on the
# child classes, ie.. to create Abstract Methods, we use the decorator
# @abc.abstractmethod on the methods we need.
# The child class which inherits from an Abstract Base Class can implement
# methods of their own, but *should always* implement the methods defined in
# the parent ABC Class.
# NOTE: This code will error out. This is an example on what
# happens when a child class doesn't implement the abstract methods
# defined in the Parent Class.
import abc
class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def set_val(self, val):
return
@abc.abstractmethod
def get_val(self):
return
# Abstract Base Class defined above ^^^
# Custom class inheriting from the above Abstract Base Class, below
class MyClass(My_ABC_Class):
def set_val(self, input):
self.val = input
def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")
my_class = MyClass()
my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
================================================
FILE: 15-abstract_base_classes/36-abstractclasses-3.py
================================================
#!/usr/bin/env python
# 34-abstractclasses-1.py
# This code snippet talks about Abstract Base Classes (abc).
# The `abc` module provides features to create
# Abstract Base Classes.
# To create an Abstract Base Class, set the `__metaclass__` magic method
# to `abc.ABCMeta`. This will mark the respective class as an Abstract
# Base Class.
# Now, in order to specify the methods which are to be enforced on the
# child classes, ie.. to create Abstract Methods, we use the decorator
# @abc.abstractmethod on the methods we need.
# The child class which inherits from an Abstract Base Class can implement
# methods of their own, but *should always* implement the methods defined in
# the parent ABC Class.
# NOTE: This code will error out. This is an example on what
# happens when a child class doesn't implement the abstract methods
# defined in the Parent Class.
import abc
class My_ABC_Class(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def set_val(self, val):
return
@abc.abstractmethod
def get_val(self):
return
# Abstract Base Class defined above ^^^
# Custom class inheriting from the above Abstract Base Class, below
class MyClass(My_ABC_Class):
def set_val(self, input):
self.val = input
def hello(self):
print("\nCalling the hello() method")
print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()")
my_class = My_ABC_Class()
my_class.set_val(10)
print(my_class.get_val())
my_class.hello()
================================================
FILE: 16-method_overloading/37-method-overloading-1.py
================================================
#!/usr/bin/env python
# 37-method-overloading-1.py
# Reference: O'Reilly Learning Path:
# http://shop.oreilly.com/product/0636920040057.do
# Chapter 24 : Method Overloading - Extending and Providing
# This code is an example on how we can extend a method inherited by
# a child class from the Parent class.
# 1) We have defined `MyClass()` as an abstract class,
# and it has three methods, my_set_val(), my_get_val(), and print_doc().
# 2) MyChildClass() inherits from MyClass()
# 3) MyChildClass() extends the parent's my_set_val() method
# by it's own my_set_val() method. It checks for the input,
# checks if it's an integer, and then calls the my_set_val()
# method in the parent.
# 4) The print_doc() method in the Parent is an abstract method
# and hence should be implemented in the child class MyChildClass()
import abc
class MyClass(object):
__metaclass__ = abc.ABCMeta
def my_set_val(self, value):
self.value = value
def my_get_val(self):
return self.value
@abc.abstractmethod
def print_doc(self):
return
class MyChildClass(MyClass):
def my_set_val(self, value):
if not isinstance(value, int):
value = 0
super(MyChildClass, self).my_set_val(self)
def print_doc(self):
print("Documentation for MyChild Class")
my_instance = MyChildClass()
my_instance.my_set_val(100)
print(my_instance.my_get_val())
print(my_instance.print_doc())
================================================
FILE: 16-method_overloading/38-method-overloading-2.py
================================================
#!/usr/bin/env python
# 37-method-overloading-1.py
# Reference: O'Reilly Learning Path:
# http://shop.oreilly.com/product/0636920040057.do
# Chapter 24 : Method Overloading - Extending and Providing
import abc
class GetSetParent(object):
__metaclass__ = abc.ABCMeta
def __init__(self, value):
self.val = 0
def set_val(self, value):
self.val = value
def get_val(self):
return self.val
@abc.abstractmethod
def showdoc(self):
return
class GetSetList(GetSetParent):
def __init__(self, value=0):
self.vallist = [value]
def get_val(self):
return self.vallist[-1]
def get_vals(self):
return self.vallist
def set_val(self, value):
self.vallist.append(value)
def showdoc(self):
print(
"GetSetList object, len {0}, store history of values set".format(
len(self.vallist)
)
)
================================================
FILE: 16-method_overloading/39-method-overloading-3.py
================================================
#!/usr/bin/env python
# 39-method-overloading-3.py
# We've seen that inherited methods can be overloaded.
# This is possible using in-built functions as well.
# Let's see how we can overload methods from the `list` module.
class MyList(list):
def __getitem__(self, index):
if index == 0:
raise IndexError
if index > 0:
index -= 1
return list.__getitem__(self, index)
def __setitem__(self, index, value):
if index == 0:
raise IndexError
if index > 0:
index -= 1
list.__setitem__(self, index, value)
x = MyList(["a", "b", "c"])
print(x)
print("-" * 10)
x.append("d")
print(x)
print("-" * 10)
x.__setitem__(4, "e")
print(x)
print("-" * 10)
print(x[1])
print(x.__getitem__(1))
print("-" * 10)
print(x[4])
print(x.__getitem__(4))
================================================
FILE: 17-super/40-super-1.py
================================================
#!/usr/bin/env python
# 40-super-1.py
# This is an example on how super() works
# in Inheritance.
# For more step-by-step details, refer :
# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/
class MyClass(object):
def func(self):
print("I'm being called from the Parent class")
class ChildClass(MyClass):
def func(self):
print("I'm actually being called from the Child class")
print("But...")
# Calling the `func()` method from the Parent class.
super(ChildClass, self).func()
my_instance_2 = ChildClass()
my_instance_2.func()
================================================
FILE: 17-super/41-super-2.py
================================================
#!/usr/bin/env python
# 41-super-2.py
# For more information on how this works, refer:
# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/
# https://arvimal.wordpress.com/2016/06/29/inheritance-and-method-overloading-object-oriented-programming/
import abc
class MyClass(object):
__metaclass__ = abc.ABCMeta
def my_set_val(self, value):
self.value = value
def my_get_val(self):
return self.value
@abc.abstractmethod
def print_doc(self):
return
class MyChildClass(MyClass):
def my_set_val(self, value):
if not isinstance(value, int):
value = 0
super(MyChildClass, self).my_set_val(self)
def print_doc(self):
print("Documentation for MyChild Class")
my_instance = MyChildClass()
my_instance.my_set_val(100)
print(my_instance.my_get_val())
my_instance.print_doc()
================================================
FILE: 17-super/42-super-3.py
================================================
#!/usr/bin/env python
# 42-super-3.py
# super() and __init__()
# Refer
# https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/
# http://www.blog.pythonlibrary.org/2014/01/21/python-201-what-is-super/
class A(object):
def foo(self):
print("A")
class B(A):
def foo(self):
print("B")
class C(A):
def foo(self):
print("C")
super(C, self).foo()
class D(B, C):
def foo(self):
print("D")
super(D, self).foo()
d = D()
d.foo()
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Vimal A R
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Object Oriented Programming in Python
A deep-dive study guide covering core OOP concepts in Python, with annotated code examples. Originally written as interview preparation notes; expanded to be a complete reference.
---
## Table of Contents
01. [Classes](#01-classes)
02. [Instances, Instance Methods, Instance Attributes](#02-instances-instance-methods-instance-attributes)
03. [Class Attributes](#03-class-attributes)
04. [The `__init__` Constructor](#04-the-__init__-constructor)
05. [Encapsulation](#05-encapsulation)
06. [Inheritance](#06-inheritance)
07. [Multiple Inheritance and Method/Attribute Lookup](#07-multiple-inheritance-and-methodattribute-lookup)
08. [Method Resolution Order (MRO)](#08-method-resolution-order-mro)
09. [Polymorphism](#09-polymorphism)
10. [Instance Methods (Bound Methods)](#10-instance-methods-bound-methods)
11. [Class Methods](#11-class-methods)
12. [Static Methods](#12-static-methods)
13. [Decorators](#13-decorators)
14. [Magic Methods (Dunder Methods)](#14-magic-methods-dunder-methods)
15. [Abstract Base Classes](#15-abstract-base-classes)
16. [Method Overloading](#16-method-overloading)
17. [super()](#17-super)
---
## 01. Classes
A **class** is the fundamental building block of Object Oriented Programming. Think of it as a blueprint or template that describes the structure and behavior of objects created from it.
- A class defines **attributes** (data) and **methods** (behavior).
- Creating a class doesn't allocate memory for data — it only defines what an object of that class will look like.
- Everything in Python is an object, and every object belongs to some class.
```python
class Dog(object):
pass
```
`object` is the root of Python's class hierarchy. In Python 3, all classes implicitly inherit from `object`, so `class Dog:` and `class Dog(object):` are equivalent. In Python 2, you must explicitly write `class Dog(object):` to get a **new-style class** (as opposed to old-style classes that don't inherit from `object`).
**Key terminology:**
| Term | Meaning |
| --- | --- |
| Class | The blueprint / template |
| Instance | A concrete object created from a class |
| Attribute | Data stored on a class or instance |
| Method | A function defined inside a class |
**Visualizing a class as a blueprint:**
```text
┌────────────────────────────────────────┐
│ CLASS Dog ← the blueprint │
│ ──────────────────────────────────── │
│ Attributes: name, breed │
│ Methods: bark() │
│ fetch(thing) │
└──────────────┬─────────────────────────┘
│ Dog("Rover") Dog("Fido")
│ instantiate ──────────────────────┐
▼ ▼
┌────────────────────────┐ ┌────────────────────────┐
│ INSTANCE rover │ │ INSTANCE fido │
│ ──────────────────── │ │ ──────────────────── │
│ name = "Rover" │ │ name = "Fido" │
│ breed = "Labrador" │ │ breed = "Poodle" │
└────────────────────────┘ └────────────────────────┘
```
Each instance is independent — `rover.name` and `fido.name` have different values and changing one never affects the other.
---
## 02. Instances, Instance Methods, Instance Attributes
### Instances
An **instance** is a concrete object created from a class using the call syntax `ClassName()`. Each instance is independent — changes to one instance do not affect others.
```python
class Dog(object):
def bark(self):
print("Woof!")
rover = Dog() # rover is an instance of Dog
fido = Dog() # fido is a separate instance of Dog
rover.bark() # Woof!
fido.bark() # Woof!
```
### Instance Attributes
**Instance attributes** are data that belong to a specific instance. They are created by assigning to `self.something` inside a method, most commonly inside `__init__`.
```python
class Dog(object):
def __init__(self, name):
self.name = name # instance attribute
rover = Dog("Rover")
fido = Dog("Fido")
print(rover.name) # Rover
print(fido.name) # Fido — completely independent
```
### Instance Methods
**Instance methods** are ordinary methods that operate on an instance. They always receive `self` as their first argument, which is the handle to the calling instance. Any method you define in a class that takes `self` is an instance method.
```python
class Dog(object):
def __init__(self, name):
self.name = name
def bark(self):
print(f"{self.name} says Woof!")
rover = Dog("Rover")
rover.bark() # Rover says Woof!
```
When you write `rover.bark()`, Python automatically passes `rover` as the `self` argument. This is equivalent to `Dog.bark(rover)`.
---
## 03. Class Attributes
**Class attributes** are defined directly on the class body, outside any method. They are shared across all instances of the class.
```python
class YourClass(object):
classy = 10 # class attribute
def set_val(self):
self.insty = 100 # instance attribute (set inside a method)
dd = YourClass()
print(dd.classy) # 10 — fetched from the class
dd.set_val()
print(dd.insty) # 100 — fetched from the instance
```
### Attribute Lookup Order
Python's attribute lookup follows this order: **instance → class → parent classes**.
```mermaid
flowchart TD
A["Access obj.attr"] --> B{"In instance\n__dict__?"}
B -- Yes --> C["✅ Return instance value"]
B -- No --> D{"In class\n__dict__?"}
D -- Yes --> E["✅ Return class value"]
D -- No --> F{"In a parent\nclass?"}
F -- Yes --> G["✅ Return parent value"]
F -- No --> H["❌ AttributeError"]
```
This has an important consequence: if you set an attribute on an instance with the same name as a class attribute, the instance attribute shadows the class one:
```python
class YourClass(object):
classy = "class value"
dd = YourClass()
print(dd.classy) # "class value" — from the class
dd.classy = "Instance value"
print(dd.classy) # "Instance value" — from the instance (shadows class attr)
del dd.classy # remove the instance-level shadow
print(dd.classy) # "class value" — falls back to the class
```
> Code example: [03-class_attributes/07-class-attributes-2.py](03-class_attributes/07-class-attributes-2.py)
### Class Attributes as Shared State
Class attributes are useful for tracking shared state, such as a count of all instances ever created:
```python
class InstanceCounter(object):
count = 0 # shared across all instances
def __init__(self, val):
self.val = val
InstanceCounter.count += 1 # update the class attribute directly
a = InstanceCounter(5)
b = InstanceCounter(10)
c = InstanceCounter(15)
print(InstanceCounter.count) # 3
```
> Code example: [03-class_attributes/08-class-instance-attributes-1.py](03-class_attributes/08-class-instance-attributes-1.py)
**Important:** Mutating a mutable class attribute (like a list) through an instance mutates the shared copy. Reassigning it (`self.attr = new_value`) creates an instance-level copy instead.
---
## 04. The `__init__` Constructor
`__init__` is a **magic method** (also called a dunder method — double underscore) that Python calls automatically when a new instance is created. It is the class constructor.
```python
class MyNum(object):
def __init__(self):
print("Instance created!")
self.val = 0 # set initial state
def increment(self):
self.val += 1
print(self.val)
dd = MyNum() # prints "Instance created!"
dd.increment() # 1
dd.increment() # 2
```
> Code example: [02-init_constructor/04-init_constructor-1.py](02-init_constructor/04-init_constructor-1.py)
### `__init__` with Arguments
`__init__` can accept arguments to configure each instance differently:
```python
class MyNum(object):
def __init__(self, value):
try:
value = int(value)
except ValueError:
value = 0
self.value = value
def increment(self):
self.value += 1
print(self.value)
a = MyNum(10)
a.increment() # 11
a.increment() # 12
```
> Code example: [02-init_constructor/05-init_constructor-2.py](02-init_constructor/05-init_constructor-2.py)
### `__init__` is not `__new__`
`__init__` *initializes* an already-created object. The actual memory allocation happens in `__new__`, which runs before `__init__`. In practice you rarely override `__new__` unless implementing singletons or immutable types like subclasses of `int` or `str`.
---
## 05. Encapsulation
**Encapsulation** is the principle of bundling data and the methods that operate on that data within a class, and restricting direct access to internal state. The idea is to interact with an object only through its public interface (methods), not by reaching into its internals.
```text
╔══════════════════════════════════════════╗
║ BankAccount object ║
║ ║
║ ┌──────────────────────────────────┐ ║
║ │ Private / Internal State │ ║
║ │ __balance = 1000 │ ║
║ └──────────────────────────────────┘ ║
║ ║
║ Public Interface (the only door in) ║
║ ┌────────────┐ ┌───────────────────┐ ║
║ │ deposit() │ │ get_balance() │ ║
║ └─────▲──────┘ └────────▲──────────┘ ║
╚═════════╪═══════════════════╪════════════╝
│ │
external code external code
calls deposit() calls get_balance()
✅ acct.deposit(50) — goes through the interface
❌ acct.__balance = 9999 — breaks encapsulation (bypasses validation)
```
### Basic Example
```python
class MyClass(object):
def set_val(self, val):
self.value = val
def get_val(self):
return self.value
a = MyClass()
a.set_val(10)
print(a.get_val()) # 10
```
> Code example: [01-encapsulation/01-encapsulation-1.py](01-encapsulation/01-encapsulation-1.py)
### Breaking Encapsulation
Python does **not** enforce encapsulation at the language level. You can bypass a setter and write directly to the attribute:
```python
a = MyClass()
a.set_val(10)
a.value = 999 # bypasses set_val() — breaking encapsulation
print(a.get_val()) # 999
```
> Code example: [01-encapsulation/02-encapsulation-2.py](01-encapsulation/02-encapsulation-2.py)
This is **bad practice** because the setter may contain validation logic. If you bypass it, you lose those guarantees:
```python
class MyInteger(object):
def set_val(self, val):
try:
val = int(val)
except ValueError:
return
self.val = val
def get_val(self):
print(self.val)
def increment_val(self):
self.val += 1
b = MyInteger()
b.val = "MyString" # breaking encapsulation — now val is a string
b.get_val() # prints "MyString"
b.increment_val() # TypeError: can only concatenate str (not "int") to str
```
> Code example: [01-encapsulation/03-encapsulation-3.py](01-encapsulation/03-encapsulation-3.py)
### Name Mangling for Private Attributes
Python uses **name mangling** as a convention for pseudo-private attributes:
- `_name` — single underscore: "internal use" by convention, not enforced. Importing modules with `from module import *` will skip these.
- `__name` — double underscore: Python renames this to `_ClassName__name` internally, making accidental access harder (but not impossible).
```python
class BankAccount(object):
def __init__(self, balance):
self.__balance = balance # name-mangled to _BankAccount__balance
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
acct = BankAccount(100)
acct.deposit(50)
print(acct.get_balance()) # 150
# print(acct.__balance) # AttributeError
print(acct._BankAccount__balance) # 150 — still accessible, just harder
```
### Properties: The Pythonic Way
The `@property` decorator provides a clean way to add getter/setter logic without changing the external interface:
```python
class Temperature(object):
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero!")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9 / 5 + 32
t = Temperature(100)
print(t.celsius) # 100
print(t.fahrenheit) # 212.0
t.celsius = 0
print(t.fahrenheit) # 32.0
```
---
## 06. Inheritance
**Inheritance** allows a class (the **child** or **subclass**) to acquire the attributes and methods of another class (the **parent** or **superclass**). This enables code reuse and the modeling of "is-a" relationships.
```mermaid
classDiagram
direction TB
class Animal {
+name : str
+__init__(name)
+eat(food)
}
class Dog {
+fetch(thing)
}
class Cat {
+swatstring()
}
Animal <|-- Dog : inherits
Animal <|-- Cat : inherits
note for Dog "Inherits: __init__, eat()\nOwn: fetch()"
note for Cat "Inherits: __init__, eat()\nOwn: swatstring()"
```
```python
class Animal(object):
def __init__(self, name):
self.name = name
def eat(self, food):
print(f"{self.name} is eating {food}")
class Dog(Animal): # Dog inherits from Animal
def fetch(self, thing):
print(f"{self.name} goes after the {thing}")
class Cat(Animal): # Cat also inherits from Animal
def swatstring(self):
print(f"{self.name} shreds the string!")
d = Dog("Roger")
c = Cat("Fluffy")
d.fetch("paper") # Dog's own method
d.eat("dog food") # inherited from Animal
c.eat("cat food") # inherited from Animal
c.swatstring() # Cat's own method
```
> Code examples: [04-inheritance/09-inheritance-1.py](04-inheritance/09-inheritance-1.py), [04-inheritance/10-inheritance-2.py](04-inheritance/10-inheritance-2.py)
**Key rules:**
- A child class inherits **all** methods and attributes from the parent.
- Sibling classes (`Dog` and `Cat`) cannot access each other's methods.
- A child can **override** a parent method by defining one with the same name.
- The parent class being inherited from is also called the **base class**.
### Inheriting `__init__`
If a child class does not define its own `__init__`, it inherits the parent's:
```python
class Animal(object):
def __init__(self, name):
self.name = name
class Dog(Animal):
def fetch(self, thing):
print(f"{self.name} goes after the {thing}")
d = Dog("Roger") # uses Animal's __init__
print(d.name) # Roger
d.fetch("frizbee")
```
> Code example: [04-inheritance/13-inheriting-init-constructor-1.py](04-inheritance/13-inheriting-init-constructor-1.py)
If the child defines its own `__init__`, it completely replaces the parent's — unless you explicitly call the parent's `__init__` using `super()` (see [section 17](#17-super)).
### Overriding Methods
A child class can override any parent method:
```python
class Animal(object):
def speak(self):
print("...")
class Dog(Animal):
def speak(self): # overrides Animal.speak
print("Woof!")
class Cat(Animal):
def speak(self): # overrides Animal.speak
print("Meow!")
Dog().speak() # Woof!
Cat().speak() # Meow!
```
---
## 07. Multiple Inheritance and Method/Attribute Lookup
Python allows a class to inherit from **multiple parent classes** simultaneously.
```mermaid
classDiagram
direction TB
class Flyable {
+fly()
}
class Swimmable {
+swim()
}
class Duck {
+fly()
+swim()
}
Flyable <|-- Duck : inherits
Swimmable <|-- Duck : inherits
```
```python
class Flyable(object):
def fly(self):
print("Flying!")
class Swimmable(object):
def swim(self):
print("Swimming!")
class Duck(Flyable, Swimmable):
pass
d = Duck()
d.fly() # inherited from Flyable
d.swim() # inherited from Swimmable
```
### Attribute Lookup Chain
When you access `instance.attribute`, Python follows this chain:
1. The instance's own `__dict__`
2. The instance's class
3. Parent classes in **MRO order** (see next section)
> Code examples: [04-inheritance/14-multiple-inheritance-1.py](04-inheritance/14-multiple-inheritance-1.py), [04-inheritance/15-multiple-inheritance-2.py](04-inheritance/15-multiple-inheritance-2.py)
---
## 08. Method Resolution Order (MRO)
**MRO** defines the order in which Python searches classes for a method or attribute. It determines which method wins when multiple classes in the hierarchy define the same name.
### Viewing the MRO
```python
print(ClassName.mro())
# or
print(ClassName.__mro__)
```
### The C3 Linearization Algorithm
Python 3 (and Python 2 new-style classes from 2.3 onwards) uses the **C3 linearization algorithm**, not a naive depth-first search. C3 guarantees:
- The class itself comes first.
- A class always appears before its parents.
- The order in which parents are listed in the class definition is preserved.
- No class appears more than once (duplicates are resolved by keeping the *last* occurrence).
### Simple Inheritance — Depth-First
```text
D inherits from B and C.
B inherits from A.
Both A and C define dothis().
Naive depth-first path: D → B → A → C → A
MRO (with C3): D → B → A → C → object
```
```mermaid
graph TD
A["A ✦ has dothis()"]
B["B (no dothis)"]
C["C ✦ has dothis()"]
D["D (start here)"]
B --> A
D --> B
D --> C
style D fill:#4a90d9,color:#fff
style A fill:#27ae60,color:#fff
style C fill:#e67e22,color:#fff
```
MRO lookup path for `D().dothis()`:
```mermaid
flowchart LR
D["D"] --> B["B"] --> A["A ✦ FOUND"] --> stop(["stop"])
style A fill:#27ae60,color:#fff
style D fill:#4a90d9,color:#fff
style stop fill:#ccc
```
```python
class A(object):
def dothis(self): print("doing this in A")
class B(A): pass
class C(object):
def dothis(self): print("doing this in C")
class D(B, C): pass
d = D()
d.dothis() # "doing this in A" — found in A before reaching C
print(D.mro()) # [D, B, A, C, object]
```
> Code example: [04-inheritance/14-multiple-inheritance-1.py](04-inheritance/14-multiple-inheritance-1.py)
### Diamond Inheritance — C3 Removes Duplicates
The **diamond problem** arises when two parents both inherit from the same grandparent. Naive depth-first would visit the grandparent twice.
```text
D inherits from B and C.
B inherits from A.
C inherits from A.
Both A and C define dothis().
Naive path: D → B → A → C → A (A appears twice)
C3 MRO: D → B → C → A (A's early occurrence removed)
```
The shape of this hierarchy gives it its name — the **diamond problem**:
```mermaid
graph TD
A["A ✦ has dothis()"]
B["B (no dothis)"]
C["C ✦ has dothis()"]
D["D (start here)"]
B --> A
C --> A
D --> B
D --> C
style D fill:#4a90d9,color:#fff
style A fill:#27ae60,color:#fff
style C fill:#e67e22,color:#fff
```
C3 MRO pushes `A` to the end so each class appears exactly once:
```mermaid
flowchart LR
D["D"] --> B["B"] --> C["C ✦ FOUND"] --> A["A"] --> obj["object"]
style C fill:#e67e22,color:#fff
style D fill:#4a90d9,color:#fff
```
`D().dothis()` resolves to **C**, not A — because C3 delayed A until after C.
```python
class A(object):
def dothis(self): print("doing this in A")
class B(A): pass
class C(A):
def dothis(self): print("doing this in C")
class D(B, C): pass
d = D()
d.dothis() # "doing this in C" — C comes before A in the MRO
print(D.mro()) # [D, B, C, A, object]
```
Because `A` appears in both `B`'s and `C`'s lineage, C3 pushes `A` to the end — after `C`. So the method is found in `C`, not `A`.
> Code example: [04-inheritance/16-multiple-inheritance-3.py](04-inheritance/16-multiple-inheritance-3.py)
**Interview tip:** When asked "which method gets called?", trace the MRO using `ClassName.mro()`. Never guess.
---
## 09. Polymorphism
**Polymorphism** means "many forms." In OOP, it means different classes can expose the same interface (method name), but each class implements it differently.
The key insight: **same call, different behavior depending on the actual type**.
```mermaid
flowchart LR
caller["for a in animals:\n a.show_affection()"]
caller --> Dog["Dog instance\n(Rover)"]
caller --> Cat1["Cat instance\n(Fluffy)"]
caller --> Cat2["Cat instance\n(Lucky)"]
caller --> Dog2["Dog instance\n(Scout)"]
Dog --> r1["'Rover wags tail'"]
Cat1 --> r2["'Fluffy purrs'"]
Cat2 --> r3["'Lucky purrs'"]
Dog2 --> r4["'Scout wags tail'"]
style Dog fill:#5dade2,color:#fff
style Dog2 fill:#5dade2,color:#fff
style Cat1 fill:#58d68d,color:#fff
style Cat2 fill:#58d68d,color:#fff
```
### Polymorphism through Inheritance
```python
class Animal(object):
def __init__(self, name):
self.name = name
class Dog(Animal):
def show_affection(self):
print(f"{self.name} wags tail")
class Cat(Animal):
def show_affection(self):
print(f"{self.name} purrs")
animals = [Dog("Rover"), Cat("Fluffy"), Cat("Lucky"), Dog("Scout")]
for a in animals:
a.show_affection() # each class handles it differently
```
> Code example: [05-polymorphism/11-polymorphism-1.py](05-polymorphism/11-polymorphism-1.py)
### Duck Typing
Python's polymorphism is rooted in **duck typing**: "If it walks like a duck and quacks like a duck, it's a duck." Python doesn't care about the type of an object — it only cares whether the object has the method you're trying to call.
```python
class Duck:
def speak(self): print("Quack!")
class Person:
def speak(self): print("I'm speaking!")
for obj in (Duck(), Person()):
obj.speak() # works — both have speak()
```
No common base class needed.
### Built-in Polymorphism
Python's built-in functions are themselves polymorphic. `len()` works on strings, lists, dicts, and any object that implements `__len__`:
```python
print(len("Hello")) # 5
print(len([1, 2, 3])) # 3
print(len({"a": 1, "b": 2})) # 2
```
Internally, `len(x)` calls `x.__len__()`. Any class that defines `__len__` participates in this protocol.
> Code example: [05-polymorphism/12-polymorphism-2.py](05-polymorphism/12-polymorphism-2.py)
---
## 10. Instance Methods (Bound Methods)
Instance methods are the default method type in Python. They take `self` as their first parameter, giving them access to the calling instance.
When you access an instance method via `instance.method`, Python returns a **bound method** — the function is bound to the instance, meaning `self` is automatically filled in.
```python
class A(object):
def method(self):
return self
a = A()
print(a.method)
# <bound method A.method of <__main__.A object at 0x...>>
```
> Code example: [07-static_class_instance_methods/17-instance_methods-1.py](07-static_class_instance_methods/17-instance_methods-1.py)
Calling `a.method()` is exactly equivalent to calling `A.method(a)`. Python inserts `a` as the first argument automatically — this is the mechanism behind `self`.
```python
class InstanceCounter(object):
count = 0
def __init__(self, val):
self.val = val
InstanceCounter.count += 1
def set_val(self, newval):
self.val = newval
def get_val(self):
return self.val
def get_count(self):
return InstanceCounter.count
a = InstanceCounter(5)
b = InstanceCounter(10)
c = InstanceCounter(15)
for obj in (a, b, c):
print(f"Value: {obj.get_val()}, Count: {obj.get_count()}")
```
> Code example: [07-static_class_instance_methods/18-instance_methods-2.py](07-static_class_instance_methods/18-instance_methods-2.py)
---
## 11. Class Methods
A **class method** is decorated with `@classmethod`. Instead of receiving the instance (`self`) as the first argument, it receives the **class itself** (`cls`). This means it operates on the class rather than on a specific instance.
```python
class MyClass(object):
@classmethod
def class_method(cls):
print(f"Called on class: {cls}")
def instance_method(self):
print(f"Called on instance: {self}")
MyClass.class_method() # works — called on the class directly
MyClass().class_method() # also works — cls is still MyClass
# MyClass.instance_method() # TypeError — no instance to bind self
MyClass().instance_method() # works
```
> Code example: [07-static_class_instance_methods/27-classmethod-1.py](07-static_class_instance_methods/27-classmethod-1.py)
### Common Use Case: Factory Methods and Class-Level State
```python
class MyClass(object):
count = 0
def __init__(self, val):
self.val = val
MyClass.count += 1
def get_val(self):
return self.val
@classmethod
def get_count(cls):
return cls.count # cls is MyClass here
obj1 = MyClass(10)
obj2 = MyClass(20)
print(MyClass.get_count()) # 2 — no instance needed
print(obj1.get_count()) # 2 — also accessible via instance
```
> Code example: [07-static_class_instance_methods/28-classmethod-2.py](07-static_class_instance_methods/28-classmethod-2.py)
**Class methods as alternative constructors** is a common pattern:
```python
class Date(object):
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_string(cls, date_string):
year, month, day = map(int, date_string.split("-"))
return cls(year, month, day) # cls() calls __init__
d = Date.from_string("2024-04-17")
print(d.year, d.month, d.day) # 2024 4 17
```
### Visual: What Each Method Type Can See
```text
┌─────────────────────────────────────────────────────────────────────────┐
│ MyClass │
│ │
│ class_attr = 0 ◄──────── shared class-level data │
│ │
│ def __init__(self): │
│ self.inst_attr = 10 ◄── per-instance data │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ Instance Method │ │ Class Method │ │ Static Method │ │
│ │ def m(self): │ │ @classmethod │ │ @staticmethod │ │
│ │ │ │ def m(cls): │ │ def m(): │ │
│ │ ✅ inst_attr │ │ ❌ inst_attr │ │ ❌ inst_attr │ │
│ │ ✅ class_attr │ │ ✅ class_attr │ │ ⚠️ class_attr │ │
│ │ via self │ │ via cls │ │ only via │ │
│ │ │ │ │ │ MyClass.attr │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────────┘ │
│ Called via instance Via class OR instance Via class OR instance │
└─────────────────────────────────────────────────────────────────────────┘
```
### Comparison: instance method vs class method vs static method
| | Instance Method | Class Method | Static Method |
| --- | --- | --- | --- |
| First arg | `self` (instance) | `cls` (class) | none |
| Access instance state? | yes | no | no |
| Access class state? | yes (via `self.__class__`) | yes (via `cls`) | only explicitly |
| Called on class directly? | no | yes | yes |
| Decorator | none | `@classmethod` | `@staticmethod` |
---
## 12. Static Methods
A **static method** is decorated with `@staticmethod`. It receives neither `self` nor `cls`. It is essentially a regular function that lives inside a class for organizational purposes.
Use a static method when the function is logically related to the class but does not need to access or modify class or instance state.
```python
class MyClass(object):
count = 0
def __init__(self, val):
self.val = self.filterint(val) # calling static method from __init__
MyClass.count += 1
@staticmethod
def filterint(value):
if not isinstance(value, int):
print("Value is not an int, defaulting to 0")
return 0
return value
a = MyClass(5)
b = MyClass("hello") # Value is not an int, defaulting to 0
print(a.val) # 5
print(b.val) # 0
print(MyClass.filterint(42)) # callable on the class directly
```
> Code example: [07-static_class_instance_methods/29-staticmethod-1.py](07-static_class_instance_methods/29-staticmethod-1.py)
```python
class MyClass(object):
count = 0
def __init__(self, name):
self.name = name
MyClass.count += 1
@staticmethod
def status():
print(f"Total instances: {MyClass.count}")
MyClass("Alpha")
MyClass("Beta")
MyClass("Gamma")
MyClass.status() # Total instances: 3
```
> Code example: [07-static_class_instance_methods/30-staticmethod-2.py](07-static_class_instance_methods/30-staticmethod-2.py)
**When to use which:**
- Use **instance method** when the method needs to read or write instance state.
- Use **class method** when the method operates on class-level state or serves as an alternative constructor.
- Use **static method** when the method is a utility that doesn't need access to either.
---
## 13. Decorators
A **decorator** is a function that takes another function (or class) as input, wraps it with additional behavior, and returns the modified function. The `@decorator_name` syntax is syntactic sugar for:
```python
my_function = decorator(my_function)
```
**What a decorator actually does — before and after:**
```text
BEFORE decoration AFTER @my_decorator
───────────────── ────────────────────────────────────────
┌──────────────────────────────────────┐
│ inner_decorator() ← what you call │
│ ┌────────────────────────────────┐ │
│ │ print("Before the function") │ │
┌─────────────────────┐ │ ├────────────────────────────────┤ │
│ my_decorated() │ ──────► │ │ my_decorated() ← original │ │
│ │ │ │ print("This happened!") │ │
│ print("This │ │ ├────────────────────────────────┤ │
│ happened!") │ │ │ print("After the function") │ │
└─────────────────────┘ │ └────────────────────────────────┘ │
└──────────────────────────────────────┘
```
The original function is **wrapped** — it still runs, but now surrounded by the decorator's extra behavior.
### Anatomy of a Decorator
```python
def my_decorator(my_function): # (1) receives the function to wrap
def inner_decorator(): # (2) defines the wrapper
print("Before the function") # (3) runs before
my_function() # (4) calls the original
print("After the function") # (5) runs after
return inner_decorator # (6) returns the wrapper, not the result
@my_decorator
def my_decorated():
print("This happened!")
my_decorated()
# Before the function
# This happened!
# After the function
```
> Code example: [06-decorators/19-decorators-1.py](06-decorators/19-decorators-1.py)
### Decorators with Timestamps
```python
import datetime
def my_decorator(inner):
def inner_decorator():
print(datetime.datetime.utcnow())
inner()
print(datetime.datetime.utcnow())
return inner_decorator
@my_decorator
def decorated():
print("This happened!")
decorated()
# 2024-04-17 10:00:00.000001
# This happened!
# 2024-04-17 10:00:00.000045
```
> Code example: [06-decorators/20-decorators-2.py](06-decorators/20-decorators-2.py)
### Decorators with Arguments
When the decorated function accepts arguments, the wrapper must accept them too:
```python
import datetime
def my_decorator(inner):
def inner_decorator(num_copy): # mirrors decorated()'s signature
print(datetime.datetime.utcnow())
inner(int(num_copy) + 1) # can transform the args
print(datetime.datetime.utcnow())
return inner_decorator
@my_decorator
def decorated(number):
print(f"This happened: {number}")
decorated(5)
# 2024-04-17 10:00:00.000001
# This happened: 6
# 2024-04-17 10:00:00.000038
```
> Code example: [06-decorators/21-decorators-3.py](06-decorators/21-decorators-3.py)
### Universal Decorators with `*args` and `**kwargs`
To write a decorator that works on any function regardless of signature, use `*args` and `**kwargs`:
```python
def decorator(inner):
def inner_decorator(*args, **kwargs):
print(f"This function takes {len(args)} positional argument(s)")
inner(*args, **kwargs)
return inner_decorator
@decorator
def greet(name):
print(f"Hello, {name}!")
@decorator
def add(a, b):
print(f"Sum: {a + b}")
greet("Alice") # This function takes 1 positional argument(s) / Hello, Alice!
add(3, 4) # This function takes 2 positional argument(s) / Sum: 7
```
> Code examples: [06-decorators/22-decorators-4.py](06-decorators/22-decorators-4.py), [06-decorators/24-decorators-6.py](06-decorators/24-decorators-6.py)
### Practical Example: Exception Handling Decorator
```python
def handle_exceptions(func):
def inner(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"An exception was thrown: {e}")
return inner
@handle_exceptions
def divide(x, y):
return x / y
print(divide(8, 2)) # 4.0
print(divide(8, 0)) # An exception was thrown: division by zero
```
> Code example: [06-decorators/23-decorators-5.py](06-decorators/23-decorators-5.py)
### Practical Example: Output Multiplier
```python
def double(my_func):
def inner_func(a, b):
return 2 * my_func(a, b)
return inner_func
@double
def adder(a, b):
return a + b
@double
def subtractor(a, b):
return a - b
print(adder(10, 20)) # 60 (2 * 30)
print(subtractor(6, 1)) # 10 (2 * 5)
```
> Code example: [06-decorators/25-decorators-7.py](06-decorators/25-decorators-7.py)
### Class Decorators
Decorators can be applied to entire classes. A class decorator receives the class as its argument and should return a class:
```python
def honorific(cls):
class HonorificCls(cls):
def full_name(self):
return "Dr. " + super().full_name()
return HonorificCls
@honorific
class Name(object):
def __init__(self, first, last):
self.first = first
self.last = last
def full_name(self):
return f"{self.first} {self.last}"
print(Name("Vimal", "A.R").full_name()) # Dr. Vimal A.R
```
> Code example: [06-decorators/26-class-decorators.py](06-decorators/26-class-decorators.py)
### The `functools.wraps` Best Practice
Wrapping a function loses its `__name__` and `__doc__`. Always use `functools.wraps` in production decorators:
```python
import functools
def my_decorator(func):
@functools.wraps(func) # preserves func's metadata
def wrapper(*args, **kwargs):
print("Before")
result = func(*args, **kwargs)
print("After")
return result
return wrapper
@my_decorator
def greet():
"""Says hello."""
print("Hello!")
print(greet.__name__) # greet (not "wrapper")
print(greet.__doc__) # Says hello.
```
---
## 14. Magic Methods (Dunder Methods)
**Magic methods** (also called **dunder methods**, from *d*ouble *under*score) are special methods that Python calls implicitly to implement operators, built-in functions, and protocols. They are the engine behind Python's data model.
All magic methods follow the pattern `__name__`.
### `__repr__` and `__str__`
- `__repr__`: machine-readable representation, used by `repr()` and in the REPL. Should ideally be a string that recreates the object.
- `__str__`: human-readable representation, used by `print()` and `str()`. Falls back to `__repr__` if not defined.
```python
class PrintList(object):
def __init__(self, my_list):
self.mylist = my_list
def __repr__(self):
return str(self.mylist)
def __str__(self):
return f"PrintList({self.mylist})"
pl = PrintList(["a", "b", "c"])
print(pl) # PrintList(['a', 'b', 'c']) — calls __str__
print(repr(pl)) # ['a', 'b', 'c'] — calls __repr__
```
> Code example: [07-static_class_instance_methods/31-magicmethods-1.py](07-static_class_instance_methods/31-magicmethods-1.py)
### Operators as Magic Methods
Every operator in Python maps to a magic method. When you write `a + b`, Python calls `a.__add__(b)`.
```python
my_list_1 = ["a", "b", "c"]
my_list_2 = ["d", "e", "f"]
print(my_list_1 + my_list_2) # ['a', 'b', 'c', 'd', 'e', 'f']
print(my_list_1.__add__(my_list_2)) # same result — + calls __add__
```
> Code example: [07-static_class_instance_methods/32-magicmethods-2.py](07-static_class_instance_methods/32-magicmethods-2.py)
### Common Magic Methods Reference
| Category | Method | Triggered by |
| --- | --- | --- |
| Representation | `__repr__` | `repr(obj)`, REPL display |
| Representation | `__str__` | `print(obj)`, `str(obj)` |
| Lifecycle | `__init__` | `ClassName(...)` |
| Lifecycle | `__new__` | object allocation (before `__init__`) |
| Lifecycle | `__del__` | garbage collection |
| Arithmetic | `__add__` | `a + b` |
| Arithmetic | `__sub__` | `a - b` |
| Arithmetic | `__mul__` | `a * b` |
| Arithmetic | `__truediv__` | `a / b` |
| Arithmetic | `__floordiv__` | `a // b` |
| Arithmetic | `__mod__` | `a % b` |
| Arithmetic | `__pow__` | `a ** b` |
| Comparison | `__eq__` | `a == b` |
| Comparison | `__ne__` | `a != b` |
| Comparison | `__lt__` | `a < b` |
| Comparison | `__le__` | `a <= b` |
| Comparison | `__gt__` | `a > b` |
| Comparison | `__ge__` | `a >= b` |
| Container | `__len__` | `len(obj)` |
| Container | `__getitem__` | `obj[key]` |
| Container | `__setitem__` | `obj[key] = val` |
| Container | `__delitem__` | `del obj[key]` |
| Container | `__contains__` | `item in obj` |
| Container | `__iter__` | `for x in obj` |
| Container | `__next__` | `next(obj)` |
| Context manager | `__enter__` | `with obj as x:` |
| Context manager | `__exit__` | end of `with` block |
| Callable | `__call__` | `obj(args)` |
| Attribute access | `__getattr__` | `obj.name` (when not found normally) |
| Attribute access | `__setattr__` | `obj.name = val` |
### Custom `__eq__` and `__hash__`
If you define `__eq__`, Python sets `__hash__` to `None` by default (making your object unhashable). Define both together if you need the object to work in sets or as dict keys:
```python
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
print({p1, p2}) # {Point} — one item, they're equal
```
---
## 15. Abstract Base Classes
An **Abstract Base Class (ABC)** is a class that defines a contract — a set of methods that every subclass *must* implement. You cannot instantiate an ABC directly; it exists purely as a template.
ABCs are defined using the `abc` module.
### Python 3 Style (Recommended)
```python
import abc
class MyABC(abc.ABC):
@abc.abstractmethod
def set_val(self, val):
pass
@abc.abstractmethod
def get_val(self):
pass
```
### Python 2 Style (Legacy — also works in Python 3)
```python
import abc
class MyABC(object):
__metaclass__ = abc.ABCMeta # Python 2 only
@abc.abstractmethod
def set_val(self, val):
return
@abc.abstractmethod
def get_val(self):
return
```
> Code examples: [08-abstract_classes/34-abstractclasses-1.py](08-abstract_classes/34-abstractclasses-1.py), [08-abstract_classes/35-abstractclasses-2.py](08-abstract_classes/35-abstractclasses-2.py)
### Implementing an ABC
A concrete subclass must implement **all** abstract methods, or it will also be abstract (and also uninstantiable):
```python
import abc
class My_ABC_Class(abc.ABC):
@abc.abstractmethod
def set_val(self, val):
pass
@abc.abstractmethod
def get_val(self):
pass
class MyClass(My_ABC_Class):
def set_val(self, value):
self.val = value
def get_val(self):
return self.val
def hello(self):
print("I'm not an abstract method — just a bonus!")
my_obj = MyClass()
my_obj.set_val(10)
print(my_obj.get_val()) # 10
my_obj.hello()
```
### What Happens When You Miss an Abstract Method
```python
class IncompleteClass(My_ABC_Class):
def set_val(self, val):
self.val = val
# get_val not implemented!
obj = IncompleteClass()
# TypeError: Can't instantiate abstract class IncompleteClass
# with abstract method get_val
```
> Code example: [08-abstract_classes/36-abstractclasses-3.py](08-abstract_classes/36-abstractclasses-3.py)
### Why ABCs?
- **Enforce contracts:** guarantee that subclasses provide required behavior.
- **Document intent:** clearly communicate which methods are part of the interface.
- **isinstance checks:** `isinstance(obj, MyABC)` returns `True` for any registered implementor, even without actual inheritance (via `MyABC.register(SomeClass)`).
ABCs are the Pythonic way to define interfaces. Languages like Java have a formal `interface` keyword; Python uses ABCs.
---
## 16. Method Overloading
**Method overloading** in Python means providing a new or extended implementation of a method that was inherited from a parent class. Python does not support overloading based on argument types the way C++ or Java do — instead, you override and extend.
### Extending a Parent Method
A child class can override a parent's method and then call the parent's version using `super()`:
```python
import abc
class MyClass(abc.ABC):
def my_set_val(self, value):
self.value = value
def my_get_val(self):
return self.value
@abc.abstractmethod
def print_doc(self):
pass
class MyChildClass(MyClass):
def my_set_val(self, value):
if not isinstance(value, int):
value = 0
super().my_set_val(value) # extend, not replace
def print_doc(self):
print("Documentation for MyChildClass")
obj = MyChildClass()
obj.my_set_val(100)
print(obj.my_get_val()) # 100
obj.print_doc()
```
> Code example: [09-method_overloading/37-method-overloading-1.py](09-method_overloading/37-method-overloading-1.py)
### Overloading to Change Behavior
A child class can completely change how a parent method works. Here `GetSetList` keeps a history of all values set, rather than just the current one:
```python
class GetSetParent(abc.ABC):
def __init__(self, value):
self.val = 0
def set_val(self, value):
self.val = value
def get_val(self):
return self.val
@abc.abstractmethod
def showdoc(self):
pass
class GetSetList(GetSetParent):
def __init__(self, value=0):
self.vallist = [value] # history of all values
def get_val(self):
return self.vallist[-1] # return most recent
def get_vals(self):
return self.vallist
def set_val(self, value):
self.vallist.append(value) # append instead of replace
def showdoc(self):
print(f"GetSetList, len={len(self.vallist)}, stores value history")
```
> Code example: [09-method_overloading/38-method-overloading-2.py](09-method_overloading/38-method-overloading-2.py)
### Overloading Built-in Methods
You can override built-in container methods by inheriting from built-in types like `list`:
```python
class MyList(list):
"""1-indexed list — index 1 maps to position 0."""
def __getitem__(self, index):
if index == 0:
raise IndexError("MyList is 1-indexed; index 0 is invalid")
if index > 0:
index -= 1
return list.__getitem__(self, index)
def __setitem__(self, index, value):
if index == 0:
raise IndexError
if index > 0:
index -= 1
list.__setitem__(self, index, value)
x = MyList(["a", "b", "c"])
print(x[1]) # "a" — 1-indexed
print(x[3]) # "c"
```
> Code example: [09-method_overloading/39-method-overloading-3.py](09-method_overloading/39-method-overloading-3.py)
### Python's Approach to Overloading by Argument Type
Python doesn't have method overloading by signature. Instead, use default arguments, `*args`, or type checking inside the method:
```python
class Adder(object):
def add(self, a, b=None):
if b is None:
return a + a # single arg: double it
return a + b
obj = Adder()
print(obj.add(5)) # 10
print(obj.add(5, 3)) # 8
```
---
## 17. `super()`
Without `super()`, a child's overridden method completely replaces the parent's. With `super()`, you can call the parent's version and **extend** rather than replace:
```text
Without super() With super()
─────────────── ────────────
ChildClass.func() ChildClass.func()
│ │
└─► "Child class" ├─► "Child class"
(parent never runs) │
└─► super().func()
│
└─► MyClass.func()
│
└─► "Parent class"
```
`super()` returns a proxy object that delegates method calls to a parent class, following the MRO. It is the correct way to call a parent's method from a child, especially in multiple inheritance scenarios.
### Basic Usage
```python
class MyClass(object):
def func(self):
print("Called from the Parent class")
class ChildClass(MyClass):
def func(self):
print("Called from the Child class")
super().func() # delegate to the parent
ChildClass().func()
# Called from the Child class
# Called from the Parent class
```
> Code example: [10-super/40-super-1.py](10-super/40-super-1.py)
### Python 2 vs Python 3 Syntax
```python
# Python 3 — preferred
super().method_name()
# Python 2 — explicit class and self required
super(ClassName, self).method_name()
```
Python 3 introduced the zero-argument form, which is cleaner and avoids repetition.
### `super()` with `__init__`
When a child class has its own `__init__` and needs to also initialize the parent, call `super().__init__()`:
```python
class Animal(object):
def __init__(self, name):
self.name = name
print(f"Animal.__init__ called for {name}")
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # initialize the Animal part
self.breed = breed
print(f"Dog.__init__ called, breed={breed}")
d = Dog("Rex", "Labrador")
# Animal.__init__ called for Rex
# Dog.__init__ called, breed=Labrador
print(d.name) # Rex
print(d.breed) # Labrador
```
Forgetting to call `super().__init__()` means the parent's initialization code never runs, leaving the parent's attributes unset.
### `super()` with `__init__` — Call Chain Diagram
```mermaid
sequenceDiagram
participant user as your code
participant Dog
participant Animal
user->>Dog: Dog("Rex", "Labrador")
Dog->>Dog: __init__(name, breed)
Dog->>Animal: super().__init__(name)
Animal->>Animal: self.name = "Rex"
Animal-->>Dog: done
Dog->>Dog: self.breed = "Labrador"
Dog-->>user: instance ready
```
### `super()` in Multiple Inheritance (Cooperative Inheritance)
In multiple inheritance, `super()` follows the MRO, not just the direct parent. This is called **cooperative multiple inheritance** — each class in the chain calls `super()`, ensuring all classes get initialized:
```python
class A(object):
def foo(self):
print("A")
class B(A):
def foo(self):
print("B")
class C(A):
def foo(self):
print("C")
super().foo() # calls A.foo per MRO
class D(B, C):
def foo(self):
print("D")
super().foo() # calls B.foo per MRO: [D, B, C, A]
d = D()
d.foo()
# D
# B
# (B doesn't call super, so chain stops)
```
> Code example: [10-super/42-super-3.py](10-super/42-super-3.py)
**Key insight:** `super()` doesn't mean "my direct parent" — it means "the next class in the MRO." This is what makes cooperative inheritance work. For it to work correctly, every class in the diamond should call `super()`.
```mermaid
flowchart TD
D["D.foo()\nprint('D')\nsuper().foo() ──►"]
B["B.foo()\nprint('B')\n(no super — chain stops)"]
C["C.foo()\n(not reached because B\ndoesn't call super)"]
A["A.foo()\n(not reached)"]
D --> B
B -.->|"would continue\nif B called super()"| C
C -.-> A
style D fill:#4a90d9,color:#fff
style B fill:#e67e22,color:#fff
style C fill:#aaa,color:#fff
style A fill:#aaa,color:#fff
```
### When to Use `super()`
- Always use `super()` instead of calling the parent class by name directly (`ParentClass.method(self)`). Direct calls break in multiple inheritance scenarios.
- Call `super().__init__()` in child `__init__` methods to ensure the parent is initialized.
- Use `super()` when overloading a method but still wanting the parent's behavior.
---
## Summary: The Four Pillars of OOP
| Pillar | What it means in Python |
| --- | --- |
| **Encapsulation** | Bundle data + methods together; use `_` / `__` naming conventions and `@property` to control access |
| **Inheritance** | A subclass acquires the attributes and methods of its parent; use `super()` to extend, not replace |
| **Polymorphism** | Different classes expose the same interface; Python uses duck typing — no common base required |
| **Abstraction** | Hide complexity behind a clean interface; use ABCs (`abc.ABC`) to enforce contracts on subclasses |
```text
┌─────────────────────────────────────────────────────────────────────────────┐
│ The Four Pillars of OOP │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────────────────────┐│
│ │ ENCAPSULATION │ │ INHERITANCE ││
│ │ │ │ ││
│ │ ╔═══════════════╗ │ │ Animal ──────────────────────────────┐ ││
│ │ ║ private state ║ │ │ │ eat() │ ││
│ │ ╚═══════════════╝ │ │ ▼ ▼ ││
│ │ [ public method ] │ │ Dog Cat ││
│ │ │ │ fetch() swatstring() ││
│ │ Hide internals, │ │ ││
│ │ expose interface │ │ Child reuses parent's code ││
│ └─────────────────────┘ └────────────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────────┐ ┌────────────────────────┐ │
│ │ POLYMORPHISM │ │ ABSTRACTION │ │
│ │ │ │ │ │
│ │ animal.speak() ──► Dog → "Woof!" │ │ class Shape(ABC): │ │
│ │ ──► Cat → "Meow!" │ │ @abstractmethod │ │
│ │ ──► Duck → "Quack!" │ │ def area(): ... │ │
│ │ │ │ │ │
│ │ Same call, different behavior │ │ Enforce a contract; │ │
│ │ based on the actual type │ │ hide the details │ │
│ └─────────────────────────────────────────┘ └────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
```
---
## Quick Reference: Python OOP Cheat Sheet
```text
Class definition: class MyClass(object):
Instance creation: obj = MyClass()
Instance attribute: self.x = value (inside a method)
Class attribute: x = value (in class body, outside methods)
Instance method: def method(self):
Class method: @classmethod / def method(cls):
Static method: @staticmethod / def method():
Property getter: @property / def attr(self):
Property setter: @attr.setter / def attr(self, value):
Abstract method: @abc.abstractmethod / def method(self):
Calling parent method: super().method()
MRO inspection: ClassName.mro()
Magic method example: def __repr__(self): return "..."
```
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
gitextract_3szx01a3/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── dependabot.yml │ └── workflows/ │ └── codeql-analysis.yml ├── 01-classes/ │ └── .gitkeep ├── 02-instances/ │ └── .gitkeep ├── 03-class_attributes/ │ ├── 06-class-attributes-1.py │ ├── 07-class-attributes-2.py │ └── 08-class-instance-attributes-1.py ├── 04-init_constructor/ │ ├── 04-init_constructor-1.py │ └── 05-init_constructor-2.py ├── 05-encapsulation/ │ ├── 01-encapsulation-1.py │ ├── 02-encapsulation-2.py │ └── 03-encapsulation-3.py ├── 06-inheritance/ │ ├── 09-inheritance-1.py │ ├── 10-inheritance-2.py │ ├── 13-inheriting-init-constructor-1.py │ ├── 14-multiple-inheritance-1.py │ ├── 15-multiple-inheritance-2.py │ └── 16-multiple-inheritance-3.py ├── 07-multiple_inheritance/ │ └── .gitkeep ├── 08-mro/ │ └── .gitkeep ├── 09-polymorphism/ │ ├── 11-polymorphism-1.py │ └── 12-polymorphism-2.py ├── 10-instance_methods/ │ ├── 17-instance_methods-1.py │ └── 18-instance_methods-2.py ├── 11-class_methods/ │ ├── 27-classmethod-1.py │ └── 28-classmethod-2.py ├── 12-static_methods/ │ ├── 29-staticmethod-1.py │ └── 30-staticmethod-2.py ├── 13-decorators/ │ ├── 19-decorators-1.py │ ├── 20-decorators-2.py │ ├── 21-decorators-3.py │ ├── 22-decorators-4.py │ ├── 23-decorators-5.py │ ├── 24-decorators-6.py │ ├── 25-decorators-7.py │ └── 26-class-decorators.py ├── 14-magic_methods/ │ ├── 31-magicmethods-1.py │ └── 32-magicmethods-2.py ├── 15-abstract_base_classes/ │ ├── 34-abstractclasses-1.py │ ├── 35-abstractclasses-2.py │ └── 36-abstractclasses-3.py ├── 16-method_overloading/ │ ├── 37-method-overloading-1.py │ ├── 38-method-overloading-2.py │ └── 39-method-overloading-3.py ├── 17-super/ │ ├── 40-super-1.py │ ├── 41-super-2.py │ └── 42-super-3.py ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md └── SECURITY.md
SYMBOL INDEX (169 symbols across 39 files)
FILE: 03-class_attributes/06-class-attributes-1.py
class YourClass (line 12) | class YourClass(object):
method set_val (line 15) | def set_val(self):
FILE: 03-class_attributes/07-class-attributes-2.py
class YourClass (line 20) | class YourClass(object):
FILE: 03-class_attributes/08-class-instance-attributes-1.py
class InstanceCounter (line 13) | class InstanceCounter(object):
method __init__ (line 16) | def __init__(self, val):
method set_val (line 20) | def set_val(self, newval):
method get_val (line 23) | def get_val(self):
method get_count (line 26) | def get_count(self):
FILE: 04-init_constructor/04-init_constructor-1.py
class MyNum (line 16) | class MyNum(object):
method __init__ (line 17) | def __init__(self):
method increment (line 21) | def increment(self):
FILE: 04-init_constructor/05-init_constructor-2.py
class MyNum (line 9) | class MyNum(object):
method __init__ (line 10) | def __init__(self, value):
method increment (line 17) | def increment(self):
FILE: 05-encapsulation/01-encapsulation-1.py
class MyClass (line 14) | class MyClass(object):
method set_val (line 15) | def set_val(self, val):
method get_val (line 18) | def get_val(self):
FILE: 05-encapsulation/02-encapsulation-2.py
class MyClass (line 13) | class MyClass(object):
method set_val (line 14) | def set_val(self, val):
method get_val (line 17) | def get_val(self):
FILE: 05-encapsulation/03-encapsulation-3.py
class MyInteger (line 23) | class MyInteger(object):
method set_val (line 24) | def set_val(self, val):
method get_val (line 31) | def get_val(self):
method increment_val (line 34) | def increment_val(self):
FILE: 06-inheritance/09-inheritance-1.py
class Date (line 16) | class Date(object):
method get_date (line 17) | def get_date(self):
class Time (line 21) | class Time(Date):
method get_time (line 22) | def get_time(self):
FILE: 06-inheritance/10-inheritance-2.py
class Animal (line 20) | class Animal(object):
method __init__ (line 21) | def __init__(self, name):
method eat (line 24) | def eat(self, food):
class Dog (line 28) | class Dog(Animal):
method fetch (line 29) | def fetch(self, thing):
class Cat (line 33) | class Cat(Animal):
method swatstring (line 34) | def swatstring(self):
FILE: 06-inheritance/13-inheriting-init-constructor-1.py
class Animal (line 10) | class Animal(object):
method __init__ (line 11) | def __init__(self, name):
class Dog (line 15) | class Dog(Animal):
method fetch (line 16) | def fetch(self, thing):
FILE: 06-inheritance/14-multiple-inheritance-1.py
class A (line 26) | class A(object):
method dothis (line 27) | def dothis(self):
class B (line 31) | class B(A):
class C (line 35) | class C(object):
method dothis (line 36) | def dothis(self):
class D (line 40) | class D(B, C):
FILE: 06-inheritance/15-multiple-inheritance-2.py
class A (line 23) | class A(object):
method dothat (line 24) | def dothat(self):
class B (line 28) | class B(A):
class C (line 32) | class C(object):
method dothis (line 33) | def dothis(self):
class D (line 37) | class D(B, C):
FILE: 06-inheritance/16-multiple-inheritance-3.py
class A (line 33) | class A(object):
method dothis (line 34) | def dothis(self):
class B (line 38) | class B(A):
class C (line 42) | class C(A):
method dothis (line 43) | def dothis(self):
class D (line 47) | class D(B, C):
FILE: 09-polymorphism/11-polymorphism-1.py
class Animal (line 23) | class Animal(object):
method __init__ (line 24) | def __init__(self, name):
method eat (line 27) | def eat(self, food):
class Dog (line 31) | class Dog(Animal):
method fetch (line 32) | def fetch(self, thing):
method show_affection (line 35) | def show_affection(self):
class Cat (line 39) | class Cat(Animal):
method swatstring (line 40) | def swatstring(self):
method show_affection (line 43) | def show_affection(self):
FILE: 10-instance_methods/17-instance_methods-1.py
class A (line 10) | class A(object):
method method (line 11) | def method(*argv):
FILE: 10-instance_methods/18-instance_methods-2.py
class InstanceCounter (line 29) | class InstanceCounter(object):
method __init__ (line 32) | def __init__(self, val):
method set_val (line 36) | def set_val(self, newval):
method get_val (line 39) | def get_val(self):
method get_count (line 42) | def get_count(self):
FILE: 11-class_methods/27-classmethod-1.py
class MyClass (line 28) | class MyClass(object):
method class_1 (line 30) | def class_1(cls):
method class_2 (line 33) | def class_2(self):
FILE: 11-class_methods/28-classmethod-2.py
class MyClass (line 18) | class MyClass(object):
method __init__ (line 21) | def __init__(self, val):
method set_val (line 25) | def set_val(self, newval):
method get_val (line 28) | def get_val(self):
method get_count (line 32) | def get_count(cls):
FILE: 12-static_methods/29-staticmethod-1.py
class MyClass (line 20) | class MyClass(object):
method __init__ (line 23) | def __init__(self, val):
method filterint (line 28) | def filterint(value):
FILE: 12-static_methods/30-staticmethod-2.py
class MyClass (line 17) | class MyClass(object):
method __init__ (line 21) | def __init__(self, name):
method status (line 28) | def status():
FILE: 13-decorators/19-decorators-1.py
function my_decorator (line 33) | def my_decorator(my_function): # <-- (4)
function my_decorated (line 45) | def my_decorated(): # <-- (2) <-- (8)
FILE: 13-decorators/20-decorators-2.py
function my_decorator (line 16) | def my_decorator(inner):
function decorated (line 26) | def decorated():
FILE: 13-decorators/21-decorators-3.py
function my_decorator (line 19) | def my_decorator(inner):
function decorated (line 29) | def decorated(number):
FILE: 13-decorators/22-decorators-4.py
function decorator (line 19) | def decorator(inner):
function decorated (line 27) | def decorated(string_args):
FILE: 13-decorators/23-decorators-5.py
function handle_exceptions (line 11) | def handle_exceptions(func_name):
function divide (line 23) | def divide(x, y):
FILE: 13-decorators/24-decorators-6.py
function decorator (line 4) | def decorator(inner):
function decorated (line 13) | def decorated(string_args):
function alsoDecorated (line 18) | def alsoDecorated(num1, num2):
FILE: 13-decorators/25-decorators-7.py
function double (line 15) | def double(my_func):
function adder (line 23) | def adder(a, b):
function subtractor (line 28) | def subtractor(a, b):
FILE: 13-decorators/26-class-decorators.py
function honirific (line 19) | def honirific(cls):
class Name (line 28) | class Name(object):
method __init__ (line 29) | def __init__(self, first_name, last_name):
method full_name (line 33) | def full_name(self):
FILE: 14-magic_methods/31-magicmethods-1.py
class PrintList (line 12) | class PrintList(object):
method __init__ (line 13) | def __init__(self, my_list):
method __repr__ (line 16) | def __repr__(self):
FILE: 15-abstract_base_classes/34-abstractclasses-1.py
class My_ABC_Class (line 25) | class My_ABC_Class(object):
method set_val (line 29) | def set_val(self, val):
method get_val (line 33) | def get_val(self):
class MyClass (line 42) | class MyClass(My_ABC_Class):
method set_val (line 43) | def set_val(self, input):
method get_val (line 46) | def get_val(self):
method hello (line 51) | def hello(self):
FILE: 15-abstract_base_classes/35-abstractclasses-2.py
class My_ABC_Class (line 29) | class My_ABC_Class(object):
method set_val (line 33) | def set_val(self, val):
method get_val (line 37) | def get_val(self):
class MyClass (line 46) | class MyClass(My_ABC_Class):
method set_val (line 47) | def set_val(self, input):
method hello (line 50) | def hello(self):
FILE: 15-abstract_base_classes/36-abstractclasses-3.py
class My_ABC_Class (line 29) | class My_ABC_Class(object):
method set_val (line 33) | def set_val(self, val):
method get_val (line 37) | def get_val(self):
class MyClass (line 46) | class MyClass(My_ABC_Class):
method set_val (line 47) | def set_val(self, input):
method hello (line 50) | def hello(self):
FILE: 16-method_overloading/37-method-overloading-1.py
class MyClass (line 27) | class MyClass(object):
method my_set_val (line 31) | def my_set_val(self, value):
method my_get_val (line 34) | def my_get_val(self):
method print_doc (line 38) | def print_doc(self):
class MyChildClass (line 42) | class MyChildClass(MyClass):
method my_set_val (line 43) | def my_set_val(self, value):
method print_doc (line 48) | def print_doc(self):
FILE: 16-method_overloading/38-method-overloading-2.py
class GetSetParent (line 12) | class GetSetParent(object):
method __init__ (line 16) | def __init__(self, value):
method set_val (line 19) | def set_val(self, value):
method get_val (line 22) | def get_val(self):
method showdoc (line 26) | def showdoc(self):
class GetSetList (line 30) | class GetSetList(GetSetParent):
method __init__ (line 31) | def __init__(self, value=0):
method get_val (line 34) | def get_val(self):
method get_vals (line 37) | def get_vals(self):
method set_val (line 40) | def set_val(self, value):
method showdoc (line 43) | def showdoc(self):
FILE: 16-method_overloading/39-method-overloading-3.py
class MyList (line 11) | class MyList(list):
method __getitem__ (line 12) | def __getitem__(self, index):
method __setitem__ (line 19) | def __setitem__(self, index, value):
FILE: 17-super/40-super-1.py
class MyClass (line 12) | class MyClass(object):
method func (line 13) | def func(self):
class ChildClass (line 17) | class ChildClass(MyClass):
method func (line 18) | def func(self):
FILE: 17-super/41-super-2.py
class MyClass (line 14) | class MyClass(object):
method my_set_val (line 18) | def my_set_val(self, value):
method my_get_val (line 21) | def my_get_val(self):
method print_doc (line 25) | def print_doc(self):
class MyChildClass (line 29) | class MyChildClass(MyClass):
method my_set_val (line 30) | def my_set_val(self, value):
method print_doc (line 35) | def print_doc(self):
FILE: 17-super/42-super-3.py
class A (line 13) | class A(object):
method foo (line 14) | def foo(self):
class B (line 18) | class B(A):
method foo (line 19) | def foo(self):
class C (line 23) | class C(A):
method foo (line 24) | def foo(self):
class D (line 29) | class D(B, C):
method foo (line 30) | def foo(self):
Condensed preview — 53 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (117K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 834,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/dependabot.yml",
"chars": 498,
"preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 2437,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": "01-classes/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "02-instances/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "03-class_attributes/06-class-attributes-1.py",
"chars": 651,
"preview": "#!/usr/bin/env python2.7\n\n# 06-class-attributes-1.py\n\n# Here we define an attribute under the class `YourClass`\n# as wel"
},
{
"path": "03-class_attributes/07-class-attributes-2.py",
"chars": 1085,
"preview": "#!/usr/bin/env python\n\n# 07-class-attributes-2.py\n\n# The code below shows two important points:\n\n# a) A class attribute "
},
{
"path": "03-class_attributes/08-class-instance-attributes-1.py",
"chars": 819,
"preview": "#!/usr/bin/env python\n\n# 08-class-instance-attributes-1.py\n\n# This code shows that an Instance can access it's own\n# att"
},
{
"path": "04-init_constructor/04-init_constructor-1.py",
"chars": 703,
"preview": "#!/usr/bin/env python\n\n# 04-init_constructor.py\n\n# __init__() is a constructor method which helps to\n# set initial value"
},
{
"path": "04-init_constructor/05-init_constructor-2.py",
"chars": 491,
"preview": "#!/usr/bin/env python\n\n# 05-init_constructor-2.py\n\n# We add a test in the __init__() constructor to check\n# if 'value' i"
},
{
"path": "05-encapsulation/01-encapsulation-1.py",
"chars": 1130,
"preview": "#!/usr/bin/env python\n\n# encapsulation-1.py\n\n# Encapsulation means to preserve data in classes using methods\n# Here, we'"
},
{
"path": "05-encapsulation/02-encapsulation-2.py",
"chars": 570,
"preview": "#!/usr/bin/env python\n\n# encapsulation-2.py\n\n# This example builds on top of `encapsulation-1.py`.\n# Here we see how we "
},
{
"path": "05-encapsulation/03-encapsulation-3.py",
"chars": 1515,
"preview": "#!/usr/bin/env python\n\n# 03-encapsulation-3.py\n\n# Here we look at another example, where we have three methods\n# set_val"
},
{
"path": "06-inheritance/09-inheritance-1.py",
"chars": 910,
"preview": "#!/usr/bin/env python\n\n# 09-inheritance-1.py\n\n# The code below shows how a class can inherit from another class.\n# We ha"
},
{
"path": "06-inheritance/10-inheritance-2.py",
"chars": 1189,
"preview": "#!/usr/bin/env python\n\n# 10-inheritance-2.py\n\n# The code below shows another example of inheritance\n# Dog and Cat are tw"
},
{
"path": "06-inheritance/13-inheriting-init-constructor-1.py",
"chars": 492,
"preview": "#!/usr/bin/env python\n\n# 13-inheriting-init-constructor-1.py\n\n# This is a normal inheritance example from which we build"
},
{
"path": "06-inheritance/14-multiple-inheritance-1.py",
"chars": 1130,
"preview": "#!/usr/bin/env python\n\n# 14-multiple-inheritance-1.py\n\n# Python supports multiple inheritance and uses a depth-first ord"
},
{
"path": "06-inheritance/15-multiple-inheritance-2.py",
"chars": 1029,
"preview": "#!/usr/bin/env python\n\n# 15-multiple-inheritance-2.py\n\n# Python supports multiple inheritance\n\n# It uses a depth-first o"
},
{
"path": "06-inheritance/16-multiple-inheritance-3.py",
"chars": 1404,
"preview": "#!/usr/bin/env python\n\n# 16-multiple-inheritance-3.py\n\n# Python supports multiple inheritance\n# and uses a depth-first o"
},
{
"path": "07-multiple_inheritance/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "08-mro/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "09-polymorphism/11-polymorphism-1.py",
"chars": 1366,
"preview": "#!/usr/bin/env python\n\n# 11-polymorphism-1.py\n\n# Polymorphism means having the same interface/attributes in different\n# "
},
{
"path": "09-polymorphism/12-polymorphism-2.py",
"chars": 866,
"preview": "#!/usr/bin/env python\n\n# 12-polymorphism-2.py\n\n# Another example for Polymorphism are the several inbuilt\n# functions in"
},
{
"path": "10-instance_methods/17-instance_methods-1.py",
"chars": 494,
"preview": "#!/usr/bin/env python\n\n# 17-instance_methods-1.py\n\n# Instance methods are also known as Bound methods since the methods\n"
},
{
"path": "10-instance_methods/18-instance_methods-2.py",
"chars": 1553,
"preview": "#!/usr/bin/env python3\n\n# 18-instance_methods.py\n\n# Instance methods are the normal way of accessing methods, seen in al"
},
{
"path": "11-class_methods/27-classmethod-1.py",
"chars": 1326,
"preview": "#!/usr/bin/env python3\n\n# 19-class_methods-1.py\n\n# A classmethod is an inbuilt decorator which is called on functions vi"
},
{
"path": "11-class_methods/28-classmethod-2.py",
"chars": 1113,
"preview": "#!/usr/bin/env python\n\n# 28-classmethod-2.py\n\n# Reference: https://jeffknupp.com/blog/2014/06/18/improve-your-python-pyt"
},
{
"path": "12-static_methods/29-staticmethod-1.py",
"chars": 1150,
"preview": "#!/usr/bin/env python3\n\n# 29-staticmethod-1.py\n\n\"\"\"\n# Refer https://arvimal.wordpress.com/2016/06/12/instance-class-stat"
},
{
"path": "12-static_methods/30-staticmethod-2.py",
"chars": 1171,
"preview": "#!/usr/bin/env python\nfrom __future__ import print_function\n\n# 30-staticmethod-2.py\n\n# Refer\n# https://arvimal.wordpress"
},
{
"path": "13-decorators/19-decorators-1.py",
"chars": 1887,
"preview": "#!/usr/bin/env python\n\n# 19-decorators-1.py\n# Decorators, as simple as it gets :)\n\n# Reference: Decorators 101 - A Gentl"
},
{
"path": "13-decorators/20-decorators-2.py",
"chars": 819,
"preview": "#!/usr/bin/env python\n\n# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.\n# By Jillian Munso"
},
{
"path": "13-decorators/21-decorators-3.py",
"chars": 1030,
"preview": "#!/usr/bin/env python\n\n# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.\n# By Jillian Munso"
},
{
"path": "13-decorators/22-decorators-4.py",
"chars": 881,
"preview": "#!/usr/bin/env python\n\n# Reference: Decorators 101 - A Gentle Introduction to Functional Programming.\n# By Jillian Munso"
},
{
"path": "13-decorators/23-decorators-5.py",
"chars": 493,
"preview": "#!/usr/bin/env python\n\n# 23-decorators-5.py\n\n# Reference : https://www.youtube.com/watch?v=bxhuLgybIro\n\nfrom __future__ "
},
{
"path": "13-decorators/24-decorators-6.py",
"chars": 495,
"preview": "#!/usr/bin/env python\n\n\ndef decorator(inner):\n def inner_decorator(*args, **kwargs):\n print(\"This function tak"
},
{
"path": "13-decorators/25-decorators-7.py",
"chars": 596,
"preview": "#!/usr/bin/env python\n\n# 25-decorators-7.py\n\n# Reference https://www.youtube.com/watch?v=Slf1b3yUocc\n\n# We have two func"
},
{
"path": "13-decorators/26-class-decorators.py",
"chars": 1010,
"preview": "#!/usr/bin/env python\n\n# 26-class-decorators.py\n\n# Reference : https://www.youtube.com/watch?v=Slf1b3yUocc\n# Talk by Mik"
},
{
"path": "14-magic_methods/31-magicmethods-1.py",
"chars": 444,
"preview": "#!/usr/bin/env python\n\n# 30-magicmethods-1.py\n\n# In the backend, python is mostly objects and method\n# calls on objects."
},
{
"path": "14-magic_methods/32-magicmethods-2.py",
"chars": 433,
"preview": "#!/usr/bin/env python\n\n# 31-magicmethods-2.py\n\n# In the backend, python is mostly objects and method\n# calls on objects."
},
{
"path": "15-abstract_base_classes/34-abstractclasses-1.py",
"chars": 1521,
"preview": "#!/usr/bin/env python\n\n# 34-abstractclasses-1.py\n\n# This code snippet talks about Abstract Base Classes (abc).\n\n# The `a"
},
{
"path": "15-abstract_base_classes/35-abstractclasses-2.py",
"chars": 1510,
"preview": "#!/usr/bin/env python\n\n# 34-abstractclasses-1.py\n\n# This code snippet talks about Abstract Base Classes (abc).\n\n# The `a"
},
{
"path": "15-abstract_base_classes/36-abstractclasses-3.py",
"chars": 1515,
"preview": "#!/usr/bin/env python\n\n# 34-abstractclasses-1.py\n\n# This code snippet talks about Abstract Base Classes (abc).\n\n# The `a"
},
{
"path": "16-method_overloading/37-method-overloading-1.py",
"chars": 1445,
"preview": "#!/usr/bin/env python\n\n# 37-method-overloading-1.py\n\n# Reference: O'Reilly Learning Path:\n# http://shop.oreilly.com/prod"
},
{
"path": "16-method_overloading/38-method-overloading-2.py",
"chars": 944,
"preview": "#!/usr/bin/env python\n\n# 37-method-overloading-1.py\n\n# Reference: O'Reilly Learning Path:\n# http://shop.oreilly.com/prod"
},
{
"path": "16-method_overloading/39-method-overloading-3.py",
"chars": 841,
"preview": "#!/usr/bin/env python\n\n# 39-method-overloading-3.py\n\n# We've seen that inherited methods can be overloaded.\n# This is po"
},
{
"path": "17-super/40-super-1.py",
"chars": 626,
"preview": "#!/usr/bin/env python\n\n# 40-super-1.py\n\n# This is an example on how super() works\n# in Inheritance.\n\n# For more step-by-"
},
{
"path": "17-super/41-super-2.py",
"chars": 907,
"preview": "#!/usr/bin/env python\n\n# 41-super-2.py\n\n# For more information on how this works, refer:\n\n# https://arvimal.wordpress.co"
},
{
"path": "17-super/42-super-3.py",
"chars": 534,
"preview": "#!/usr/bin/env python\n\n# 42-super-3.py\n\n# super() and __init__()\n\n# Refer\n# https://arvimal.wordpress.com/2016/07/01/inh"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 5202,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
},
{
"path": "LICENSE",
"chars": 1066,
"preview": "MIT License\n\nCopyright (c) 2021 Vimal A R\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
},
{
"path": "README.md",
"chars": 53711,
"preview": "# Object Oriented Programming in Python\n\nA deep-dive study guide covering core OOP concepts in Python, with annotated co"
},
{
"path": "SECURITY.md",
"chars": 619,
"preview": "# Security Policy\n\n## Supported Versions\n\nUse this section to tell people about which versions of your project are\ncurre"
}
]
About this extraction
This page contains the full source code of the arvimal/oop_with_python GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 53 files (102.6 KB), approximately 28.5k tokens, and a symbol index with 169 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.