├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── codeql-analysis.yml ├── 01-encapsulation ├── 01-encapsulation-1.py ├── 02-encapsulation-2.py └── 03-encapsulation-3.py ├── 02-init_constructor ├── 04-init_constructor-1.py └── 05-init_constructor-2.py ├── 03-class_attributes ├── 06-class-attributes-1.py ├── 07-class-attributes-2.py └── 08-class-instance-attributes-1.py ├── 04-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 ├── 05-polymorphism ├── 11-polymorphism-1.py └── 12-polymorphism-2.py ├── 06-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 ├── 07-static_class_instance_methods ├── 17-instance_methods-1.py ├── 18-instance_methods-2.py ├── 27-classmethod-1.py ├── 28-classmethod-2.py ├── 29-staticmethod-1.py ├── 30-staticmethod-2.py ├── 31-magicmethods-1.py └── 32-magicmethods-2.py ├── 08-abstract_classes ├── 34-abstractclasses-1.py ├── 35-abstractclasses-2.py └── 36-abstractclasses-3.py ├── 09-method_overloading ├── 37-method-overloading-1.py ├── 38-method-overloading-2.py └── 39-method-overloading-3.py ├── 10-super ├── 40-super-1.py ├── 41-super-2.py └── 42-super-3.py ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md └── SECURITY.md /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '27 12 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # 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 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /01-encapsulation/01-encapsulation-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # encapsulation-1.py 4 | 5 | # Encapsulation means to preserve data in classes using methods 6 | # Here, we're setting the 'val' attribute through 'set_val()'. 7 | # See the next example, `encapsulation-2.py` for more info 8 | 9 | # In this example, we have two methods, `set_val` and `get_val`. 10 | # The first one sets the `val` value while the second one 11 | # prints/returns the value. 12 | 13 | 14 | class MyClass(object): 15 | def set_val(self, val): 16 | self.value = val 17 | 18 | def get_val(self): 19 | # print(self.value) 20 | return self.value 21 | 22 | 23 | a = MyClass() 24 | b = MyClass() 25 | 26 | a.set_val(10) 27 | b.set_val(100) 28 | 29 | a.get_val() 30 | b.get_val() 31 | 32 | # NOTE: If you run this code, it won't print anything to the screen. 33 | # This is because, even if we're calling `a.get_val()` and `b.get_val()`, 34 | # the `get_val()` function doesn't contain a `print()` function. 35 | # If we want to get the output printed to screen, we should do any of 36 | # the following: 37 | 38 | # a) Either replace `return self.value` with `print(self.value)` 39 | # or add a print statement **above** `return` as `print(self.value)`. 40 | 41 | # b) Remove `return(self.value)` and replace it with `print(self.value)` 42 | -------------------------------------------------------------------------------- /01-encapsulation/02-encapsulation-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # encapsulation-2.py 4 | 5 | # This example builds on top of `encapsulation-1.py`. 6 | # Here we see how we can set values in methods without 7 | # going through the method itself, ie.. how we can break 8 | # encapsulation. 9 | 10 | # NOTE: BREAKING ENCAPSULATION IS BAD. 11 | 12 | 13 | class MyClass(object): 14 | def set_val(self, val): 15 | self.value = val 16 | 17 | def get_val(self): 18 | print(self.value) 19 | 20 | 21 | a = MyClass() 22 | b = MyClass() 23 | 24 | a.set_val(10) 25 | b.set_val(1000) 26 | a.value = 100 # <== Overriding `set_value` directly 27 | # <== ie.. Breaking encapsulation 28 | 29 | a.get_val() 30 | b.get_val() 31 | -------------------------------------------------------------------------------- /01-encapsulation/03-encapsulation-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 03-encapsulation-3.py 4 | 5 | # Here we look at another example, where we have three methods 6 | # set_val(), get_val(), and increment_val(). 7 | 8 | # set_val() helps to set a value, get_val() prints the value, 9 | # and increment_val() increments the set value by 1. 10 | 11 | # We can still break encapsulation here by calling 'self.value' 12 | # directly in an instance, which is **BAD**. 13 | 14 | # set_val() forces us to input an integer, ie.. what the code wants 15 | # to work properly. Here, it's possible to break the encapsulation by 16 | # calling 'self.val` directly, which will cause unexpected results later. 17 | # In this example, the code is written to enforce an intger as input, if we 18 | # don't break encapsulation and go through the gateway 'set_val()' 19 | 20 | # 21 | 22 | 23 | class MyInteger(object): 24 | def set_val(self, val): 25 | try: 26 | val = int(val) 27 | except ValueError: 28 | return 29 | self.val = val 30 | 31 | def get_val(self): 32 | print(self.val) 33 | 34 | def increment_val(self): 35 | self.val = self.val + 1 36 | print(self.val) 37 | 38 | 39 | a = MyInteger() 40 | a.set_val(10) 41 | a.get_val() 42 | a.increment_val() 43 | print("\n") 44 | 45 | # Trying to break encapsulation in a new instance with an int 46 | c = MyInteger() 47 | c.val = 15 48 | c.get_val() 49 | c.increment_val() 50 | print("\n") 51 | 52 | # Trying to break encapsulation in a new instance with a str 53 | b = MyInteger() 54 | b.val = "MyString" # <== Breaking encapsulation, works fine 55 | b.get_val() # <== Prints the val set by breaking encap 56 | b.increment_val() # This will fail, since str + int wont work 57 | print("\n") 58 | -------------------------------------------------------------------------------- /02-init_constructor/04-init_constructor-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 04-init_constructor.py 4 | 5 | # __init__() is a constructor method which helps to 6 | # set initial values while instatiating a class. 7 | 8 | # __init__() will get called with the attributes set in __init__(), 9 | # when a class is instantiated. 10 | 11 | # The '__' before and after the method name denotes that 12 | # the method is private. It's called private or magic methods 13 | # since it's called internally and automatically. 14 | 15 | 16 | class MyNum(object): 17 | def __init__(self): 18 | print("Calling the __init__() constructor!\n") 19 | self.val = 0 20 | 21 | def increment(self): 22 | self.val = self.val + 1 23 | print(self.val) 24 | 25 | 26 | dd = MyNum() 27 | dd.increment() # will print 1 28 | dd.increment() # will print 2 29 | -------------------------------------------------------------------------------- /02-init_constructor/05-init_constructor-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 05-init_constructor-2.py 4 | 5 | # We add a test in the __init__() constructor to check 6 | # if 'value' is an int or not. 7 | 8 | 9 | class MyNum(object): 10 | def __init__(self, value): 11 | try: 12 | value = int(value) 13 | except ValueError: 14 | value = 0 15 | self.value = value 16 | 17 | def increment(self): 18 | self.value = self.value + 1 19 | print(self.value) 20 | 21 | 22 | a = MyNum(10) 23 | a.increment() # This should print 11 24 | a.increment() # This should print 12 25 | -------------------------------------------------------------------------------- /03-class_attributes/06-class-attributes-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2.7 2 | 3 | # 06-class-attributes-1.py 4 | 5 | # Here we define an attribute under the class `YourClass` 6 | # as well as an attribute within the function. 7 | 8 | # The attribute defined in the class is called `class attributes` 9 | # and the attribute defined in the function is called `instance attributes`. 10 | 11 | 12 | class YourClass(object): 13 | classy = 10 14 | 15 | def set_val(self): 16 | self.insty = 100 17 | 18 | 19 | dd = YourClass() 20 | dd.classy # This will fetch the class attribute 10. 21 | dd.set_val() 22 | dd.insty # This will fetch the instance attribute 100. 23 | 24 | # Once `dd` is instantiated, we can access both the class and instance 25 | # attributes, ie.. dd.classy and dd.insty. 26 | -------------------------------------------------------------------------------- /03-class_attributes/07-class-attributes-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 07-class-attributes-2.py 4 | 5 | # The code below shows two important points: 6 | 7 | # a) A class attribute can be overridden in an instance, even 8 | # though it is bad due to breaking Encapsulation. 9 | 10 | # b) There is a lookup path for attributes in Python. The first being 11 | # the method defined within the class, and then the class above it. 12 | 13 | # We are overriding the 'classy' class attribute in the instance 'dd'. 14 | # When it's overridden, the python interpreter reads the overridden value. 15 | # But once the new value is deleted with 'del', the overridden value is no longer 16 | # present in the instance, and hence the lookup goes a level above and gets it from 17 | # the class. 18 | 19 | 20 | class YourClass(object): 21 | classy = "class value" 22 | 23 | 24 | dd = YourClass() 25 | print(dd.classy) # < This should return the string "class value" 26 | 27 | dd.classy = "Instance value" 28 | print(dd.classy) # This should return the string "Instance value" 29 | 30 | # This will delete the value set for 'dd.classy' in the instance. 31 | del dd.classy 32 | # Since the overriding attribute was deleted, this will print 'class value'. 33 | print(dd.classy) 34 | -------------------------------------------------------------------------------- /03-class_attributes/08-class-instance-attributes-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 08-class-instance-attributes-1.py 4 | 5 | # This code shows that an Instance can access it's own 6 | # attributes as well as Class attributes. 7 | 8 | # We have a class attribute named 'count', and we add 1 to 9 | # it each time we create an instance. This can help count the 10 | # number of instances at the time of instantiation. 11 | 12 | 13 | class InstanceCounter(object): 14 | count = 0 15 | 16 | def __init__(self, val): 17 | self.val = val 18 | InstanceCounter.count += 1 19 | 20 | def set_val(self, newval): 21 | self.val = newval 22 | 23 | def get_val(self): 24 | print(self.val) 25 | 26 | def get_count(self): 27 | print(InstanceCounter.count) 28 | 29 | 30 | a = InstanceCounter(5) 31 | b = InstanceCounter(10) 32 | c = InstanceCounter(15) 33 | 34 | for obj in (a, b, c): 35 | print("value of obj: %s" % obj.get_val()) 36 | print("Count : %s" % obj.get_count()) 37 | -------------------------------------------------------------------------------- /04-inheritance/09-inheritance-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 09-inheritance-1.py 4 | 5 | # The code below shows how a class can inherit from another class. 6 | # We have two classes, `Date` and `Time`. Here `Time` inherits from 7 | # `Date`. 8 | 9 | # Any class inheriting from another class (also called a Parent class) 10 | # inherits the methods and attributes from the Parent class. 11 | 12 | # Hence, any instances created from the class `Time` can access 13 | # the methods defined in the parent class `Date`. 14 | 15 | 16 | class Date(object): 17 | def get_date(self): 18 | print("2016-05-14") 19 | 20 | 21 | class Time(Date): 22 | def get_time(self): 23 | print("07:00:00") 24 | 25 | 26 | # Creating an instance from `Date` 27 | dt = Date() 28 | dt.get_date() # Accesing the `get_date()` method of `Date` 29 | print("--------") 30 | 31 | # Creating an instance from `Time`. 32 | tm = Time() 33 | tm.get_time() # Accessing the `get_time()` method from `Time`. 34 | # Accessing the `get_date() which is defined in the parent class `Date`. 35 | tm.get_date() 36 | -------------------------------------------------------------------------------- /04-inheritance/10-inheritance-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 10-inheritance-2.py 4 | 5 | # The code below shows another example of inheritance 6 | # Dog and Cat are two classes which inherits from Animal. 7 | # This an instance created from Dog or Cat can access the methods 8 | # in the Animal class, ie.. eat(). 9 | 10 | # The instance of 'Dog' can access the methods of the Dog class 11 | # and it's parent class 'Animal'. 12 | 13 | # The instance of 'Cat' can access the methods of the Cat class 14 | # and it's parent class 'Animal'. 15 | 16 | # But the instance created from 'Cat' cannot access the attributes 17 | # within the 'Dog' class, and vice versa. 18 | 19 | 20 | class Animal(object): 21 | def __init__(self, name): 22 | self.name = name 23 | 24 | def eat(self, food): 25 | print("%s is eating %s" % (self.name, food)) 26 | 27 | 28 | class Dog(Animal): 29 | def fetch(self, thing): 30 | print("%s goes after the %s" % (self.name, thing)) 31 | 32 | 33 | class Cat(Animal): 34 | def swatstring(self): 35 | print("%s shred the string!" % self.name) 36 | 37 | 38 | d = Dog("Roger") 39 | c = Cat("Fluffy") 40 | 41 | d.fetch("paper") 42 | d.eat("dog food") 43 | print("--------") 44 | c.eat("cat food") 45 | c.swatstring() 46 | 47 | # The below methods would fail, since the instances doesn't have 48 | # have access to the other class. 49 | 50 | c.fetch("frizbee") 51 | d.swatstring() 52 | -------------------------------------------------------------------------------- /04-inheritance/13-inheriting-init-constructor-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 13-inheriting-init-constructor-1.py 4 | 5 | # This is a normal inheritance example from which we build 6 | # the next example. Make sure to read and understand the 7 | # next example '14-inheriting-init-constructor-2.py'. 8 | 9 | 10 | class Animal(object): 11 | def __init__(self, name): 12 | self.name = name 13 | 14 | 15 | class Dog(Animal): 16 | def fetch(self, thing): 17 | print("%s goes after the %s" % (self.name, thing)) 18 | 19 | 20 | d = Dog("Roger") 21 | print("The dog's name is", d.name) 22 | d.fetch("frizbee") 23 | -------------------------------------------------------------------------------- /04-inheritance/14-multiple-inheritance-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 14-multiple-inheritance-1.py 4 | 5 | # Python supports multiple inheritance and uses a depth-first order 6 | # when searching for methods. 7 | # This search pattern is call MRO (Method Resolution Order) 8 | 9 | # This is the first example, which shows the lookup of a common 10 | # function named 'dothis()', which we'll continue in other examples. 11 | 12 | # As per the MRO output, it starts in class D, then B, A, and lastly C. 13 | 14 | # Both A and C contains 'dothis()'. Let's trace how the lookup happens. 15 | 16 | # As per the MRO output, it starts in class D, then B, A, and lastly C. 17 | 18 | # class `A` defines 'dothis()' and the search ends there. It doesn't go to C. 19 | 20 | # The MRO will show the full resolution path even if the full path is 21 | # not traversed. 22 | 23 | # The method lookup flow in this case is : D -> B -> A -> C 24 | 25 | 26 | class A(object): 27 | def dothis(self): 28 | print("doing this in A") 29 | 30 | 31 | class B(A): 32 | pass 33 | 34 | 35 | class C(object): 36 | def dothis(self): 37 | print("doing this in C") 38 | 39 | 40 | class D(B, C): 41 | pass 42 | 43 | 44 | d_instance = D() 45 | d_instance.dothis() # <== This should print from class A. 46 | 47 | print("\nPrint the Method Resolution Order") 48 | print(D.mro()) 49 | -------------------------------------------------------------------------------- /04-inheritance/15-multiple-inheritance-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 15-multiple-inheritance-2.py 4 | 5 | # Python supports multiple inheritance 6 | 7 | # It uses a depth-first order when searching for methods. 8 | # This search pattern is call MRO (Method Resolution Order) 9 | 10 | # This is a second example, which shows the lookup of 'dothis()'. 11 | # Both A and C contains 'dothis()'. Let's trace how the lookup happens. 12 | 13 | # As per the MRO output using depth-first search, 14 | # it starts in class D, then B, A, and lastly C. 15 | 16 | # Here we're looking for 'dothis()' which is defined in class `C`. 17 | # The lookup goes from D -> B -> A -> C. 18 | 19 | # Since class `A` doesn't have `dothis()`, the lookup goes back to class `C` 20 | # and finds it there. 21 | 22 | 23 | class A(object): 24 | def dothat(self): 25 | print("Doing this in A") 26 | 27 | 28 | class B(A): 29 | pass 30 | 31 | 32 | class C(object): 33 | def dothis(self): 34 | print("\nDoing this in C") 35 | 36 | 37 | class D(B, C): 38 | """Multiple Inheritance, 39 | D inheriting from both B and C""" 40 | 41 | pass 42 | 43 | 44 | d_instance = D() 45 | 46 | d_instance.dothis() 47 | 48 | print("\nPrint the Method Resolution Order") 49 | print(D.mro()) 50 | -------------------------------------------------------------------------------- /04-inheritance/16-multiple-inheritance-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 16-multiple-inheritance-3.py 4 | 5 | # Python supports multiple inheritance 6 | # and uses a depth-first order when searching for methods. 7 | # This search pattern is call MRO (Method Resolution Order) 8 | 9 | # Example for "Diamond Shape" inheritance 10 | # Lookup can get complicated when multiple classes inherit 11 | # from multiple parent classes. 12 | 13 | # In order to avoid ambiguity while doing a lookup for a method 14 | # in various classes, from Python 2.3, the MRO lookup order has an 15 | # additional feature. 16 | 17 | # It still does a depth-first lookup, but if the occurrence of a class 18 | # happens multiple times in the MRO path, it removes the initial occurrence 19 | # and keeps the latter. 20 | 21 | # In the example below, class `D` inherits from `B` and `C`. 22 | # And both `B` and `C` inherits from `A`. 23 | # Both `A` and `C` has the method `dothis()`. 24 | 25 | # We instantiate `D` and requests the 'dothis()' method. 26 | # By default, the lookup should go D -> B -> A -> C -> A. 27 | # But from Python 2.3, in order to reduce the lookup time, 28 | # the MRO skips the classes which occur multiple times in the path. 29 | 30 | # Hence the lookup will be D -> B -> C -> A. 31 | 32 | 33 | class A(object): 34 | def dothis(self): 35 | print("doing this in A") 36 | 37 | 38 | class B(A): 39 | pass 40 | 41 | 42 | class C(A): 43 | def dothis(self): 44 | print("doing this in C") 45 | 46 | 47 | class D(B, C): 48 | pass 49 | 50 | 51 | d_instance = D() 52 | d_instance.dothis() 53 | 54 | print("\nPrint the Method Resolution Order") 55 | print(D.mro()) 56 | -------------------------------------------------------------------------------- /05-polymorphism/11-polymorphism-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 11-polymorphism-1.py 4 | 5 | # Polymorphism means having the same interface/attributes in different 6 | # classes. 7 | 8 | # Polymorphism is the characteristic of being able to assign 9 | # a different meaning or usage in different contexts. 10 | # A not-so-clear/clean example is, different classes can have 11 | # the same function name. 12 | 13 | # Here, the class Dog and Cat has the same method named 'show_affection' 14 | # Even if they are same, both does different actions in the instance. 15 | # 16 | # Since the order of the lookup is 17 | # 'instance' -> 'class' -> 'parent class', even if the 18 | # 'class' and 'parent class' has functions with the same name, 19 | # the instance will only pick up the first hit, 20 | # ie.. from the 'class' and won't go to the parent class. 21 | 22 | 23 | class Animal(object): 24 | def __init__(self, name): 25 | self.name = name 26 | 27 | def eat(self, food): 28 | print("{0} eats {1}".format(self.name, food)) 29 | 30 | 31 | class Dog(Animal): 32 | def fetch(self, thing): 33 | print("{0} goes after the {1}!".format(self.name, thing)) 34 | 35 | def show_affection(self): 36 | print("{0} wags tail".format(self.name)) 37 | 38 | 39 | class Cat(Animal): 40 | def swatstring(self): 41 | print("{0} shreds more string".format(self.name)) 42 | 43 | def show_affection(self): 44 | print("{0} purrs".format(self.name)) 45 | 46 | 47 | for a in (Dog("Rover"), Cat("Fluffy"), Cat("Lucky"), Dog("Scout")): 48 | a.show_affection() 49 | -------------------------------------------------------------------------------- /05-polymorphism/12-polymorphism-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 12-polymorphism-2.py 4 | 5 | # Another example for Polymorphism are the several inbuilt 6 | # functions in Python. Take for example, the builtin function 7 | # called 'len'. 8 | 9 | # 'len' is available for almost all types, such as strings, 10 | # ints, floats, dictionaries, lists, tuples etc.. 11 | # When len is called on a type, it actually calls the inbuilts 12 | # private function 'len' on that type or __len__ 13 | 14 | # Every object type that supports 'len' will have a private 15 | # 'len' function inbuilt. 16 | 17 | # Hence, for example, a list type already has a 'len()' 18 | # function inbuilt in the Python code, and when you run the 19 | # len() function on the data type, it checks if the len 20 | # private function is available for that type or not. 21 | # If it is available, it runs that. 22 | 23 | text = ["Hello", "Hola", "helo"] 24 | print(len(text)) 25 | 26 | print(len("Hello")) 27 | print(len({"a": 1, "b": 2, "c": 3})) 28 | -------------------------------------------------------------------------------- /06-decorators/19-decorators-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 19-decorators-1.py 4 | # Decorators, as simple as it gets :) 5 | 6 | # Reference: Decorators 101 - A Gentle Introduction to Functional Programming. 7 | # By Jillian Munson - PyGotham 2014. 8 | # https://www.youtube.com/watch?v=yW0cK3IxlHc 9 | 10 | # Decorators are functions that compliment other functions, 11 | # or in other words, modify a function or method. 12 | 13 | # In the example below, we have a function named `decorated`. 14 | # This function just prints "This happened". 15 | # We have a decorator created named `inner_decorator()`. 16 | # This decorator function has an function within, which 17 | # does some operations (print stuff for simplicity) and then 18 | # returns the return-value of the internal function. 19 | 20 | # How does it work? 21 | # a) The function `decorated()` gets called. 22 | # b) Since the decorator `@my_decorator` is defined above 23 | # `decorated()`, `my_decorator()` gets called. 24 | # c) my_decorator() takes a function name as args, and hence `decorated()` 25 | # gets passed as the arg. 26 | # d) `my_decorator()` does it's job, and when it reaches `myfunction()` 27 | # calls the actual function, ie.. decorated() 28 | # e) Once the function `decorated()` is done, it gets back to `my_decorator()`. 29 | # f) Hence, using a decorator can drastically change the behavior of the 30 | # function you're actually executing. 31 | 32 | 33 | def my_decorator(my_function): # <-- (4) 34 | def inner_decorator(): # <-- (5) 35 | print("This happened before!") # <-- (6) 36 | my_function() # <-- (7) 37 | print("This happens after ") # <-- (10) 38 | print("This happened at the end!") # <-- (11) 39 | 40 | return inner_decorator 41 | # return None 42 | 43 | 44 | @my_decorator # <-- (3) 45 | def my_decorated(): # <-- (2) <-- (8) 46 | print("This happened!") # <-- (9) 47 | 48 | 49 | if __name__ == "__main__": 50 | my_decorated() # <-- (1) 51 | 52 | # This prints: 53 | # # python 19-decorators-1.py 54 | # This happened before! 55 | # This happened! 56 | # This happens after 57 | # This happened at the end! 58 | -------------------------------------------------------------------------------- /06-decorators/20-decorators-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Reference: Decorators 101 - A Gentle Introduction to Functional Programming. 4 | # By Jillian Munson - PyGotham 2014. 5 | # https://www.youtube.com/watch?v=yW0cK3IxlHc 6 | 7 | # 20-decorators-2.py 8 | # An updated version of 19-decorators-1.py 9 | 10 | # This code snippet takes the previous example, and add a bit more information 11 | # to the output. 12 | 13 | import datetime 14 | 15 | 16 | def my_decorator(inner): 17 | def inner_decorator(): 18 | print(datetime.datetime.utcnow()) 19 | inner() 20 | print(datetime.datetime.utcnow()) 21 | 22 | return inner_decorator 23 | 24 | 25 | @my_decorator 26 | def decorated(): 27 | print("This happened!") 28 | 29 | 30 | if __name__ == "__main__": 31 | decorated() 32 | 33 | # This will print: (NOTE: The time will change of course :P) 34 | # # python 20-decorators-2.py 35 | # 2016-05-29 11:46:07.444330 36 | # This happened! 37 | # 2016-05-29 11:46:07.444367 38 | -------------------------------------------------------------------------------- /06-decorators/21-decorators-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Reference: Decorators 101 - A Gentle Introduction to Functional Programming. 4 | # By Jillian Munson - PyGotham 2014. 5 | # https://www.youtube.com/watch?v=yW0cK3IxlHc 6 | 7 | # This is an updated version of 20-decorators-2.py. 8 | # Here, the `decorated()` function takes an argument 9 | # and prints it back on terminal. 10 | 11 | # When the decorator `@my_decorator` is called, it 12 | # takes the function `decorated()` as its argument, and 13 | # the argument of `decorated()` as the argument of `inner_decorator()`. 14 | # Hence the arg `number` is passed to `num_copy`. 15 | 16 | import datetime 17 | 18 | 19 | def my_decorator(inner): 20 | def inner_decorator(num_copy): 21 | print(datetime.datetime.utcnow()) 22 | inner(int(num_copy) + 1) 23 | print(datetime.datetime.utcnow()) 24 | 25 | return inner_decorator 26 | 27 | 28 | @my_decorator 29 | def decorated(number): 30 | print("This happened : " + str(number)) 31 | 32 | 33 | if __name__ == "__main__": 34 | decorated(5) 35 | 36 | # This prints: 37 | # python 21-decorators-3.py 38 | # 2016-05-29 12:11:57.212125 39 | # This happened : 6 40 | # 2016-05-29 12:11:57.212168 41 | -------------------------------------------------------------------------------- /06-decorators/22-decorators-4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Reference: Decorators 101 - A Gentle Introduction to Functional Programming. 4 | # By Jillian Munson - PyGotham 2014. 5 | # https://www.youtube.com/watch?v=yW0cK3IxlHc 6 | 7 | # 22-decorators-4.py 8 | 9 | # This example builds on the previous decorator examples. 10 | # The previous example, 21-decorators-3.py showed how to 11 | # deal with one argument passed to the function. 12 | 13 | # This example shows how we can deal with multiple args. 14 | 15 | # Reminder : `args` is a list of arguments passed, while 16 | # kwargs is a dictionary passed as arguments. 17 | 18 | 19 | def decorator(inner): 20 | def inner_decorator(*args, **kwargs): 21 | print(args, kwargs) 22 | 23 | return inner_decorator 24 | 25 | 26 | @decorator 27 | def decorated(string_args): 28 | print("This happened : " + string_args) 29 | 30 | 31 | if __name__ == "__main__": 32 | decorated("Hello, how are you?") 33 | 34 | # This prints : 35 | # # python 22-decorators-4.py 36 | # ('Hello, how are you?',) 37 | -------------------------------------------------------------------------------- /06-decorators/23-decorators-5.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 23-decorators-5.py 4 | 5 | # Reference : https://www.youtube.com/watch?v=bxhuLgybIro 6 | 7 | from __future__ import print_function 8 | 9 | 10 | # 2. Decorator function 11 | def handle_exceptions(func_name): 12 | def inner(*args, **kwargs): 13 | try: 14 | return func_name(*args, **kwargs) 15 | except Exception: 16 | print("An exception was thrown : ", Exception) 17 | 18 | return inner 19 | 20 | 21 | # 1. Main function 22 | @handle_exceptions 23 | def divide(x, y): 24 | return x / y 25 | 26 | 27 | print(divide(8, 0)) 28 | -------------------------------------------------------------------------------- /06-decorators/24-decorators-6.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | def decorator(inner): 5 | def inner_decorator(*args, **kwargs): 6 | print("This function takes " + str(len(args)) + " arguments") 7 | inner(*args) 8 | 9 | return inner_decorator 10 | 11 | 12 | @decorator 13 | def decorated(string_args): 14 | print("This happened: " + str(string_args)) 15 | 16 | 17 | @decorator 18 | def alsoDecorated(num1, num2): 19 | print("Sum of " + str(num1) + "and" + str(num2) + ": " + str(num1 + num2)) 20 | 21 | 22 | if __name__ == "__main__": 23 | decorated("Hello") 24 | alsoDecorated(1, 2) 25 | -------------------------------------------------------------------------------- /06-decorators/25-decorators-7.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 25-decorators-7.py 4 | 5 | # Reference https://www.youtube.com/watch?v=Slf1b3yUocc 6 | 7 | # We have two functions, one which adds two numbers, 8 | # and another which subtracts two numbers. 9 | 10 | # We apply the decorator @double which takes in the 11 | # functions that is called with the decorator, and doubles 12 | # the output of the respective function. 13 | 14 | 15 | def double(my_func): 16 | def inner_func(a, b): 17 | return 2 * my_func(a, b) 18 | 19 | return inner_func 20 | 21 | 22 | @double 23 | def adder(a, b): 24 | return a + b 25 | 26 | 27 | @double 28 | def subtractor(a, b): 29 | return a - b 30 | 31 | 32 | print(adder(10, 20)) 33 | print(subtractor(6, 1)) 34 | -------------------------------------------------------------------------------- /06-decorators/26-class-decorators.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 26-class-decorators.py 4 | 5 | # Reference : https://www.youtube.com/watch?v=Slf1b3yUocc 6 | # Talk by Mike Burns 7 | 8 | # Till the previous examples, we saw function decorators. 9 | # But decorators can be applied to Classes as well. 10 | # This example deals with class decorators. 11 | 12 | # NOTE: If you are creating a decorator for a class, you'll it 13 | # to return a Class. 14 | 15 | # NOTE: Similarly, if you are creating a decorator for a function, 16 | # you'll need it to return a function. 17 | 18 | 19 | def honirific(cls): 20 | class HonirificCls(cls): 21 | def full_name(self): 22 | return "Dr. " + super(HonirificCls, self).full_name() 23 | 24 | return HonirificCls 25 | 26 | 27 | @honirific 28 | class Name(object): 29 | def __init__(self, first_name, last_name): 30 | self.first_name = first_name 31 | self.last_name = last_name 32 | 33 | def full_name(self): 34 | return " ".join([self.first_name, self.last_name]) 35 | 36 | 37 | result = Name("Vimal", "A.R").full_name() 38 | print("Full name: {0}".format(result)) 39 | 40 | 41 | # This needs further check. Erroring out. 42 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/17-instance_methods-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 17-instance_methods-1.py 4 | 5 | # Instance methods are also known as Bound methods since the methods 6 | # within a class are bound to the instance created from the class, via 7 | # 'self'. 8 | 9 | 10 | class A(object): 11 | def method(*argv): 12 | return argv 13 | 14 | 15 | a = A() 16 | print(a.method) 17 | 18 | # The print() function will print the following : 19 | # python 17-instance_methods-1.py 20 | # > 21 | 22 | # The output shows that 'method' is a bound method 23 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/18-instance_methods-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 18-instance_methods.py 4 | 5 | # Instance methods are the normal way of accessing methods, seen in all 6 | # classes till now. ie.. by instantiating instances from a class, and 7 | # access the methods within the class. The usage of `self` is very 8 | # important in instance methods due to `self` being a hook/handle to the 9 | # instance itself, or the instance itself. 10 | 11 | # We look into a previous example, once more, to understand `Instance methods`. 12 | 13 | # We have an __init__() constructor, and three methods within the 14 | # `InstanceCounter` class. 15 | 16 | # Three instances a, b, and c are created from the class `InstanceCounter`. 17 | 18 | # Since the methods defined in the class are accessed through the 19 | # instances 'a', 'b', and 'c', these methods are called 'Instance 20 | # methods'. 21 | 22 | # Since the instance is bound to the methods defined in the class by the 23 | # keyword `self`, we also call `Instance methods` as 'Bound methods'. 24 | 25 | # In the code below, the instance is `obj` (the iterator) and we access 26 | # each method as `obj.set_val()`, `obj.get_val()`, and `obj.get_count`. 27 | 28 | 29 | class InstanceCounter(object): 30 | count = 0 31 | 32 | def __init__(self, val): 33 | self.val = val 34 | InstanceCounter.count += 1 35 | 36 | def set_val(self, newval): 37 | self.val = newval 38 | 39 | def get_val(self): 40 | return self.val 41 | 42 | def get_count(self): 43 | return InstanceCounter.count 44 | 45 | 46 | a = InstanceCounter(5) 47 | b = InstanceCounter(10) 48 | c = InstanceCounter(15) 49 | 50 | for obj in (a, b, c): 51 | print("Value of object: %s" % (obj.get_val)) 52 | print("Count : %s " % (obj.get_count)) 53 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/27-classmethod-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 19-class_methods-1.py 4 | 5 | # A classmethod is an inbuilt decorator which is called on functions via 6 | # @classmethod. 7 | 8 | # The @classmethod decorator marks the function/method as bound to the 9 | # class and not to an instance. 10 | 11 | # Remember that we used 'self' in a function within a class, which denoted 12 | # the instance. In class methods, we use `cls` which denotes the class 13 | # rather than the instance. 14 | 15 | # The following example is a very simple explanation of class-methods. 16 | 17 | # class_1() is a class method while class_2() is an instance method. 18 | 19 | # Class methods can be accessed by the class as well as the instance. 20 | 21 | # Instance methods can only be accessed by the Instance. That's why in this example, MyClass.class_2() will fail with an error. 22 | 23 | # NOTE : For the class MyClass: 24 | # MyClass is the class itself 25 | # MyClass() is an instance 26 | 27 | 28 | class MyClass(object): 29 | @classmethod 30 | def class_1(cls): 31 | print("Class method 1") 32 | 33 | def class_2(self): 34 | print("Self/Instance method 1") 35 | 36 | 37 | print("Calling the class `MyClass` directly without an instance:") 38 | MyClass.class_1() 39 | # MyClass.class_2() 40 | 41 | # NOTE: You will want to comment `MyClass.class_2()` once you hit the `TypeError` 42 | # to continue with the examples below. 43 | 44 | print("\nCalling the instance `MyClass()`:") 45 | MyClass().class_1() 46 | MyClass().class_2() 47 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/28-classmethod-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 28-classmethod-2.py 4 | 5 | # Reference: https://jeffknupp.com/blog/2014/06/18/improve-your-python-python-classes-and-object-oriented-programming/ 6 | 7 | # Classmethods are decorators which are inbuilt in Python. 8 | # We decorate a function as a classemethod using the decorator 9 | # @classmethod. 10 | 11 | # Class methods are used for functions which need not be 12 | # called via an instance. Certain use cases may be: 13 | 14 | # a) Creating instances take resources, hence the methods/functions 15 | # which need necessarily 16 | 17 | 18 | class MyClass(object): 19 | count = 0 20 | 21 | def __init__(self, val): 22 | self.val = val 23 | MyClass.count += 1 24 | 25 | def set_val(self, newval): 26 | self.val = newval 27 | 28 | def get_val(self): 29 | return self.val 30 | 31 | @classmethod 32 | def get_count(cls): 33 | return cls.count 34 | 35 | 36 | object_1 = MyClass(10) 37 | print("\nValue of object : %s" % object_1.get_val()) 38 | print(MyClass.get_count()) 39 | 40 | object_2 = MyClass(20) 41 | print("\nValue of object : %s" % object_2.get_val()) 42 | print(MyClass.get_count()) 43 | 44 | object_3 = MyClass(40) 45 | print("\nValue of object : %s" % object_3.get_val()) 46 | print(MyClass.get_count()) 47 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/29-staticmethod-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # 29-staticmethod-1.py 4 | 5 | """ 6 | # Refer https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/ 7 | # a) Static methods are functions methods which doesn’t need a binding 8 | # to a class or an instance. 9 | # b) Static methods, as well as Class methods, don’t require an instance 10 | # to be called. 11 | # c) Static methods doesn’t need self or cls as the first argument 12 | # since it’s not bound to an instance or class. 13 | # d) Static methods are normal functions, but within a class. 14 | # e) Static methods are defined with the keyword @staticmethod 15 | # above the function/method. 16 | # f) Static methods are usually used to work on Class Attributes. 17 | """ 18 | 19 | 20 | class MyClass(object): 21 | count = 0 22 | 23 | def __init__(self, val): 24 | self.val = self.filterint(val) 25 | MyClass.count += 1 26 | 27 | @staticmethod 28 | def filterint(value): 29 | if not isinstance(value, int): 30 | print("Entered value is not an INT, value set to 0") 31 | return 0 32 | else: 33 | return value 34 | 35 | 36 | a = MyClass(5) 37 | b = MyClass(10) 38 | c = MyClass(15) 39 | 40 | print(a.val) 41 | print(b.val) 42 | print(c.val) 43 | print(a.filterint(100)) 44 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/30-staticmethod-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | # 30-staticmethod-2.py 5 | 6 | # Refer 7 | # https://arvimal.wordpress.com/2016/06/12/instance-class-static-methods-object-oriented-programming/ 8 | 9 | # Static methods are functions/methods which doesn’t need a binding to a class or an instance. 10 | # Static methods, as well as Class methods, don’t require an instance to be called. 11 | # Static methods doesn’t need self or cls as the first argument since it’s not bound to an instance or class. 12 | # Static methods are normal functions, but within a class. 13 | # Static methods are defined with the keyword @staticmethod above the function/method. 14 | # Static methods are usually used to work on Class Attributes. 15 | 16 | 17 | class MyClass(object): 18 | # A class attribute 19 | count = 0 20 | 21 | def __init__(self, name): 22 | print("An instance is created!") 23 | self.name = name 24 | MyClass.count += 1 25 | 26 | # Our class method 27 | @staticmethod 28 | def status(): 29 | print("The total number of instances are ", MyClass.count) 30 | 31 | 32 | print(MyClass.count) 33 | 34 | my_func_1 = MyClass("MyClass 1") 35 | my_func_2 = MyClass("MyClass 2") 36 | my_func_3 = MyClass("MyClass 3") 37 | 38 | MyClass.status() 39 | print(MyClass.count) 40 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/31-magicmethods-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 30-magicmethods-1.py 4 | 5 | # In the backend, python is mostly objects and method 6 | # calls on objects. 7 | 8 | # Here we see an example, where the `print()` function 9 | # is just a call to the magic method `__repr__()`. 10 | 11 | 12 | class PrintList(object): 13 | def __init__(self, my_list): 14 | self.mylist = my_list 15 | 16 | def __repr__(self): 17 | return str(self.mylist) 18 | 19 | 20 | printlist = PrintList(["a", "b", "c"]) 21 | print(printlist.__repr__()) 22 | -------------------------------------------------------------------------------- /07-static_class_instance_methods/32-magicmethods-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 31-magicmethods-2.py 4 | 5 | # In the backend, python is mostly objects and method 6 | # calls on objects. 7 | 8 | # To read more on magic methods, refer : 9 | # http://www.rafekettler.com/magicmethods.html 10 | 11 | my_list_1 = ["a", "b", "c"] 12 | 13 | my_list_2 = ["d", "e", "f"] 14 | 15 | print("\nCalling the `+` builtin with both lists") 16 | print(my_list_1 + my_list_2) 17 | 18 | print("\nCalling `__add__()` with both lists") 19 | print(my_list_1.__add__(my_list_2)) 20 | -------------------------------------------------------------------------------- /08-abstract_classes/34-abstractclasses-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 34-abstractclasses-1.py 4 | 5 | # This code snippet talks about Abstract Base Classes (abc). 6 | 7 | # The `abc` module provides features to create 8 | # Abstract Base Classes. 9 | 10 | # To create an Abstract Base Class, set the `__metaclass__` magic method 11 | # to `abc.ABCMeta`. This will mark the respective class as an Abstract 12 | # Base Class. 13 | 14 | # Now, in order to specify the methods which are to be enforced on the 15 | # child classes, ie.. to create Abstract Methods, we use the decorator 16 | # @abc.abstractmethod on the methods we need. 17 | 18 | # The child class which inherits from an Abstract Base Class can implement 19 | # methods of their own, but *should always* implement the methods defined in 20 | # the parent ABC Class. 21 | 22 | import abc 23 | 24 | 25 | class My_ABC_Class(object): 26 | __metaclass__ = abc.ABCMeta 27 | 28 | @abc.abstractmethod 29 | def set_val(self, val): 30 | return 31 | 32 | @abc.abstractmethod 33 | def get_val(self): 34 | return 35 | 36 | 37 | # Abstract Base Class defined above ^^^ 38 | 39 | # Custom class inheriting from the above Abstract Base Class, below 40 | 41 | 42 | class MyClass(My_ABC_Class): 43 | def set_val(self, input): 44 | self.val = input 45 | 46 | def get_val(self): 47 | print("\nCalling the get_val() method") 48 | print("I'm part of the Abstract Methods defined in My_ABC_Class()") 49 | return self.val 50 | 51 | def hello(self): 52 | print("\nCalling the hello() method") 53 | print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") 54 | 55 | 56 | my_class = MyClass() 57 | 58 | my_class.set_val(10) 59 | print(my_class.get_val()) 60 | my_class.hello() 61 | -------------------------------------------------------------------------------- /08-abstract_classes/35-abstractclasses-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 34-abstractclasses-1.py 4 | 5 | # This code snippet talks about Abstract Base Classes (abc). 6 | 7 | # The `abc` module provides features to create 8 | # Abstract Base Classes. 9 | 10 | # To create an Abstract Base Class, set the `__metaclass__` magic method 11 | # to `abc.ABCMeta`. This will mark the respective class as an Abstract 12 | # Base Class. 13 | 14 | # Now, in order to specify the methods which are to be enforced on the 15 | # child classes, ie.. to create Abstract Methods, we use the decorator 16 | # @abc.abstractmethod on the methods we need. 17 | 18 | # The child class which inherits from an Abstract Base Class can implement 19 | # methods of their own, but *should always* implement the methods defined in 20 | # the parent ABC Class. 21 | 22 | # NOTE: This code will error out. This is an example on what 23 | # happens when a child class doesn't implement the abstract methods 24 | # defined in the Parent Class. 25 | 26 | import abc 27 | 28 | 29 | class My_ABC_Class(object): 30 | __metaclass__ = abc.ABCMeta 31 | 32 | @abc.abstractmethod 33 | def set_val(self, val): 34 | return 35 | 36 | @abc.abstractmethod 37 | def get_val(self): 38 | return 39 | 40 | 41 | # Abstract Base Class defined above ^^^ 42 | 43 | # Custom class inheriting from the above Abstract Base Class, below 44 | 45 | 46 | class MyClass(My_ABC_Class): 47 | def set_val(self, input): 48 | self.val = input 49 | 50 | def hello(self): 51 | print("\nCalling the hello() method") 52 | print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") 53 | 54 | 55 | my_class = MyClass() 56 | 57 | my_class.set_val(10) 58 | print(my_class.get_val()) 59 | my_class.hello() 60 | -------------------------------------------------------------------------------- /08-abstract_classes/36-abstractclasses-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 34-abstractclasses-1.py 4 | 5 | # This code snippet talks about Abstract Base Classes (abc). 6 | 7 | # The `abc` module provides features to create 8 | # Abstract Base Classes. 9 | 10 | # To create an Abstract Base Class, set the `__metaclass__` magic method 11 | # to `abc.ABCMeta`. This will mark the respective class as an Abstract 12 | # Base Class. 13 | 14 | # Now, in order to specify the methods which are to be enforced on the 15 | # child classes, ie.. to create Abstract Methods, we use the decorator 16 | # @abc.abstractmethod on the methods we need. 17 | 18 | # The child class which inherits from an Abstract Base Class can implement 19 | # methods of their own, but *should always* implement the methods defined in 20 | # the parent ABC Class. 21 | 22 | # NOTE: This code will error out. This is an example on what 23 | # happens when a child class doesn't implement the abstract methods 24 | # defined in the Parent Class. 25 | 26 | import abc 27 | 28 | 29 | class My_ABC_Class(object): 30 | __metaclass__ = abc.ABCMeta 31 | 32 | @abc.abstractmethod 33 | def set_val(self, val): 34 | return 35 | 36 | @abc.abstractmethod 37 | def get_val(self): 38 | return 39 | 40 | 41 | # Abstract Base Class defined above ^^^ 42 | 43 | # Custom class inheriting from the above Abstract Base Class, below 44 | 45 | 46 | class MyClass(My_ABC_Class): 47 | def set_val(self, input): 48 | self.val = input 49 | 50 | def hello(self): 51 | print("\nCalling the hello() method") 52 | print("I'm *not* part of the Abstract Methods defined in My_ABC_Class()") 53 | 54 | 55 | my_class = My_ABC_Class() 56 | 57 | my_class.set_val(10) 58 | print(my_class.get_val()) 59 | my_class.hello() 60 | -------------------------------------------------------------------------------- /09-method_overloading/37-method-overloading-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 37-method-overloading-1.py 4 | 5 | # Reference: O'Reilly Learning Path: 6 | # http://shop.oreilly.com/product/0636920040057.do 7 | # Chapter 24 : Method Overloading - Extending and Providing 8 | 9 | # This code is an example on how we can extend a method inherited by 10 | # a child class from the Parent class. 11 | 12 | # 1) We have defined `MyClass()` as an abstract class, 13 | # and it has three methods, my_set_val(), my_get_val(), and print_doc(). 14 | # 2) MyChildClass() inherits from MyClass() 15 | # 3) MyChildClass() extends the parent's my_set_val() method 16 | # by it's own my_set_val() method. It checks for the input, 17 | # checks if it's an integer, and then calls the my_set_val() 18 | # method in the parent. 19 | 20 | # 4) The print_doc() method in the Parent is an abstract method 21 | # and hence should be implemented in the child class MyChildClass() 22 | 23 | 24 | import abc 25 | 26 | 27 | class MyClass(object): 28 | 29 | __metaclass__ = abc.ABCMeta 30 | 31 | def my_set_val(self, value): 32 | self.value = value 33 | 34 | def my_get_val(self): 35 | return self.value 36 | 37 | @abc.abstractmethod 38 | def print_doc(self): 39 | return 40 | 41 | 42 | class MyChildClass(MyClass): 43 | def my_set_val(self, value): 44 | if not isinstance(value, int): 45 | value = 0 46 | super(MyChildClass, self).my_set_val(self) 47 | 48 | def print_doc(self): 49 | print("Documentation for MyChild Class") 50 | 51 | 52 | my_instance = MyChildClass() 53 | my_instance.my_set_val(100) 54 | print(my_instance.my_get_val()) 55 | print(my_instance.print_doc()) 56 | -------------------------------------------------------------------------------- /09-method_overloading/38-method-overloading-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 37-method-overloading-1.py 4 | 5 | # Reference: O'Reilly Learning Path: 6 | # http://shop.oreilly.com/product/0636920040057.do 7 | # Chapter 24 : Method Overloading - Extending and Providing 8 | 9 | import abc 10 | 11 | 12 | class GetSetParent(object): 13 | 14 | __metaclass__ = abc.ABCMeta 15 | 16 | def __init__(self, value): 17 | self.val = 0 18 | 19 | def set_val(self, value): 20 | self.val = value 21 | 22 | def get_val(self): 23 | return self.val 24 | 25 | @abc.abstractmethod 26 | def showdoc(self): 27 | return 28 | 29 | 30 | class GetSetList(GetSetParent): 31 | def __init__(self, value=0): 32 | self.vallist = [value] 33 | 34 | def get_val(self): 35 | return self.vallist[-1] 36 | 37 | def get_vals(self): 38 | return self.vallist 39 | 40 | def set_val(self, value): 41 | self.vallist.append(value) 42 | 43 | def showdoc(self): 44 | print( 45 | "GetSetList object, len {0}, store history of values set".format( 46 | len(self.vallist) 47 | ) 48 | ) 49 | -------------------------------------------------------------------------------- /09-method_overloading/39-method-overloading-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 39-method-overloading-3.py 4 | 5 | # We've seen that inherited methods can be overloaded. 6 | # This is possible using in-built functions as well. 7 | 8 | # Let's see how we can overload methods from the `list` module. 9 | 10 | 11 | class MyList(list): 12 | def __getitem__(self, index): 13 | if index == 0: 14 | raise IndexError 15 | if index > 0: 16 | index -= 1 17 | return list.__getitem__(self, index) 18 | 19 | def __setitem__(self, index, value): 20 | if index == 0: 21 | raise IndexError 22 | if index > 0: 23 | index -= 1 24 | list.__setitem__(self, index, value) 25 | 26 | 27 | x = MyList(["a", "b", "c"]) 28 | print(x) 29 | print("-" * 10) 30 | 31 | x.append("d") 32 | print(x) 33 | print("-" * 10) 34 | 35 | x.__setitem__(4, "e") 36 | print(x) 37 | print("-" * 10) 38 | 39 | print(x[1]) 40 | print(x.__getitem__(1)) 41 | print("-" * 10) 42 | 43 | print(x[4]) 44 | print(x.__getitem__(4)) 45 | -------------------------------------------------------------------------------- /10-super/40-super-1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 40-super-1.py 4 | 5 | # This is an example on how super() works 6 | # in Inheritance. 7 | 8 | # For more step-by-step details, refer : 9 | # https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ 10 | 11 | 12 | class MyClass(object): 13 | def func(self): 14 | print("I'm being called from the Parent class") 15 | 16 | 17 | class ChildClass(MyClass): 18 | def func(self): 19 | print("I'm actually being called from the Child class") 20 | print("But...") 21 | # Calling the `func()` method from the Parent class. 22 | super(ChildClass, self).func() 23 | 24 | 25 | my_instance_2 = ChildClass() 26 | my_instance_2.func() 27 | -------------------------------------------------------------------------------- /10-super/41-super-2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 41-super-2.py 4 | 5 | # For more information on how this works, refer: 6 | 7 | # https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ 8 | 9 | # https://arvimal.wordpress.com/2016/06/29/inheritance-and-method-overloading-object-oriented-programming/ 10 | 11 | import abc 12 | 13 | 14 | class MyClass(object): 15 | 16 | __metaclass__ = abc.ABCMeta 17 | 18 | def my_set_val(self, value): 19 | self.value = value 20 | 21 | def my_get_val(self): 22 | return self.value 23 | 24 | @abc.abstractmethod 25 | def print_doc(self): 26 | return 27 | 28 | 29 | class MyChildClass(MyClass): 30 | def my_set_val(self, value): 31 | if not isinstance(value, int): 32 | value = 0 33 | super(MyChildClass, self).my_set_val(self) 34 | 35 | def print_doc(self): 36 | print("Documentation for MyChild Class") 37 | 38 | 39 | my_instance = MyChildClass() 40 | my_instance.my_set_val(100) 41 | print(my_instance.my_get_val()) 42 | my_instance.print_doc() 43 | -------------------------------------------------------------------------------- /10-super/42-super-3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # 42-super-3.py 4 | 5 | # super() and __init__() 6 | 7 | # Refer 8 | # https://arvimal.wordpress.com/2016/07/01/inheritance-and-super-object-oriented-programming/ 9 | 10 | # http://www.blog.pythonlibrary.org/2014/01/21/python-201-what-is-super/ 11 | 12 | 13 | class A(object): 14 | def foo(self): 15 | print("A") 16 | 17 | 18 | class B(A): 19 | def foo(self): 20 | print("B") 21 | 22 | 23 | class C(A): 24 | def foo(self): 25 | print("C") 26 | super(C, self).foo() 27 | 28 | 29 | class D(B, C): 30 | def foo(self): 31 | print("D") 32 | super(D, self).foo() 33 | 34 | 35 | d = D() 36 | d.foo() 37 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Vimal A R 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object Oriented Programming in Python 2 | 3 | 01. [Classes](https://github.com/arvimal/Python-and-OOP#01-classes) 4 | 02. [Instances, Instance methods, Instance attributes](https://github.com/arvimal/Python-and-OOP#02-instances-instance-methods-instance-attributes) 5 | 03. [Class attributes](https://github.com/arvimal/Python-and-OOP#03-class-attributes) 6 | 04. [The __init__ constructor](https://github.com/arvimal/Python-and-OOP#04-the-init-constructor) 7 | 05. [Inheritance (Inheriting {attributes,methods,constructors etc..})](https://github.com/arvimal/Python-and-OOP#05-inheritance-inheriting-attributesmethodsconstructors-etc) 8 | 06. [Encapsulation](https://github.com/arvimal/Python-and-OOP#06-encapsulation) 9 | 07. [Polymorphism](https://github.com/arvimal/Python-and-OOP#07-polymorphism) 10 | 08. [Instance methods](https://github.com/arvimal/Python-and-OOP#08-instance-methods) 11 | 09. [Multiple Inheritance and method/attribute lookup](https://github.com/arvimal/Python-and-OOP#10-multiple-inheritance-and-how-lookup-works) 12 | 10. [Method Resolution Order (MRO)](https://github.com/arvimal/Python-and-OOP#11-method-resolution-order-mro) 13 | 11. [Decorators](https://github.com/arvimal/Python-and-OOP#12-decorators) 14 | 12. [Static methods](https://github.com/arvimal/Python-and-OOP#13-static-methods) 15 | 13. [Class methods](https://github.com/arvimal/Python-and-OOP#14-class-methods) 16 | 17 | 18 | # NOTES 19 | ------------ 20 | #### 01. Classes 21 | 22 | Classes are the building blocks in Object Oriented Programming. 23 | 24 | Classes can be seen as blueprints from which you create your Instances. 25 | 26 | ------------ 27 | #### 02. Instances, Instance methods, Instance attributes 28 | 29 | ------------ 30 | #### 03. Class attributes 31 | 32 | Attributes or methods specific to a class are called Class attributes 33 | 34 | Example: 35 | 36 | ``` 37 | class MyClass(object): 38 | value = 10 39 | 40 | def __init__(self): 41 | pass 42 | ``` 43 | 44 | Here `value` is a class attribute. These are used when certain values need to be set outside a function. 45 | 46 | ------------ 47 | #### 04. The __init__ constructor 48 | 49 | The __init__() constructor is a magic method which gets called when a class is instantiated. 50 | 51 | Any attributes set under the __init__() constructor will be instantiated at the time of instance creation. 52 | 53 | ------------ 54 | #### 05. Inheritance (Inheriting {attributes,methods,constructors etc..}) 55 | 56 | ------------- 57 | #### 06. Encapsulation 58 | 59 | ------------ 60 | #### 07. Polymorphism 61 | 62 | ------------ 63 | #### 08. Instance methods 64 | 65 | ------------ 66 | #### 09. Multiple Inheritance and method/attribute lookup 67 | 68 | * Any class can inherit from other classes. 69 | * Any python class can inherit from multiple classes at the same time. 70 | * The class that inherits another class is called the Base/Child class. 71 | * The class being inherited by the Child class is called the Parent class. 72 | * The child class inherits any methods and attributes defined in the parent classes. 73 | * Python uses a depth-first method resolution order (MRO) to fetch methods. 74 | * When two classes inherits from the same class, from Python2.3 onwards, the MRO omits the first occurrence of the class. 75 | * This new MRO lookup method applies from Python2.3, and is for the new-style classes. 76 | *NOTE:* New style classes inherits from the 'object' parent class. 77 | 78 | ------------ 79 | #### 10. Method Resolution Order (MRO) 80 | 81 | * Python has a method lookup order, called `MRO` (Method Resolution Order) 82 | * The MRO path can be printed to stdout using `print .mro()` 83 | * Python, by default, uses a depth-first lookup path for MRO. 84 | 85 | * ie.. Imagine you have four classes, A, B, C, D. 86 | 87 | 1. You instance is created from `D`. 88 | 2. `D` inherits from `B` and `C` 89 | 3. `B` inherits from `A`. 90 | 4. Both `C` and `A` has a method with the same name. 91 | 5. Since python follows a depth-first MRO, the method is called from `A` 92 | 93 | REFERENCE: Check the code examples in: 94 | * 14-multiple-inheritance-1.py 95 | * 15-multiple-inheritance-2.py 96 | 97 | In some cases, the inheritance can get quite cumbersome when multiple classes inherit from the same classes, in multiple levels. 98 | 99 | **NOTE** : From Python2.3, the MRO has changed slightly in order to speed up the method lookups. 100 | 101 | The MRO lookup now skips/omits the initial occurrences of classes which occurs multiple time in the lookup path. 102 | 103 | * Example: 104 | 1. Four classes, A, B, C, D. 105 | 2. D inherits from both B and C 106 | 3. B inherits from A 107 | 4. C inherits from A 108 | 5. Both C and A contains a similar named method. 109 | 6. Your instance in created from class D. 110 | 7. You try a lookup for the method which is named both in A and C. 111 | 8. The usual lookup should be D -> B -> A -> C -> A 112 | a. Hence since the method exists in A and C, it should return from A. 113 | b. But since the class A is occurring twice and due to the new MRO method, the lookup will be 114 | D -> B -> C -> A. 115 | 9. The lookup should return the method from class C, rather than A. 116 | 117 | REFERENCE: Check the code example in: 118 | * 16-multiple-inheritance-3.py 119 | 120 | -------------- 121 | #### 11. Decorators 122 | 123 | -------------- 124 | #### 12. Static methods 125 | 126 | -------------- 127 | #### 13. Class methods 128 | 129 | -------------- 130 | 131 | #### 14. Magic methods 132 | 133 | Magic methods 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | --------------------------------------------------------------------------------