Course

Python for
Automation

Master the core concepts of Python needed to script your way to freedom.

Variables & Data Types

Learn how to store data. In Python, variables are dynamically typed containers for storing data values.

# Variables

X = 1 # int
y = 2.5 # float
name = 'Asad' # String
is_cool = True # bool

# Multiple Variable Assignment
x, y, name, is_cool = (1, 2.5, 'Asad', True)

# Basic Math
a = x + y

# Casting (Changing type)
x = str(x)
y = int(y)
z = float(y)

# print function
print(x, y, name, is_cool, a)

# Check the type
print(type(z), z)

Progress Test

  • Write code to check the type of the value 5 and print it.
  • Cast the integer 10 to a string and print its type.
  • Create a variable pi = 3.14 and print it.
  • Add 5 and 10 and print the result.

Strings

Strings in python are surrounded by either single or double quotation marks. Let's look at string formatting and some string methods

# String Formatting
name = 'Brad'
age = 37

# Concatenate
print('Hello, my name is ' + name + ' and I am ' + str(age))

# Arguments by position
print('My name is {name} and I am {age}'.format(name=name, age=age))

# F-Strings (Only available in python 3.6 and after)
print(f'Hello, my name is {name} and I am {age}')

# String Methods

# Capitalize
s = 'hello world'
print(s.capitalize())

# Make all uppercase
s = 'hello world'
print(s.upper())

# Make all lowercase
s = 'hello world'
print(s.lower())

# Swap case
s = 'hello woRld'
print(s.swapcase())

# Get length
s = 'hello woRld'
print(len(s))

# Replace
s = ' hello world'
print(s.replace('world', 'everone'))

# Count how many 'H' in the variable or string
sub = 'h'
print(s.count(sub))

# Starts with will return true or false if the variable is starting with the provided name
s = 'hello woRld'
print(s.startswith('hello'))

# Ends with will do the opposite of starts with
s = 'hello woRld'
print(s.endswith('woRld'))

# Split into a list will convert it into a list which is an arry
s = 'hello woRld'
print(s.split())

# Find position
print(s.find('d'))

# Is all alphanumeric
print(s.isalnum())

# Is all alphabetic
print(s.isalpha())

# Is all numeric
print(s.isalnum())

Progress Test

  • Capitalize the string 'python' and print it.
  • Check if '123' is a digit using .isdigit() and print result.
  • Replace 'Bad' with 'Good' in 'Python is Bad' and print it.
  • Create an F-String to say 'I am 25' using age=25 and print it.

Lists

A List is a collection which is ordered and changeable. Allows duplicate numbers.

#Create list
numbers = [1, 2, 3, 4, 5]
fruits = ['Apples', 'Oranges', 'Pears', 'Banana']

#Use a constructor
number2 = list((1, 2, 3, 4, 5))

print(numbers, number2)
print(fruits[0])

# Get length
print(len(fruits))

# Append to list
fruits.append('Mangos')
print(fruits)


# Extend list
fruits.extend(["fig", "grape"])

# Remove from list
fruits.remove('Banana')
print(fruits)

# Insert into position
fruits.insert(0, 'Strawberries')
print(fruits)

# Change value
fruits[0] = 'Blueberries'

# Remove with pop
fruits.pop(2)
print(fruits)

# Reverse the list
fruits.reverse()
print(fruits)

# Sort list (Alphabetically sorting)
fruits.sort()
print(fruits)

# Sorting reverse
fruits.sort(reverse=True)
print(fruits)


print(type(number2))

Progress Test

  • Add 'Grapes' to fruits = ['Apple'] and print the list.
  • Remove 'Apple' from fruits = ['Apple', 'Banana'] and print the list.
  • Sort nums = [3, 1, 2] and print it.
  • Print the length of [1, 2, 3].

Tuples & Sets

A Tuple is a collection which is ordered and unchangeable. Allows duplicate members. A Set is a collection which is unordered and unindexed. No duplicate members.

# Create tuple
fruits = ('Apples', 'Oranges', 'Grapes')
fruits2 = tuple(('Apples', 'Oranges', 'Grapes'))

print(fruits, fruits2)

# Single value needs trailing comma
fruits1 = ('Apples',)
print(fruits1, type(fruits1))

# Get value
print(fruits[1])

# Can't change value
# fruits[0] = 'pears'

# Delete tuple
# del fruits
print(fruits)

# Get lenth
print(len(fruits))


# A Set is a collection which is unordered and unindexed. No duplicate members.

# Create set ( it is created with curly braces)
fruits_set = {'Apples', 'Bananas', 'Mangos'}

# Check if in set
print('Apples' in fruits_set)

# Add to set
fruits_set.add('Grape')
print(fruits_set)

# No duplicate members (If the value already exists then it won't add it again or one more time)
fruits_set.add('Bananas')
print(fruits_set)

# Remove from set
fruits_set.remove('Grape')
print(fruits_set)

# Clearing set
fruits_set.clear()
print(fruits_set)

# Deleting set
del fruits_set
print(fruits_set)

Dictionaries

A Dictionary is a collection which is unordered, changeable and indexed. No duplicate members.

# Create dict
person = {
    'first_name' : 'John',
    'last_name' : 'Doe',
    'age' : 30
}

print(person, type(person))

# User constructor
person2 = dict(first_name='Muhammad',last_name='Asad',age=30)

print(person2, type(person2))

# Get the value
print(person2['first_name'], 'Getting the value')

# Another way to get value using get method
print(person.get('age'), 'getting value with another method')

# Add key value
person['profession'] = 'Worker'
print(person, 'Adding key and value to the dictionary')

# Get dict Keys
print(person.keys(), 'Geting the keys')

# Get dict Values
print(person.values(), 'Getting the values')

# Copy the dict
person = person2.copy()
print(person, 'Copying the dictionary')

# Remove item
del(person['last_name'])
print(person, 'Removing last_name with remove method')

# Another method to remove an item
person.pop('age')
print(person, 'Removing age with pop method')

# Clear the data
person.clear()
print(person, 'Clearing Data')

# Getting length
print(len(person), 'getting the length of the Dictionary')

# List of dict
people = [
    {'name': 'Martha', 'age': 31},
    {'name': 'Addie', 'age': 25}
]
print(people, 'list/Array of dictionary')

# Getting the value from the array of dictionarry
print(people[0]['name'],people[1]['name'], 'getting the values from the array of dictionary')

Conditionals

If/ Else conditions are used to decide to do something based on something being true or false

# Comparision Operators (==, !=, >, <, >=, <=) - Used to compare values

x = 7
y = 10

# Simple if

if x > y:
    print(f'{x} is greator then {y}', ': Simple if condition')

# If / Else
    
if x > y:
    print(f'{x} is greator then {y}', ': if else condition')
else:
    print(f'{x} is less then {y}', ': if else condition')

# elif or else / if
if x > y:
    print(f'{x} is greator then {y}', ': elif or else / if condition')
elif x == y:
    print(f'{x} is equal to {y}', ': elif or else / if condition')
else:
    print(f'{x} is less then {y}', ': elif or else / if condition')

# Nasted if
    if x > 2:
        if x <= 10:
          print(f'{x} is greater than 2 and less than or equal to {y}',': Nasted if condition')  


# Logical Operators (and, or, not) - Used to combile conditional statements

# And logical operator
if x > 2 and x <= 10:
    print(f'{x} is greater than 2 and less than or equal to {y}',': logical operator (And) condition')  

# Or logical operator
if x > 2 and x <= 10:
    print(f'{x} is greater than 2 and less than or equal to {y}',': logical operator (Or) condition')  

# Not logical operator
if not(x == y):
    print(f'{x} is not equal to {y}',': logical operator (Not) condition')  

# Membership Operators (not, not in) - Membership operators are used to test if a sequence is presented in an object
numbers = [1,2,3,4,5,6,7,8,9,10]

z = 7
a = 13
# in
if z in numbers:
    print(z in numbers, ': in condition')

# not in
if a not in numbers:
    print(a not in numbers, ': not in condition')


# Identity Operators (is, is not) - Compare the objects, not if they are equal, but if they are actually the same object, with the same memory location:
    b = 10
    c = 10

    # is
    if b is c:
        print(b is c, ': is condition')

    if x is not y:
        print(b is not c, ': is not condition')

Loops

A for loop is used for iterating over a sequence (that is either a list, a tuple, a dictionary, a set, or a string).

# Creating list
people = ['John', 'Paul', 'Sara', 'Susan']

# Simple for loop
for Person in people:
    print(f'Current Person: {Person}', ': Simple loop')

# Break
for Person in people:
    if Person == 'Sara':
        break
    print(f'Current Person: {Person}', ': Break in loop')

# Continue will skip the 'Sara'
for Person in people:
    if Person == 'Sara':
        continue
    print(f'Current Person: {Person}', ': Continue loop')

# Range
for i in range(len(people)):
    print(people[i])

# Custom ranges
for i in range(3, 11):
    print(f'Number: {i}')


# While loops execute a set of statements as long as a condition is true.
        
count = 0
while count <= 10:
    print(f'Count : {count}')
    count += 1

Functions

A function is a block of code which only runs when it is called. In Python, we do not use curly brackets, we use indentation with tabs or spaces

# Create function
def sayHello(name):
    print(f'Hello {name}')

sayHello('John deo')

# Creating funcion with the default value
def sayHello(name = 'Asad'):
    print(f'Hello {name}')

sayHello()

# Return values
def getSum(num1,num2):
    total = num1 + num2
    return total

print(getSum(1,2))
# Another way to print
num = getSum(6,7)
print(num)

# A lambda function is a small anonymous function
# A lambda function can take any number of arguments, but can only have one expression. Very similar to JS arrow functions
# 'getSumlambda' is a funtion name
# 'lambda num1, num2' is telling that if will accept the parameteres 
# ': num1 + num2' is thie main functionality
getSumlambda = lambda num1, num2 : num1 + num2
print(getSumlambda(10,2))

Built-in Functions

Examples of various built-in string functions

# isalpha(): Checks if all characters in a string are alphabetic (letters)
string_alpha = "abc"
print(string_alpha.isalpha())  # Output: True

# isdigit(): Checks if all characters in a string are digits
string_digit = "123"
print(string_digit.isdigit())  # Output: True

# isspace(): Checks if all characters in a string are whitespace characters (spaces, tabs, newline)
string_space = " \t\n"
print(string_space.isspace())  # Output: True

# islower(): Checks if all characters in a string are lowercase
string_lower = "abc"
print(string_lower.islower())  # Output: True

# isupper(): Checks if all characters in a string are uppercase
string_upper = "ABC"
print(string_upper.isupper())  # Output: True

# startswith(prefix): Checks if a string starts with the specified prefix
string_start = "Hello, world!"
print(string_start.startswith("Hello"))  # Output: True


# endswith(suffix): Checks if a string ends with the specified suffix
string_end = "Hello, world!"
print(string_end.endswith("world!"))  # Output: True

# split(): Splits a string into a list of substrings based on a delimiter
string_split = "Hello, world!"
print(string_split.split(", "))  # Output: ['Hello', 'world!']

# join(iterable): Concatenates elements of an iterable (like a list) into a single string, using the string as a delimiter
my_list = ["Hello", "world!"]
print(", ".join(my_list))  # Output: Hello, world!

Classes & Objects

A class is like a blueprint for creating objects. An object has properties and methods (functions) associated with it. Almost everything in Python is an object

# Create class
class User:
    # Constructor
    def __init__(self, name, email, age):
        self.name = name
        self.email = email
        self.age = age

    def greeting(self):
        return f'My name is {self.name} and I am {self.age} and my balance is {self.balance}'
    
    def has_birthday(self):
        self.age += 1


class Customer(User):
    # Constructor
    def __init__(self, name, email, age):
        self.name = name
        self.email = email
        self.age = age
        self.balance = 0

    def set_balance(self, balance):
        self.balance = balance

# Init customer object
janet = Customer('Janet Johnson', 'janet@yahoo.com', 25)

janet.set_balance(500)

# Init user object
brad = User('Brad Traversy', 'brad@gmail.com', 37)

brad.has_birthday()
print(brad.greeting())

Constructors

It is a special method within a class that gets automatically called when an object or instance of the class is created.

class faculty:
    def putdata(self):
        self.id=int((input("Enter Faculty ID")))
        self.name=input("Enter Name")
        self.salary=float(input("Enter Salary"))
    def display(self):
        print(f'Faculty ID : {self.id}')
        print(f'Faculty Name :{self.name}')
        print(f'Faculty Salary : {self.salary}')

a = faculty()
a.putdata()
a.display()



class faculty:
    def __init__(self):
        self.id=int((input("Enter Faculty ID")))
        self.name=input("Enter Name")
        self.salary=float(input("Enter Salary"))
    def display(self):
        print(f'Faculty ID : {self.id}')
        print(f'Faculty Name : {self.name}')
        print(f'Faculty Salary : {self.salary}')

a = faculty()
# Here we don't need to call init, it will run automatically when an object is called or created
a.display()



# Parameter passing

class MyClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# Create an instance of MyClass
obj = MyClass(10, 20)

# Access the instance variables
print(obj.x)  # Output: 10
print(obj.y)  # Output: 20

Advanced OOP

It might seem like encapsulation and abstraction are the same because they both deal with hiding or simplifying things, but they are actually different concepts that work together in programming.

Encapsulation:

  • Focus: Encapsulation is about hiding the internal details and protecting the internal state of an object.
  • It bundles the data (attributes) and the methods (functions) that operate on the data into a single unit, called a class, and hides the details from the outside world.

Abstraction:

  • Focus: Abstraction is about simplifying complexity by providing a simple interface or interaction method for the user, without exposing the underlying complex details.
  • It focuses on what an object does, rather than how it does it.

Encapsulation in Python is primarily achieved by using private and protected attributes and methods.

# Define a class called BankAccount to represent a bank account
class BankAccount:
    def __init__(self, account_number, balance):
        # Private attributes
        self._account_number = account_number  # Protected attribute
        self.__balance = balance  # Private attribute
    
    # Public method to deposit money into the account
    def deposit(self, amount):
        self.__balance = self.__balance + amount  # Explicit addition instead of +=
        print(f"Deposit of ${amount} successful. New balance: ${self.__balance}")
    
    # Public method to withdraw money from the account
    def withdraw(self, amount):
        if amount > self.__balance:
            print("Insufficient funds")
        else:
            self.__balance = self.__balance - amount  # Explicit subtraction instead of -=
            print(f"Withdrawal of ${amount} successful. New balance: ${self.__balance}")
    
    # Public method to get account balance
    def get_balance(self):
        return self.__balance

# Create an instance of BankAccount
account = BankAccount("1234567890", 1000)

# Perform operations using public methods
account.deposit(500)   # Outputs: "Deposit of $500 successful. New balance: $1500"
account.withdraw(200)  # Outputs: "Withdrawal of $200 successful. New balance: $1300"
print(account.get_balance())  # Outputs: 1300

Abstraction in Python involves hiding the complex implementation details and showing only the essential features of an object.

from abc import ABC, abstractmethod  # Import the ABC module and abstractmethod decorator

# Define an abstract class called Mouse
class Mouse(ABC):
    # Abstract method for clicking
    @abstractmethod
    def click(self):
        pass  # Placeholder statement to indicate no implementation, must be overridden in subclasses

# Define a subclass WirelessMouse which inherits from Mouse
class WirelessMouse(Mouse):
    def __init__(self, brand):
        self.brand = brand  # Initialize the brand attribute
    
    # Implementing the abstract method to perform a click for WirelessMouse
    def click(self):
        print(f"{self.brand} Wireless Mouse: Click")  # Print a message indicating a click with the brand of the mouse

# Define a subclass WiredMouse which also inherits from Mouse
class WiredMouse(Mouse):
    def __init__(self, brand):
        self.brand = brand  # Initialize the brand attribute
    
    # Implementing the abstract method to perform a click for WiredMouse
    def click(self):
        print(f"{self.brand} Wired Mouse: Click")  # Print a message indicating a click with the brand of the mouse

# Create instances of WirelessMouse and WiredMouse
wireless_mouse = WirelessMouse("Logitech")  # Instantiate a WirelessMouse object with brand "Logitech"
wired_mouse = WiredMouse("Microsoft")      # Instantiate a WiredMouse object with brand "Microsoft"

# Click with both types of mice
wireless_mouse.click()  # Outputs: "Logitech Wireless Mouse: Click"
wired_mouse.click()     # Outputs: "Microsoft Wired Mouse: Click"

Inheritance allows a class to inherit attributes and methods from another class.

# Inheritance

class Employee:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def showData(self):
        print(f'Emplyee name is {self.name} and id is {self.id}')

# Inheriting everything which Employee have 
class Programmer(Employee):
    def showLanguage(self):
        print('Default language is Python')

# Calling Employee method directly
e1 = Employee("Asad",200)
e1.showData()

# Calling Employee method using Programmer to see if the inheritence is working
e3 = Programmer("Hadi",150)
e3.showData()
e3.showLanguage()

Polymorphism allows methods to do different things based on the object it is acting upon.

# Define a base class called Phone
class Phone:
    # Define a method to produce a generic phone ring sound
    def phone_ring(self):
        print("Ring Ring Ring")
    
    # Define a method to indicate a call connection
    def call(self):
        print("Call Has Been Connected")
    
    # Define a method to indicate a message has been sent
    def message(self):
        print("Message Has Been Sent")

# Define a subclass Samsung which inherits from Phone
class Samsung(Phone):
    # Override the phone_ring method to produce a Samsung-specific ring sound
    def phone_ring(self):
        print("Tu Tu Tu")

# Define another subclass IPhone which also inherits from Phone
class IPhone(Phone):
    # Override the phone_ring method to produce an iPhone-specific ring sound
    def phone_ring(self):
        print("iPhone Ring")

# Create instances of generic phone
generic_phone = Phone()
# Create instances of Samsung and iPhone
samsung_phone = Samsung()  # Creating an instance of Samsung phone
iphone_phone = IPhone()     # Creating an instance of iPhone phone

# Call the phone_ring method for both instances
# The phone_ring method behaves differently for each object
# This is polymorphism - the same method name is used, but behaves differently depending on the object
generic_phone.phone_ring()  # Outputs: "Ring Ring Ring"
samsung_phone.phone_ring()  # Outputs: "Tu Tu Tu"
iphone_phone.phone_ring()   # Outputs: "iPhone Ring"

A class can inherit from multiple parent classes.

# Define a base class called Animal
class Animal:
    def __init__(self, name):
        self.name = name
    
    def sound(self):
        print("Some generic sound")

# Define a subclass Dog which inherits from Animal
class Dog(Animal):
    def sound(self):
        print("Woof! Woof!")

# Define another subclass Cat which also inherits from Animal
class Cat(Animal):
    def sound(self):
        print("Meow! Meow!")

# Define a subclass Duck which inherits from both Dog and Cat
# This demonstrates multiple inheritance
class Duck(Dog, Cat):
    def sound(self):
        print("Quack! Quack!")

# Create instances of Dog, Cat, and Duck
dog = Dog("Buddy")
cat = Cat("Whiskers")
duck = Duck("Daffy")

# Call the sound method on instances of Dog, Cat, and Duck
# The sound method behaves differently for each object based on their respective implementations
dog.sound()  # Outputs: "Woof! Woof!"
cat.sound()  # Outputs: "Meow! Meow!"
duck.sound()  # Outputs: "Quack! Quack!"

# Accessing the 'name' attribute inherited from the Animal class
print(duck.name,dog.name,cat.name)  # Outputs: "Daffy"

Demonstrating Single, Multiple, and Multilevel inheritance structures.

# Single Inheritance
class A:
    pass

class B(A):
    pass

# Multiple Inheritance
class A:
    pass

class B:
    pass

class C(A, B):
    pass
# Multilevel Inheritance
class A:
    pass

class B(A):
    pass

class C(B):
    pass

Method overloading allows multiple methods with the same name but different parameters.

class Phone:
    # Define a method to produce a generic phone ring sound
    def phone_ring(self, ringtone="Ring Ring Ring"):
        print(ringtone)
    
    # Define a method to indicate a call connection
    def call(self):
        print("Call Has Been Connected")
    
    # Define a method to indicate a message has been sent
    def message(self):
        print("Message Has Been Sent")

# Create an instance of Phone
phone = Phone()

# Call the phone_ring method with different parameters
# The phone_ring method behaves differently based on the number of parameters passed
# This demonstrates method overloading-like behavior
phone.phone_ring()               # Outputs: "Ring Ring Ring" (default ringtone)
phone.phone_ring("Tu Tu Tu")     # Outputs: "Tu Tu Tu"
phone.phone_ring("iPhone Ring")  # Outputs: "iPhone Ring"

Modules

A module is basically a file containing a set of functions to include in your application.

# A module is basically a file containing a set of functions to include in your application.
# There are core python modules, modules you can install using pip package manager (incuding Django) as well as custom modules

# Core Modules

# Importing date time module
import datetime
today = datetime.date.today()
print(today)

# Another way of importing directly date from the datetime
from datetime import date
today1 = date.today()
print(today1)

# Importing time
import time
timestamp = time.time()
print(timestamp)

# Pip Modules
from camelcase import CamelCase

c = CamelCase()
print(c.hump('hello there world'))


# Import Custom Module
import Validator
from Validator import validate_email

email = "test@test.com"
if validate_email(email):
    print('Email is valid')
else:
    print('Email is bad')

Practice Interview Questions

Test your skills with these real-world Python interview questions. Try to solve them yourself before viewing the solution.

Question 1 of 30

Loading question...

Ready to try it? Open the compiler below: