Django Email

Django provides a powerful and flexible way to send emails using its built-in email functionality. You can use this feature to send transactional emails, notifications, or other types of email communications from your application.

Configuring Email in Django

To use Django’s email functionality, you must first configure your email settings in the settings.py file. Here are the common settings:

Example:


EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'  # Default SMTP backend
EMAIL_HOST = 'smtp.gmail.com'  # Your email host, e.g., Gmail, Outlook, etc.
EMAIL_PORT = 587  # Port number for SMTP
EMAIL_USE_TLS = True  # Enable TLS for security
EMAIL_HOST_USER = 'your_email@example.com'  # Your email address
EMAIL_HOST_PASSWORD = 'your_email_password'  # Your email password or app password

Sending Email

Django provides the send_mail() function to send simple emails. Here’s an example:

Example


from django.core.mail import send_mail

def send_simple_email():
    send_mail(
        'Subject of the Email',  # Email subject
        'This is the message body.',  # Email message
        'from@example.com',  # Sender email
        ['to@example.com'],  # Recipient email(s)
        fail_silently=False,  # Raise an exception if sending fails
    )

Sending Emails with Attachments

For more advanced email functionality, use EmailMessage:

Example


from django.core.mail import EmailMessage

def send_email_with_attachment():
    email = EmailMessage(
        'Subject of the Email',
        'This is the message body.',
        'from@example.com',
        ['to@example.com'],
    )
    email.attach_file('/path/to/attachment.pdf')
    email.send()

Use HTML Emails

Django also supports sending HTML emails.

Example:


from django.core.mail import EmailMultiAlternatives

def send_html_email():
    subject, from_email, to = 'Subject', 'from@example.com', 'to@example.com'
    text_content = 'This is the plain text message.'
    html_content = '<p>This is the <strong>HTML</strong> message.</p>'
    msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
    msg.attach_alternative(html_content, "text/html")
    msg.send()

Django Redirect

In Django, redirects are used to send users to a different URL from the one they initially requested. Django provides utilities for managing redirects easily in both views and templates.

How Redirects Work

When a redirect is triggered, Django sends an HTTP response with a status code (usually 302 Found or 301 Moved Permanently) and the new target URL. The browser automatically navigates to the new location.

There are many ways to perform Redirect

1. Using redirect()

Django’s redirect() shortcut simplifies redirection by handling URLs, named routes, and model objects.

Example:


from django.shortcuts import redirect

def my_view(request):
    return redirect('/new-url/')  # Redirect to a specific URL

Redirecting to a named URL pattern:


def my_view(request):
    return redirect('home')  # 'home' is the name of a URL pattern

Redirecting to an object’s URL:


def my_view(request, pk):
    from myapp.models import MyModel
    obj = MyModel.objects.get(pk=pk)
    return redirect(obj)  # Redirects to the URL of the object

2. Using HttpResponseRedirect

The HttpResponseRedirect class allows manual control over redirection.

Example:


from django.http import HttpResponseRedirect

def my_view(request):
    return HttpResponseRedirect('/new-url/')

3. Redirecting After Form Submission

Redirects are often used after successful form submissions to prevent duplicate form submissions when refreshing the page.

Example:


def submit_form(request):
    if request.method == "POST":
        # Process the form data
        return redirect('success-page')
    return render(request, 'form.html')

4. Permanent Redirects

To create a permanent redirect (301 Moved Permanently), use the HttpResponsePermanentRedirect class.

Example


from django.http import HttpResponsePermanentRedirect

def old_view(request):
    return HttpResponsePermanentRedirect('/new-url/')

Redirects in URL Configuration

Django’s URL routing system allows simple redirects using django.views.generic.RedirectView.

Example:


from django.urls import path
from django.views.generic import RedirectView

urlpatterns = [
    path('old-url/', RedirectView.as_view(url='/new-url/', permanent=True)),
]

Permanent vs. Temporary Redirect:

Use permanent=True for a 301 redirect.

Use permanent=False for a 302 redirect (default).

Django CRUD Application

Django CRUD application is a web application built using Django that allows users to perform the four essential database operations: CreateReadUpdate, and Delete. These operations are typically implemented to manage records in a database through Django’s ORM, views, forms, and templates.

Features of a Django CRUD Application

Create: Add new records using forms.

Read: Display records in a list or detail view.

Update: Edit existing records through forms.

Delete: Remove records with confirmation dialogs.

Components of a Django CRUD Application

1. Models (Database Layer)

Models define the structure of the data, serving as the link between your application and the database.

Example:


from django.db import models

class Item(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)

2. Views (Logic Layer)

Views handle the application’s logic and determine what data to send to templates. Django supports:

Function-based Views (FBVs) for simple logic.

Class-based Views (CBVs) like ListView, CreateView, UpdateView, and DeleteView for streamlined CRUD functionality.

Example of CBVs for CRUD:


from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from .models import Item
from django.urls import reverse_lazy

class ItemListView(ListView):
    model = Item
    template_name = "item_list.html"

class ItemCreateView(CreateView):
    model = Item
    fields = ['name', 'description', 'price']
    template_name = "item_form.html"

class ItemUpdateView(UpdateView):
    model = Item
    fields = ['name', 'description', 'price']
    template_name = "item_form.html"

class ItemDeleteView(DeleteView):
    model = Item
    success_url = reverse_lazy('item-list')

3. Templates (Presentation Layer)

Templates render the HTML for the application’s user interface.

Example:


<h1>Item List</h1>
<ul>
    {% for item in object_list %}
        <li>{{ item.name }} - {{ item.price }}</li>
    {% endfor %}
</ul>
<a href="{% url 'item-create' %}">Add New Item</a>

4. URLs (Routing)

URL patterns map HTTP requests to specific views.


from django.urls import path
from .views import ItemListView, ItemCreateView, ItemUpdateView, ItemDeleteView

urlpatterns = [
    path('', ItemListView.as_view(), name='item-list'),
    path('create/', ItemCreateView.as_view(), name='item-create'),
    path('/update/', ItemUpdateView.as_view(), name='item-update'),
    path('/delete/', ItemDeleteView.as_view(), name='item-delete'),
]

Django Default CRUD

In Django, default CRUD (Create, Read, Update, Delete) operations refer to the built-in capabilities provided by Django to handle these fundamental database actions through its models and views. Here’s an overview:

1. Create

You can use Django’s ORM (Object Relational Mapping) to add new entries to the database.

Example:


obj = MyModel.objects.create(field1="value1", field2="value2")

The save() method can also be used after instantiating a model object:


obj = MyModel(field1="value1", field2="value2")
obj.save()

2. Read

Django provides several ways to retrieve data from the database using the QuerySet API.

Example:

Retrieve all objects: MyModel.objects.all()

Filter objects: MyModel.objects.filter(field1=”value1″)

Get a single object: MyModel.objects.get(id=1)

3. Update

To update an object, retrieve it first, modify its fields, and then call save()


obj = MyModel.objects.get(id=1)
obj.field1 = "new_value"
obj.save()

Bulk updates can be performed with update()


MyModel.objects.filter(field1="value1").update(field2="new_value")

4. Delete

To delete an object, use its delete() method:


obj = MyModel.objects.get(id=1)
obj.delete()

Bulk deletion:


MyModel.objects.filter(field1="value1").delete()

Default CRUD in Django Admin

The Django admin interface provides a default CRUD GUI for managing models:

Create: Add new entries through the admin form.

Read: View existing entries in a list or detail view.

Update: Edit entries using the admin form.

Delete: Remove entries through the admin interface.

Django Cookies

Django cookies are small pieces of data stored on a user’s browser, sent by the server, and used to persist information about a user across multiple requests. Cookies are useful for tasks like tracking user sessions, storing preferences, and maintaining temporary data.

How Cookies Work in Django?

1. Setting Cookies: The server sends cookies to the client’s browser via the response.

2. Storing Cookies: The browser stores the cookies and includes them in subsequent requests to the same server.

3. Accessing Cookies: The server reads cookies from the incoming request for specific data.

How to use Cookies in Django?

1. Setting Cookies

You can set a cookie in the response using the set_cookie method:


from django.http import HttpResponse

def set_cookie(request):
    response = HttpResponse("Cookie has been set!")
    response.set_cookie('user_name', 'John', max_age=3600)  # Cookie valid for 1 hour
    return response

Parameters Details:

Key: The name of the cookie (‘user_name’).

Value: The data stored in the cookie (‘John’).

max_age: Expiry time in seconds (optional).

expires: Date and time when the cookie expires (optional).

secure: If True, the cookie is sent only over HTTPS.

httponly: If True, the cookie is inaccessible to JavaScript (adds security).

2. Accessing Cookies

You can access cookies from the request object using request.COOKIES:


def get_cookie(request):
    user_name = request.COOKIES.get('user_name', 'Guest')  # Default to 'Guest' if cookie not set
    return HttpResponse(f"Hello, {user_name}!")

3. Deleting Cookies

To delete a cookie, use the delete_cookie method:


def delete_cookie(request):
    response = HttpResponse("Cookie has been deleted!")
    response.delete_cookie('user_name')
    return response

Django session

Django session is a way to store information about a user across multiple requests. This enables you to manage user-specific data, such as login status, preferences, or shopping cart contents, without requiring the user to authenticate or pass data on every request.

How Django Sessions Work?

1. Session ID: Django generates a unique session ID for each user, stored in a cookie on the client’s browser.

2. Session Data: The actual session data is stored on the server (e.g., database, file system, or cache).

3. Session Persistence: The session ID in the client’s cookie links to the data on the server, enabling persistence across multiple requests.

How to Enable Sessions in Django

Django sessions are enabled by default. Ensure the following middleware is added to your MIDDLEWARE setting in settings.py:


MIDDLEWARE = [
    ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    ...
]

Also, ensure ‘django.contrib.sessions’ is in INSTALLED_APPS.

Using Sessions in Django

1. Set Session Data

Store data in the session using the request.session dictionary:


def set_session(request):
    request.session['username'] = 'John'
    request.session['user_id'] = 35
    return HttpResponse("Session data set!")

2. Get Session Data

Retrieve data from the session:


def get_session(request):
    username = request.session.get('username', 'Guest')  # Default to 'Guest' if not set
    return HttpResponse(f"Hello, {username}!")

3. Delete Session Data

Remove specific session data:


def delete_session(request):
    if 'username' in request.session:
        del request.session['username']
    return HttpResponse("Username removed from session.")

Example: Login System with Sessions


from django.http import HttpResponse, HttpResponseRedirect

def login(request):
    # Simulate user authentication
    username = request.POST.get('username')
    if username == "admin":
        request.session['is_logged_in'] = True
        request.session['username'] = username
        return HttpResponseRedirect('/dashboard/')
    return HttpResponse("Invalid login.")

def dashboard(request):
    if request.session.get('is_logged_in', False):
        username = request.session.get('username')
        return HttpResponse(f"Welcome, {username}!")
    return HttpResponseRedirect('/login/')

def logout(request):
    request.session.flush()  # Clear all session data
    return HttpResponse("Logged out.")

Django with Bootstrap

Using Bootstrap in Django is straightforward and enhances the design of your web application with minimal effort. Here’s a step-by-step guide:

1. Include Bootstrap in Your Project

Option 1: Use a CDN (Recommended)

You can directly include Bootstrap via a CDN in your Django templates.

Add the following <link> and <script> tags to your base template (e.g., base.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Django App</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>

    <!-- Your content goes here -->

    <!-- Bootstrap JS Bundle -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Option 2: Install Bootstrap Locally

If you prefer local files, download Bootstrap from Bootstrap’s official website or install it using npm or yarn.

1. Download the CSS and JS files.

2. Place them in a static folder in your Django app


myapp/static/css/
myapp/static/js/

3. Link the files in your base.html template:

2. Set Up Django’s Static Files

Ensure your static files are configured in settings.py:


STATIC_URL = '/static/'
STATICFILES_DIRS = [
    BASE_DIR / "static",
]

3. Use Bootstrap in Your Templates

Now you can use Bootstrap classes in your Django templates. For example:


<form>
  <div class="mb-3">
    <label for="email" class="form-label">Email address</label>
    <input type="email" class="form-control" id="email" placeholder="name@example.com">
  </div>
  <div class="mb-3">
    <label for="password" class="form-label">Password</label>
    <input type="password" class="form-control" id="password">
  </div>
  <button type="submit" class="btn btn-primary">Submit</button>
</form>

Django Exceptions

In Django, an exception is an error that occurs during the execution of your application. Exceptions disrupt the normal flow of a program and can be caused by various issues such as invalid user input, database errors, or bugs in the code.

How Django Handles Exceptions

Django has a robust exception-handling system that:

1. Detects exceptions during the request/response cycle.

2. Provides error messages to developers (detailed error pages in debug mode).

3. Returns appropriate HTTP responses to users in production mode.

Common Types of Django Exceptions

There are some common exceptions

1. Http404

Raised when a requested resource (like a page or object) is not found.

Example:


from django.http import Http404

def my_view(request):
    if not some_condition:
        raise Http404("Page not found")

2. PermissionDenied

It is raised when a user doesn’t have permission to access a view or resource.

Example


from django.core.exceptions import PermissionDenied

def my_view(request):
    if not request.user.is_staff:
        raise PermissionDenied("You do not have permission to view this page.")

3. ValidationError

Raised when data doesn’t pass validation rules.

Example:


from django.core.exceptions import ValidationError

def validate_positive(value):
    if value < 0:
        raise ValidationError(f"{value} is not a positive number.")

4. ObjectDoesNotExist

Raised when a database query doesn’t find a matching object.

Example:


from django.shortcuts import get_object_or_404
from myapp.models import MyModel

def my_view(request, id):
    obj = get_object_or_404(MyModel, id=id)
    return obj

5. SuspiciousOperation

Raised when the framework detects potentially malicious activity (e.g., invalid input, tampered cookies).

Example:


from django.core.exceptions import SuspiciousOperation

def my_view(request):
    if "bad_input" in request.GET:
        raise SuspiciousOperation("Invalid request.")

Django Middleware

Django Middleware is a framework component in Django that processes requests and responses globally. Middleware acts as a layer of hooks or logic that is executed during the request and response cycle, allowing you to add functionality that applies across your entire application.

How Middleware Works

When a request comes into a Django application:

1. Middleware processes the request before it reaches the view.

2. After the view processes the request, middleware processes the response before it is sent back to the client.

What Middleware Can Do

Session Management: Manage user sessions across requests.

Authentication: Check if a user is logged in and authorized.

Logging: Record requests and responses for debugging or analytics.

Security Enhancements: Add headers like X-Frame-Options or Content-Security-Policy.

Custom Logic: Add application-specific behavior, like modifying responses or tracking user activity.

Type of Middleware

Django provides several middleware options

SecurityMiddleware: Adds security-related headers.

SessionMiddleware: Enables session management.

AuthenticationMiddleware: Associates users with requests.

CommonMiddleware: Provides various utilities like handling www. prefixes or trailing slashes.

How to create Custom Middleware

Step 1: Create the Middleware

1. Inside your app (e.g., myapp), create a new file called middleware.py.

2. Add the following code:


# myapp/middleware.py

class LogURLMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # This code runs before the view is called
        print(f"Request URL: {request.path}")

        # Call the next middleware or view
        response = self.get_response(request)

        # This code runs after the view is called
        return response

Step 2: Add Middleware to settings.py

In your project’s settings.py, add the middleware class to the MIDDLEWARE list:


MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # Add your custom middleware here
    'myapp.middleware.LogURLMiddleware',
]

Step 3: Run the Server

Start the Django development server:


python manage.py runserver

When you visit any URL, Django will print the requested URL in the terminal. For example:


Request URL: /
Request URL: /about/
Request URL: /contact/

Django Database Connectivity

Django supports multiple relational database systems such as SQLite, MySQL, PostgreSQL, and Oracle. By default, Django comes preconfigured to use SQLite, but you can easily configure it to connect to other databases.

There are some Steps for Django Database Connectivity

1. Install Database Driver

To connect to a database like MySQL or PostgreSQL, you need to install the respective driver:

SQLite: No additional installation is required (default).

MySQL: Install mysqlclient:


pip install mysqlclient

PostgreSQL: Install psycopg2


pip install psycopg2

2. Configure the Database in settings.py

Update the DATABASES setting in your settings.py file. The configuration depends on the database engine you’re using.

SQLite (default):


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

MySQL:


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'your_database_name',
        'USER': 'your_username',
        'PASSWORD': 'your_password',
        'HOST': 'localhost',  # Use '127.0.0.1' or the IP address of your database server
        'PORT': '3306',       # Default MySQL port
    }
}

PostgreSQL:


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_database_name',
        'USER': 'your_username',
        'PASSWORD': 'your_password',
        'HOST': 'localhost',  # Use '127.0.0.1' or the IP address of your database server
        'PORT': '5432',       # Default PostgreSQL port
    }
}

3. Create Models

Define your database schema in Django models. Each model corresponds to a database table.


# models.py
from django.db import models

class Blog(models.Model):
    title = models.CharField(max_length=100)
    message = models.TextField()  # A large text column

    def __str__(self):
        return self.title

4. Apply Migrations

Django uses migrations to apply model changes to the database.

1. Create Migrations:


python manage.py makemigrations

2. Apply Migrations:


python manage.py migrate

5. Interact with the Database

1. Using the Django Shell: You can interact with the database directly using Django’s ORM (Object-Relational Mapping).


python manage.py shell

# Import the model
from myapp.models import Blog

# Create a new record
blog = Blog(name="How to learn python", message="It's very easy and you can follow my website removeload.com")
blog.save()

# Query records
all_blogs = Blog.objects.all()
print(all_blogs)

# Update a name
blog.name = "Learn Python"
blog.save()

# Delete a record
blog.delete()

Django File upload

Django provides a straightforward way to handle file uploads through its forms and models framework. File uploads in Django are managed using the FileField or ImageField fields, along with configurations in settings for file storage.

Steps for File Upload in Django

1. Configure Settings

Add the MEDIA_URL and MEDIA_ROOT settings in your settings.py file to specify where uploaded files will be stored:


# settings.py

MEDIA_URL = '/media/'  # URL to access media files in development
MEDIA_ROOT = BASE_DIR / 'media'  # Directory to store uploaded files

2. Create a File Upload Form

Create a form with a FileField or ImageField to handle file inputs.


# forms.py
from django import forms

class FileUploadForm(forms.Form):
    file = forms.FileField(label="Select a file")

3. Create a Model for File Upload

If you want to save uploaded files in the database, define a model with a FileField or ImageField.


# models.py
from django.db import models

class UploadedFile(models.Model):
    name = models.CharField(max_length=255)
    file = models.FileField(upload_to='uploads/')  # Files will be saved in MEDIA_ROOT/uploads/
    uploaded_at = models.DateTimeField(auto_now_add=True)

4. Create a View to Handle File Upload

The view processes the uploaded file and saves it to the specified location.


# views.py
from django.shortcuts import render
from .forms import FileUploadForm

def file_upload_view(request):
    if request.method == 'POST':
        form = FileUploadForm(request.POST, request.FILES)
        if form.is_valid():
            uploaded_file = form.cleaned_data['file']
            # Save the file or process it as needed
            with open(f'media/uploads/{uploaded_file.name}', 'wb+') as destination:
                for chunk in uploaded_file.chunks():
                    destination.write(chunk)
            return render(request, 'upload_success.html', {'file_name': uploaded_file.name})
    else:
        form = FileUploadForm()

    return render(request, 'file_upload.html', {'form': form})

Save files into the Database (Using the model)


# views.py
from django.shortcuts import render
from .models import UploadedFile
from .forms import FileUploadForm

def file_upload_view(request):
    if request.method == 'POST':
        form = FileUploadForm(request.POST, request.FILES)
        if form.is_valid():
            uploaded_file = UploadedFile(
                name=request.FILES['file'].name,
                file=request.FILES['file']
            )
            uploaded_file.save()
            return render(request, 'upload_success.html', {'file_name': uploaded_file.file.url})
    else:
        form = FileUploadForm()

    return render(request, 'file_upload.html', {'form': form})

5. Create a Template for File Upload

Create an HTML form to allow users to upload files.


#file_upload.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
    <title>File Upload</title>
</head>
<body>
    <h1>Upload a File</h1>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Upload</button>
    </form>
</body>
</html>

6. Configure URL Patterns

Add the URL patterns to serve uploaded files during development.


# urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from . import views

urlpatterns = [
    path('upload/', views.file_upload_view, name='file_upload'),
]

if settings.DEBUG:  # Serve media files in development
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Django Static Files Handling

Static files in Django refer to files like CSS, JavaScript, images, and fonts that are served to the client as-is. Django provides a powerful framework for managing and serving these files during both development and production.

How Static Files Work in Django

1. Static Files in Development:

Django’s built-in development server automatically serves static files without requiring additional configuration.

Files are located in the STATICFILES_DIRS directory or app-specific static/ directories.

2. Static Files in Production:

Static files are collected into a single directory using the collectstatic command.


python manage.py collectstatic

These files are served by a web server like Nginx or Apache.

How to Configure Static Files?

1. Directory Structure

The typical structure for static files in a Django project:


myproject/
    ├── myapp/
    │   ├── static/
    │   │   └── myapp/
    │   │       ├── css/
    │   │       │   └── styles.css
    │   │       ├── js/
    │   │       │   └── script.js
    │   │       └── images/
    │   │           └── logo.png
    ├── static/
    │   └── global/
    │       └── css/
    │           └── global_styles.css
    ├── settings.py

2. Settings

In your settings.py, configure the static files:


# settings.py

# URL for accessing static files
STATIC_URL = '/static/'

# Directories for collecting static files (development)
STATICFILES_DIRS = [
    BASE_DIR / "static",  # Your global static files
]

# Directory where collected static files are stored (production)
STATIC_ROOT = BASE_DIR / "staticfiles"

Adding Static Files to Templates

To use static files in templates, load the static template tag and reference the files.


{% load static %}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{% static 'global/css/global_styles.css' %}">
    <title>Static Files Example</title>
</head>
<body>
    <h1>Welcome to Django Static Files</h1>
    <img src="{% static 'myapp/images/logo.png' %}" alt="Logo">
    <script src="{% static 'myapp/js/script.js' %}"></script>
</body>
</html>

Django Form Validations

Form validation in Django ensures that the data submitted by users is clean, meets the expected format, and adheres to any specific rules or constraints defined for the fields. This process helps maintain the integrity of your application’s data and improves user experience by providing meaningful error messages when invalid data is entered.

Types of Validations

1. Predefined Validations

These validations come predefined with Django and are automatically applied based on the field type.

Example:


from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(
        max_length=100, 
        required=True,  # Ensures the field is not empty
        label="Name"
    )
    email = forms.EmailField(
        required=True, 
        label="Email"
    )
    age = forms.IntegerField(
        min_value=18,  # Ensures the value is at least 18
        max_value=100, # Ensures the value is at most 100
        required=True, 
        label="Your Age"
    )
    message = forms.CharField(
        widget=forms.Textarea, 
        required=False,  # Optional field
        label="Your Message"
    )

2. Custom Field-Level Validations

You can define custom logic for validating individual fields by creating a clean_<fieldname> method. This method is called during the validation process for that specific field.


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label="Name")
    email = forms.EmailField(label="Your Email")
    age = forms.IntegerField(min_value=18, label="Age")
    message = forms.CharField(widget=forms.Textarea, required=False, label="Your Message")

    def clean_email(self):
        email = self.cleaned_data.get('email')
        if not email.endswith('@example.com'):
            raise forms.ValidationError("Email must be from the domain '@example.com'.")
        return email

Note:- clean_email: This method checks if the email ends with @example.com. If not, it raises a ValidationError.

3. Custom Form-Level Validations

You can override the clean() method to validate the relationships between multiple fields or apply complex validation logic.


class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label="Name")
    email = forms.EmailField(label="Email")
    confirm_email = forms.EmailField(label="Confirm Email")
    age = forms.IntegerField(min_value=18, label="Your Age")
    message = forms.CharField(widget=forms.Textarea, required=False, label="Your Message")

    def clean(self):
        cleaned_data = super().clean()
        email = cleaned_data.get('email')
        confirm_email = cleaned_data.get('confirm_email')

        if email != confirm_email:
            raise forms.ValidationError("Emails do not match!")
        
        return cleaned_data

Customize Error Messages

To customize error messages for fields, use the error_messages parameter.

Example:


class ContactForm(forms.Form):
    name = forms.CharField(
        max_length=100, 
        label="Your Name",
        error_messages={
            'required': 'Name is required!',
            'max_length': 'Name cannot exceed 100 characters.',
        }
    )
    email = forms.EmailField(
        label="Your Email",
        error_messages={
            'required': 'Email is required!',
            'invalid': 'Enter a valid email address.',
        }
    )

Rendering Errors in Templates

In your template, you can display form errors like this:


<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    {% for field in form %}
        {% if field.errors %}
            <ul>
                {% for error in field.errors %}
                    <li style="color: red;">{{ error }}</li>
                {% endfor %}
            </ul>
        {% endif %}
    {% endfor %}
    <button type="submit">Submit</button>
</form>

Django Forms

Django Forms are a powerful way to handle user input in web applications. They provide tools to generate HTML forms, validate data, and even save it to a database. A form in Django can be a class that defines the fields you want to display and the rules you want to enforce.

Characteristics

1. Automatic HTML Generation: Django generates HTML forms based on form classes.

2. Validation: Django provides built-in validations like required fields, email formats, and custom validations.

3. Integration with Models: Django can link forms directly to models (ModelForm), simplifying CRUD operations.

Example: Create a Contact Form


# forms.py
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(max_length=150, label="Name")
    email = forms.EmailField(label="Email")
    message = forms.CharField(widget=forms.Textarea, label="Message")

Explanation

CharField: A text input field.

EmailField: A field that validates email format.

widget=forms.Textarea: Customizes the message field to render as a <textarea> instead of a single-line input.

Use Contact Form in View


# views.py
from django.shortcuts import render
from .forms import ContactForm

def contact_view(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Process the form data
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            message = form.cleaned_data['message']
            return render(request, 'thank_you.html', {'name': name})
    else:
        form = ContactForm()
    return render(request, 'contact.html', {'form': form})

Call Contact Form View into Template


#contact.html
<!DOCTYPE html>
<html>
<head>
    <title>Contact Us</title>
</head>
<body>
    <h1>Contact Us</h1>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Submit</button>
    </form>
</body>
</html>

Explanation

{{ form.as_p }}: Renders the form fields wrapped in <p> tags.

{% csrf_token %}: Adds CSRF protection to the form.

Django Delete Data

In Django, deleting data from the database involves removing records (rows) from the corresponding table. The Django ORM provides methods like .delete() to handle this easily, either for a single record or multiple records.

Delete a Single Record

You retrieve the record you want to delete and call the .delete() method on it.


from myapp.models import Book  # Replace 'myapp' with your app name

# Retrieve a book by its ID
book = Book.objects.get(id=1)

# Delete the record
book.delete()

Explanation:

1) get(id=1) fetches the book with the primary key (ID) 1.

2) .delete() removes this record from the database.

Delete Multiple Records

If you want to delete multiple records, use the .filter() method to select the records and call .delete() on the queryset.

Example:


from myapp.models import Book

# Delete all books published before 2000
Book.objects.filter(published_year__lt=2000).delete()

Explanation:

1. filter(published_year__lt=2000) selects all books published before the year 2000.

2. .delete() removes all matching records.

Delete All Records in a Table

To delete all records from a table (be cautious as this cannot be undone):

Example:


from myapp.models import Book

# Delete all records in the Book table
Book.objects.all().delete()

Summary

Delete a single record: Use get() and .delete().

Delete multiple records: Use filter() and .delete().

Delete all records: Use all() and .delete().

Deletions are permanent and immediate, so always double-check before running .delete().

Django update Data

In Django, updating data in the database involves modifying existing records in a table. This can be done using the Django ORM methods like save(), update(), or filtering records and updating them.

Update a Single Record

You retrieve the record, modify its fields, and call .save().

Example:


from myapp.models import Book  # Replace 'myapp' with your app name

# Retrieve a book by its ID (or any other filter condition)
book = Book.objects.get(id=1)

# Modify the fields
book.title = "Updated Title"
book.author = "Updated Author"

# Save the changes to the database
book.save()

Explanation:

  • get(id=1) retrieves the book with the primary key 1.
  • Fields title and author are updated.
  • The .save() method writes the changes back to the database.

Update Multiple Records

If you want to update multiple records at once, use the .filter() method combined with .update().

Example:


from myapp.models import Book

# Update all books published before 2000
Book.objects.filter(published_year__lt=2000).update(author="New Author")

Explanation:

1. filter(published_year__lt=2000) selects books published before the year 2000.

2. update(author=”New Author”) changes the author field for all matching records.

Updating multiple Data through bulk_update

If you need to update multiple objects’ fields at once:


from django.db import models
from myapp.models import Book

# Get all books with the 'Unknown' author
books = Book.objects.filter(author="Unknown")

# Modify the records in memory
for book in books:
    book.published_year = 2023

# Bulk update the `published_year` field for all books
Book.objects.bulk_update(books, ['published_year'])

Updating multiple Data through save()

For bulk updates (updating multiple fields of multiple records efficiently), Django requires iterating through each record and calling .save():


books = Book.objects.filter(author="Unknown")  # Fetch books with 'Unknown' author

# Update each book
for book in books:
    book.published_year = 2023  # Update field
    book.save()  # Save changes

Summary

Use get() and .save() to update a single record.

Use filter() and .update() for conditional or bulk updates.

Use bulk_update() for efficiently updating specific fields of many records.

Django Insert Data

In Django, inserting data into the database involves creating an object (instance) of a model and saving it. This process writes the object’s data into the corresponding table in the database.

Create a Django Model Book


from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200)  # Title of the book
    author = models.CharField(max_length=100)  # Author name
    published_year = models.IntegerField()     # Year the book was published

    def __str__(self):
        return self.title

How to Insert Data into the Database

Using Django Shell

1. Open the Django shell:


python manage.py shell

2. Import the model:


from myapp.models import Book  # Replace 'myapp' with your app name

3. Create and save a new book instance:


# Create a book instance
new_book = Book(
    title="A Suitable Boy",
    author="Vikram Seth",
    published_year=1993
)

# Save the book to the database
new_book.save()

Now, the new book is saved in the database.

Using create() Method

Instead of creating and saving separately, you can use the create() method, which does both in one step:


Book.objects.create(
    title="The White Tiger",
    author="Aravind Adiga",
    published_year=2008
)

Insert Multiple Data

1. Using bulk_create() for Multiple Inserts

The bulk_create() method is used to insert multiple records at once. It’s faster than saving each instance individually because it sends a single query to the database.

Example:


from myapp.models import Book  # Replace 'myapp' with your app name

# Create a list of Book instances (do not save yet)
books = [
    Book(title="The Inheritance of Loss", author="Kiran Desai", published_year=2007),
    Book(title="The Guide", author="R. K. Narayan", published_year=1958),
    Book(title="The Palace of Illusions", author="Chitra Banerjee Divakaruni", published_year=2008),
]

# Use bulk_create to insert all the books at once
Book.objects.bulk_create(books)

Explanation:

A list of Book instances is created but not saved.

bulk_create() inserts all the instances in one database query, which is efficient.

2. Using Loops to Insert Multiple Records

If you need custom logic for each instance, you can use a loop with .save(). However, this sends one query per record, which is slower for large datasets.

Example:


from myapp.models import Book  # Replace 'myapp' with your app name

# List of data to insert
book_data = [
    {"title": "The Inheritance of Loss", "author": "Kiran Desai", "published_year": 2007},
    {"title": "The Guide", "author": "R. K. Narayan", "published_year": 1958},
    {"title": "The Palace of Illusions", "author": "Chitra Banerjee Divakaruni", "published_year": 2008},
]

# Loop through the data and save each record
for data in book_data:
    Book.objects.create(**data)

Explanation:

The create() method saves each record one at a time.

**data unpacks the dictionary keys into model fields.

Django Model

A Django Model is a Python class used in the Django framework to define your application’s data structure and behavior. Models represent the database schema and act as the interface for interacting with your database. They allow you to perform CRUD (Create, Read, Update, Delete) operations easily and abstract the complexities of raw SQL queries.

Important Features of Django Models

1. Data Representation:

Each model represents a single table in the database.

Each attribute of the model corresponds to a column in the table.

2. Automatic Table Creation:

Django automatically creates a database table for each model when you run database migrations.

3. Field Types:

Django provides various field types (e.g., CharField, IntegerField, DateField) to define the type of data stored in each column.

Example:


from django.db import models
# This defines your table
class User(models.Model):
    name= models.CharField(max_length=200)  # A text column with max 100 characters
    address= models.TextField()  # A large text column
    created_at = models.DateTimeField(auto_now_add=True) # A column for date and time

 # A method to show a user name
  def __str__(self):
        return self.name

Process flow

1. When you create a model like this:

  • Django understands it as a database table.
  • The table will be called something like appname_user.

2. Attributes like name, address, and created_at become database columns with specific types:

  • CharField becomes a short text column.
  • TextField becomes a large text column.
  • DateTimeField is for storing date and time.

3. Django will create the table automatically when you run:


python manage.py makemigrations
python manage.py migrate

How to use a Model in your App?

You can add or retrieve user like this:

Adding a User:


# Import the model
from myapp.models import User

# Create a new user 
new_user = User(name="John", address="47 W 13th St, New York", published_date="2024-01-01 10:00")
new_user.save()  # Save to the database

Retrieving a User:


# Get all Users
all_users = User.objects.all()

# Print each title
for user in all_users :
    print(user.name)

Django Templates

A Django Template is a text-based file that defines the structure and layout of content (HTML, plain text, XML, etc.) to be presented to the user. Templates are a key part of Django’s MVT (Model-View-Template) architecture, allowing you to separate presentation logic from business logic.

Important Features of Django Templates

1. Dynamic Content: Use placeholders to insert data passed from views.

2. Template Tags: Add logic like loops, conditions, and filters to templates.

3. Reusable: Templates can include other templates and use common layouts.

4. Extensible: Use custom template tags and filters for advanced functionality.

Setting Up Django Templates

1. Create a Template Directory

By default, Django looks for templates in a templates directory. Create it in your app or project folder:


myproject/
    myapp/
        templates/
            myapp/
                home.html

2. Configure Template Directory in Settings

In settings.py, add the templates directory to the DIRS option:


TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / "templates"],  # Global template directory
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

Creating a Basic Template

1. Create a template file, e.g., templates/myapp/home.html:


<!DOCTYPE html>
<html>
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h1>{{ heading }}</h1>
    <p>{{ message }}</p>
</body>
</html>

2. Pass data to the template from the view:


from django.shortcuts import render

def home(request):
    context = {
        'title': 'Home Page',
        'heading': 'Welcome to Django Templates!',
        'message': 'Django makes web development easy.',
    }
    return render(request, 'myapp/home.html', context)

3. Map the view to a URL in urls.py:


from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
]

Template Syntax

1. Variables

Variables are enclosed in {{ }}.

Example:


Welcome, {{ username }}!

2. Template Tags

Tags are enclosed in {% %}.

Example: Loops


<ul>
    {% for item in items %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>

Example: Conditions


{% if user.is_authenticated %}
    <p>Welcome back, {{ user.username }}!</p>
{% else %}
    <p>Please log in.</p>
{% endif %}

3. Filters

Filters modify variables.

Syntax: {{ variable|filter_name }}.

Example:


<p>Uppercase: {{ name|upper }}</p>
<p>Default: {{ greeting|default:"Hello" }}</p>

4. Include Other Templates

Example:


{% include 'myapp/navbar.html' %}

Django URLs

In Django, URLs are used to define the paths through which users can access different parts of your web application. The URLconf (URL configuration) maps specific URL patterns to corresponding views, which handle the request and return a response.

How Django URL Mapping Works?

1. User Request: A user visits a URL like http://abc.com/home.

2. URLconf: Django matches the URL against patterns defined in urls.py.

3. View: The matched pattern invokes the corresponding view.

4. Response: The view processes the request and returns a response (HTML, JSON, etc.).

URL Syntax:

Django uses the path() function to define URL patterns.


path(route, view, kwargs=None, name=None)

route: The URL pattern to match (e.g., ‘about/’).

view: The view function/class to handle the request.

kwargs: Optional parameters passed to the view.

name: A unique name to refer to the URL (useful in templates and redirects).

How to create Django URLs?

There are two types of creation of Django URLs.

1. Project-Level URLs

When you create a Django project, a urls.py file is generated in the project folder (e.g., myproject/urls.py). This file routes top-level URLs and includes app-specific URLs.

Example (myproject/urls.py):


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),          # Admin site
    path('myapp/', include('myapp.urls')),    # Include URLs from an app
]

2. App-Level URLs

Each app can have its own urls.py to defining app-specific routes.

Example (myapp/urls.py):


from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),  # Root URL of the app
    path('about/', views.about, name='about'),
]

How to create Dynamic URLs?

Django URLs can capture dynamic values using <type:variable> syntax.
<int:variable>: Matches an integer (e.g., /article/2/).
<str:variable>: Matches a string (default).

Example:


from django.urls import path
from . import views

urlpatterns = [
    path('article/<int:id>/', views.article, name='article'),  # Example: /article/2/
    path('user/<str:username>/', views.user_profile, name='user_profile'),  # Example: /user/john/
]

Django View

A Django view is a Python function or class that handles the logic for a specific web request. When a user accesses a URL, the corresponding view is called to process the request and return an appropriate response.

What is the Role of View?

Views act as the bridge between the user request and the data or response.

Process:

  • It fetches data from the database (using models).
  • Processes the data.
  • Returns a response (HTML, JSON, file, etc.).

Types of Views in Django

1. Function-Based Views

Views written as Python functions.

Flexible and straightforward for simple tasks.

Example:


from django.http import HttpResponse
def hello_world(request):
    return HttpResponse("Hello, world!")

2. Class-Based Views

Views written as Python classes.

Built-in features like inheritance, mixins, and reusable methods.

Example:


from django.http import HttpResponse
from django.views import View

class HelloWorldView(View):
    def get(self, request):
        return HttpResponse("Hello, world!")

Create a Simple View

1. Open views.py in your Django app.

2. Define a function-based view:


from django.http import HttpResponse

def home(request):
    return HttpResponse("Welcome to the home page!")

3. Map it to a URL in urls.py:


from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='home'),
]

Rendering Templates in Views

Django views often render HTML templates using the render() function:

Example:


from django.shortcuts import render

def home(request):
    context = {'title': 'Home', 'message': 'Welcome to Django!'}
    return render(request, 'home.html', context)

Class-Based Views (CBVs)

Django provides pre-built generic CBVs for common tasks:

TemplateView: Renders a template.

ListView: Displays a list of objects.

DetailView: Shows details for a single object.

Example:


from django.views.generic import TemplateView

class HomeView(TemplateView):
    template_name = 'home.html'

# In urls.py
from django.urls import path
from .views import HomeView

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
]

Create Django app

Creating an app in Django is a straightforward process. Here’s a step-by-step guide:

1. Set Up Your Django Project (If Not Already Done)

Ensure you have a Django project set up. If you don’t, create one:


django-admin startproject myproject
cd myproject

Note: Replace myproject with your desired project name.

2. Create a New Django App

Run the following command to create a new app:


python manage.py startapp myapp

Replace myapp with the name of your app. This will create a folder structure like:


myapp/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

3. Register the App in the Project

Add your new app to the INSTALLED_APPS section in your project’s settings.py:


INSTALLED_APPS = [
    # Default apps...
    'myapp',  # Add your app here
]

4. Define Models (Optional)

Open models.py in your app and define your data models:


from django.db import models

class ExampleModel(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()

    def __str__(self):
        return self.name

Run the following commands to create and apply migrations for your model:


python manage.py makemigrations
python manage.py migrate

5. Create Views

Define a view in views.py


from django.http import HttpResponse

def hello_world(request):
    return HttpResponse("Hello, world!")

6. Configure URLs

Create a urls.py file in your app if it doesn’t exist, and add your view:


from django.urls import path
from . import views

urlpatterns = [
    path('', views.hello_world, name='hello_world'),
]

In your project’s main urls.py, include the app’s URLs:


from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('myapp/', include('myapp.urls')),  # Include app URLs
]

7. Run the Server

Start the development server:


python manage.py runserver

Visit http://127.0.0.1:8000/myapp/ in your browser to see your app in action.

What is a Django App

A Django app is a self-contained module within a Django project that is designed to handle specific functionality. It can be thought of as a reusable component that performs a particular set of operations, such as managing blog posts, user authentication, or e-commerce cart systems.

Important Concept of Django App

1. Modularity:

Each app is designed to handle a single aspect of your project.

Apps can be reused across multiple Django projects.

2. Directory Structure: When you create an app with the startapp command, Django generates a structure like this:


myapp/
    __init__.py      # Marks the folder as a Python package.
    admin.py         # Register models in Django admin interface.
    apps.py          # App-specific configuration.
    migrations/      # Database migrations for this app.
        __init__.py
    models.py        # Database models.
    tests.py         # Unit tests for this app.
    views.py         # Logic to handle requests and responses.

3. Independence:

Apps are designed to be self-contained.

For example, you can create a “polls” app to manage polls, which can be plugged into different Django projects.

4. Connected to the Project:

A Django project is the overall container for your apps.

You register apps in the INSTALLED_APPS list in the project’s settings.py.

5. Apps and URLs:

Each app typically has its own urls.py file to define routes (URLs) specific to that app.

The project’s urls.py file includes all app URLs.

6. Scalability:

Breaking functionality into multiple apps makes large projects easier to scale and maintain.

For instance, you could have separate apps for blog, authentication, payment, and reporting in one project.

Why Use Multiple Apps?

Separation of Concerns: Each app focuses on a specific area of functionality.

Reusability: You can extract and reuse an app in another project.

Collaboration: Teams can work on different apps simultaneously without conflicts.

Easier Maintenance: Debugging and updating is simpler when functionality is isolated.

Create Django Project

Creating a Django project involves a few simple steps. Here’s a guide to help you get started:

Step 1: Ensure Django is Installed

Make sure Django is installed on your system. Check by running:


python -m django --version
If Django is not installed, follow the URL

Step 2: Create a New Project

Run the following command to create a new Django project:


django-admin startproject project_name

Replace project_name with the name of your project like myproject.

This creates a directory named project_name containing the following structure:


project_name/
    manage.py
    project_name/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py

manage.py: A command-line utility for interacting with the project.

settings.py: Configuration settings for the project.

urls.py: URL declarations for routing.

asgi.py: Entry point for ASGI applications (for asynchronous features).

wsgi.py: Entry point for WSGI applications (for traditional web servers).

Step 3: Navigate to the Project Directory

Change to the project directory:


cd project_name

Step 4: Start the Development Server

Run the following command to start Django’s built-in development server:


python manage.py runserver

The server will start, and you’ll see output like this:


Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Open a web browser and go to:


http://127.0.0.1:8000/

You should see the default Django welcome page, which confirms that the project was created successfully.

Install Django

Installing Django is a straightforward process. Here’s a step-by-step guide to get Django up and running on your system:

Step 1: Install Python

1. Ensure Python is installed on your system. Django requires Python 3.8 or higher.

2. Check if Python is already installed


python --version

or


python3 --version

3. If Python isn’t installed, download it from the official Python website and follow the installation instructions.

Step 2: Set Up a Virtual Environment (Recommended)

A virtual environment isolates your project’s dependencies, avoiding conflicts with other projects.

1. Install the venv module if it’s not already installed:


python -m ensurepip --upgrade

2. Create a virtual environment:


python -m venv mydata

Replace mydata with your preferred name for the environment.

3. Activate the virtual environment:

On Windows:


mydata\Scripts\activate

On Mac/Linux:


source mydata/bin/activate

4. Your terminal prompt should change to indicate the virtual environment is active.

Step 3: Install Django

With your virtual environment activated, use pip to install Django.

1. Install the latest version of Django:


pip install django

2. Verify the installation: This will display the installed version of Django.


python -m django --version

Django architecture

The Model-View-Template (MVT) architecture is a core design pattern in Django. It is similar to the Model-View-Controller (MVC) pattern used in other frameworks but adapted to Django’s design philosophy. Here’s an overview of its components:

1. Model

Represents the data layer of the application.

It defines the structure and behavior of the data (e.g., database tables) through Python classes.

Django’s Object-Relational Mapping (ORM) allows seamless interaction with the database using Python code.

Example responsibilities:

  • Define database schema.
  • Perform data validations.
  • Implement business logic related to the data.

Example:


from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    description = models.TextField()

2. View

Represents the business logic and controls how data is presented to the user.

It handles HTTP requests, processes data (via Models), and passes the appropriate data to the Template.

Views can return:

  • HTML responses (rendered with a Template).
  • JSON responses (e.g., for APIs).
  • Other HTTP responses (e.g., redirects, errors).

Example:


from django.shortcuts import render
from .models import Product

def product_list(request):
    products = Product.objects.all()
    return render(request, 'product_list.html', {'products': products})

3. Template

Represents the presentation layer.

It is responsible for defining how the data sent by the View is displayed to the user.

Uses Django’s templating language, which supports:

  • Variable rendering.
  • Control structures (loops, conditionals).
  • Template inheritance for reusability.

Example:


<!DOCTYPE html>
<html>
<head>
    <title>Product List</title>
</head>
<body>
    <h1>Products</h1>
    <ul>
        {% for product in products %}
            <li>{{ product.name }} - ${{ product.price }}</li>
        {% endfor %}
    </ul>
</body>
</html>

Django Benefits

Django is a high-level Python web framework known for its simplicity and robust features. Here are the key benefits of using Django:

1. Rapid Development

Django’s built-in tools and features, like the admin interface, ORM (Object-Relational Mapping), and pre-built templates, allow developers to quickly build web applications without starting from scratch.

2. Batteries-Included Framework

It includes built-in modules for common functionalities such as authentication, URL routing, database interaction, and session management. This reduces the need for third-party libraries.

3. Scalability

Django is designed to handle high traffic and large-scale applications. Its modular architecture allows you to scale different components (e.g., database, caching) independently.

4. Security

Django has robust built-in security features, including protection against common vulnerabilities like SQL injection, cross-site scripting (XSS), and cross-site request forgery (CSRF).

5. Versatile Framework

Django is suitable for a variety of applications, including e-commerce platforms, content management systems (CMS), social networks, and machine learning platforms.

6. ORM for Database Management

The Django ORM allows developers to interact with the database using Python instead of writing raw SQL queries, simplifying database operations.

7. Large Community and Documentation

Django has an active community that provides extensive documentation, tutorials, and third-party packages to enhance functionality.

8. SEO-Friendly

Its ability to dynamically manage URLs makes Django applications more SEO-friendly by allowing human-readable and customizable URLs.

9. Highly Customizable

Although Django comes with many built-in features, it is flexible enough to allow developers to override or extend its functionality as needed.

10. Asynchronous Capabilities

Recent updates to Django introduced support for asynchronous views, making it possible to handle tasks like WebSockets and long-running requests efficiently.

11. REST Framework Integration

Django REST Framework (DRF) is a powerful extension for building RESTful APIs, making Django a great choice for backend services in modern web and mobile applications.

12. Built-In Admin Interface

Django’s admin panel allows easy management of application data without requiring custom development, especially useful for quick project setups.

What is Django

Django is a high-level Python web framework that simplifies the process of building robust, scalable, and secure web applications. It is designed to help developers take applications from concept to completion as quickly as possible while adhering to best practices.

Key Features of Django

1. Rapid Development

Django provides tools and libraries that allow developers to build web applications quickly without having to reinvent the wheel.

2. MVC Architecture

Django uses a Model-View-Template (MVT) architecture:

  1. Model: Manages the data and database schema.
  2. View: Handles the business logic and user requests.
  3. Template: Defines how the data is presented to the user.

3. Scalability: Django is designed to handle large and complex web applications efficiently.

4. Security

Django includes built-in protection against common security threats like:

  1. SQL injection.
  2. Cross-site scripting (XSS).
  3. Cross-site request forgery (CSRF).
  4. Clickjacking.

5. Versatility

Django is suitable for building all types of websites, from small projects to large-scale systems, including:

  1. Content management systems (CMS).
  2. Social media platforms.
  3. E-commerce websites.
  4. APIs using Django REST Framework (DRF).

6. Built-in Admin Interface

Django provides an automatically generated admin interface for managing application data, making it easier to test and manage the application during development.

7. Batteries Included

Django includes a wide range of features out of the box, such as authentication, URL routing, form handling, and more, reducing the need for external libraries.

8. Community Support

Django has an active community of developers and extensive documentation, making it beginner-friendly and well-supported.

JavaScript Fetch API

The Fetch API is a modern JavaScript interface used to make HTTP requests. It allows developers to retrieve resources (e.g., data from a server or a file) across the network. Fetch API is promise-based, making it a simpler and more flexible alternative to older methods like XMLHttpRequest.

Advantages of Fetch API

1. Simplifies asynchronous requests.

2. Built-in support for modern JavaScript features.

3. Cleaner and more readable than XMLHttpRequest.

How to Use Fetch API

1. Basic Syntax:


fetch(url, options)
    .then(response => {
        if (!response.ok) {
            throw new Error("Network response was not ok");
        }
        return response.json();
    })
    .then(data => console.log(data))
    .catch(error => console.error("Error:", error));

2. Making a GET Request:

The default method of fetch() is GET.


fetch("https://api.example.com/users")
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error("Error:", error));

3. Making a POST Request:

Include a method and body in the options object.


fetch("https://api.example.com/users", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
    },
    body: JSON.stringify({ name: "John", age: 35 }),
})
    .then(response => response.json())
    .then(data => console.log("Success:", data))
    .catch(error => console.error("Error:", error));

4. Handling Errors:

Fetch doesn’t reject the promise for HTTP errors like 404 or 500. You must manually check the response.ok property.


fetch("https://api.example.com/invalid-endpoint")
    .then(response => {
        if (!response.ok) {
            throw new Error("HTTP error " + response.status);
        }
        return response.json();
    })
    .catch(error => console.error("Error:", error));

JavaScript Async/Await

Async/Await in JavaScript is a modern syntax for handling asynchronous operations, introduced in ES2017 (also known as ES8). It simplifies working with promises, making asynchronous code easier to read and maintain by using a more synchronous-looking structure.

How Async/Await Works

Async Functions: An async function is a function declared with the async keyword. It always returns a promise, and within it, you can use the await keyword.


async function greetings() {
    return "Hello, Async!";
}

greetings().then(console.log); // Output: "Hello, Async!"

Await Keyword: The await keyword pauses the execution of the async function until the promise it is waiting for resolves. It can only be used inside async functions.


async function fetchData() {
    let response = await fetch("https://api.example.com/data");
    let data = await response.json();
    console.log(data);
}
fetchData();

Advantages

Improved Readability: Code written with async/await is more linear and easier to understand compared to nested .then() chains.

Error Handling: Errors can be handled more cleanly using try/catch blocks.


async function fetchData() {
    try {
        let response = await fetch("https://api.example.com/data");
        let data = await response.json();
        console.log(data);
    } catch (error) {
        console.error("Error fetching data:", error);
    }
}

Works Well with Promises: Async/await is built on top of promises, so it integrates seamlessly with existing promise-based APIs.

When to Use Async/Await

Use async/await for scenarios involving:

  1. Fetching data from APIs.
  2. Sequential asynchronous operations.
  3. Readable and maintainable code for complex async flows.

JavaScript Promises

JavaScript promises are a feature in the language used to handle asynchronous operations more effectively and avoid callback hell (nested callbacks). A promise represents a value that may be available now, in the future, or never. It acts as a placeholder for a result that will be returned later, allowing you to write cleaner and more manageable asynchronous code.

States of a Promise

1. Pending: The initial state; the promise is neither fulfilled nor rejected.

2. Fulfilled: The operation is completed successfully, and the promise has a resulting value.

3. Rejected: The operation failed, and the promise has a reason for the failure (an error).

Using Promises

A promise is created using the Promise constructor:


let promise = new Promise((resolve, reject) => {
    // Perform an operation
    let success = true; // Example condition
    if (success) {
        resolve("Operation was successful!");
    } else {
        reject("Operation failed.");
    }
});

You can handle the resolved or rejected outcomes using .then() and .catch():


promise.then((result) => {
        console.log(result); // "Operation was successful!"
    }).catch((error) => {
        console.error(error); // "Operation failed."
    });

Chaining Promises

Promises can be chained to handle sequences of asynchronous operations:


fetch("https://api.example.com/data")
    .then((response) => response.json())
    .then((data) => console.log(data))
    .catch((error) => console.error("Error:", error));

CSS Animations

CSS animations allow you to create smooth transitions and effects on web elements without relying on JavaScript. These animations involve defining keyframes for stages of the animation and applying properties to control its behavior.

Components of CSS Animation

1. Keyframes: Define the animation’s start, end, and intermediate steps.


@keyframes animation-name {
  0% { property: value; }
  50% { property: value; }
  100% { property: value; }
}

2. Animation Properties:

Apply to elements to control the animation’s behavior.

Property
Description
Example Value
animation-name
Specifies the keyframes to use
move
animation-duration
Sets how long the animation lasts.
2s, 500ms
animation-timing-function
Specifies the speed curve.
linear, ease
animation-delay
Delays the start of the animation.
1s
animation-iteration-count
Number of times the animation repeats.
1, infinite
animation-direction
Specifies if the animation reverses.
normal, alternate
animation-fill-mode
Applies styles before/after the animation.
forwards, backwards
animation
Combines all animation properties.
move 2s ease 1

Animation Properties

Example 1: Bouncing Box

HTML:


<div class="box"></div>

CSS:


.box {
  width: 100px;
  height: 100px;
  background-color: red;
  animation: bounce 2s infinite;
}

@keyframes bounce {
  0%, 100% { transform: translateY(0); }   /* Start and end at the original position */
  50% { transform: translateY(-50px); }   /* Move up */
}

Effect: The box moves up and down repeatedly.

Example 2: Fading Text

HTML:


<p class="fade-text">Hello World!</p>

CSS:


.fade-text {
  font-size: 24px;
  color: black;
  animation: fade 3s infinite;
}

@keyframes fade {
  0% { opacity: 1; }  /* Fully visible */
  50% { opacity: 0.5; } /* Semi-transparent */
  100% { opacity: 1; } /* Fully visible again */
}

Effect: The text fades in and out.

Example 3: Color Changing Button

HTML:


<button class="color-button">Click Me</button>

CSS


.color-button {
  padding: 10px 20px;
  font-size: 18px;
  border: none;
  cursor: pointer;
  animation: color-change 4s infinite alternate;
}

@keyframes color-change {
  0% { background-color: lightblue; }
  100% { background-color: lightcoral; }
}

Effect: The button smoothly transitions between light blue and light coral.

CSS Flexbox

CSS Flexbox (short for Flexible Box Layout) is a CSS layout model designed to distribute and align items within a container efficiently, even when their sizes are dynamic or unknown. It works in a single dimension (either a row or a column) and is particularly useful for creating responsive designs.

How Flexbox Works

1. Flex Container: A parent element with display: flex or display: inline-flex. This enables the flexbox behavior for its child elements.

2. Flex Items: The child elements inside the flex container. These are automatically arranged and aligned based on the container’s properties.

Example:

HTML:


<div class="flex-container">
  <div class="flex-item">1</div>
  <div class="flex-item">2</div>
  <div class="flex-item">3</div>
</div>

CSS:


.flex-container {
  display: flex; /* Enables Flexbox layout */
  justify-content: space-around; /* Distributes items horizontally with equal space around them */
  align-items: center; /* Aligns items vertically to the center */
  height: 200px; /* Example height to visualize alignment */
  background-color: lightgray;
}

.flex-item {
  background-color: coral;
  padding: 20px;
  text-align: center;
  color: white;
  border-radius: 5px;
}

Result: This creates a horizontally aligned layout where the items are evenly spaced (space-around) with vertical centering (align-items: center).

Example: Responsive Navigation Bar

HTML:


<nav class="flex-container">
  <div class="flex-item">Home</div>
  <div class="flex-item">About</div>
  <div class="flex-item">Services</div>
  <div class="flex-item">Contact</div>
</nav>

CSS:


.flex-container {
  display: flex;
  justify-content: space-between; /* Distributes items with space between them */
  align-items: center;
  background-color: navy;
  padding: 10px;
}

.flex-item {
  color: white;
  padding: 10px 20px;
  text-decoration: none;
  font-size: 18px;
}

Result:

This creates a responsive navigation bar where items are spaced evenly and aligned in the center.

Flexbox simplifies layout creation, making it an essential tool for modern web design.

CSS grid

CSS Grid is a powerful layout system in CSS designed for creating two-dimensional web layouts. It allows you to define rows and columns and place items within these grid structures with precision. CSS Grid makes it easier to create complex, responsive designs without the need for additional frameworks or extensive CSS workarounds.

Key Features of CSS Grid

Two-Dimensional Layout: It handles both rows and columns simultaneously, unlike Flexbox, which is primarily one-dimensional.

Explicit and Implicit Grids: You can define grid rows and columns explicitly or let the browser generate them implicitly as needed.

Responsive Design: It simplifies responsive design by allowing easy rearrangement of grid items using media queries or the auto-fit and auto-fill properties.

Grid Template Areas: You can define named grid areas to make layout definitions more readable and structured.

Syntax

Define a Grid Container

To use Grid, you need a container element with display: grid or display: inline-grid.


.container {
  display: grid;
  grid-template-columns: 100px 200px auto;
  grid-template-rows: 50px auto 100px;
}

Place Items

You can position child items using properties like grid-column and grid-row.


.item1 {
  grid-column: 1 / 3; /* Span from column 1 to column 3 */
  grid-row: 1 / 2;    /* Span from row 1 to row 2 */
}

Example:


<div class="grid-container">
  <div class="item1">1</div>
  <div class="item2">2</div>
  <div class="item3">3</div>
  <div class="item4">4</div>
</div>


CSS Media Queries

CSS media queries are a feature of CSS that allow you to apply styles conditionally based on the characteristics of the user’s device, such as screen size, resolution, orientation, or even color preferences. They are essential for creating responsive designs, ensuring websites look good and function well on a variety of devices (e.g., desktops, tablets, and smartphones).

What Are CSS Media Queries?

CSS media queries are a feature of CSS that allow you to apply styles conditionally based on the characteristics of the user’s device, such as screen size, resolution, orientation, or even color preferences. They are essential for creating responsive designs, ensuring websites look good and function well on a variety of devices (e.g., desktops, tablets, and smartphones).

Syntax

Media queries use the @media rule, followed by a condition and a block of CSS rules that apply if the condition is true.


@media (condition) {
  /* CSS rules go here */
}

Example: Basic Media Query


/* Default styles */
body {
  background-color: white;
  color: black;
}

/* Apply styles when the screen width is 768px or less */
@media (max-width: 768px) {
  body {
    background-color: black;
    color: white;
  }
}

Explanations:

The default styles apply when the screen width is greater than 768px.

The media query styles apply when the screen width is 768px or less.

Common Media Query Features

Screen Width and Height:

  • max-width: Targets devices with a maximum width.
  • min-width: Targets devices with a minimum width.

Example:


@media (min-width: 600px) {
  /* Styles for screens 600px and wider */
}

Device Orientation:

  • orientation: landscape: Targets devices in landscape mode.
  • orientation: portrait: Targets devices in portrait mode.

Example:


@media (orientation: portrait) {
  body {
    font-size: 18px;
  }
}

Resolution:

min-resolution or max-resolution: Targets devices based on their screen resolution.

Example:


@media (min-resolution: 300dpi) {
  img {
    filter: grayscale(100%);
  }
}

Why Use Media Queries?

Responsive Design: Tailor your website for different devices and screen sizes.

Improved User Experience: Provide optimized layouts and styles for every user.

Future-Proof Design: Adapt your design to new devices and technologies.

CSS Variables

CSS Variables, also known as Custom Properties, are entities defined in CSS that store reusable values. They allow developers to centralize values like colors, fonts, and sizes, making CSS easier to maintain and update.

Syntax:

Declaring a Variable: CSS variables are prefixed with -- and are usually defined in a global scope, such as the :root pseudo-class.


:root {
  --primary-color: #3498db;
  --font-size: 16px;
}

Using a Variable: To use a variable, the var() function is applied.


h1 {
  color: var(--primary-color);
  font-size: var(--font-size);
}

Fallback Value: You can add a fallback value in case the variable is not defined.


h1 {
  color: var(--secondary-color, #333);
}

How to Update CSS Variables Dynamically with JavaScript

Define the Variable in CSS: Use :root (global scope) or a specific selector to declare your variable.


:root {
  --primary-color: #3498db;
}

Access and Modify the Variable with JavaScript: Use the setProperty method on the style object of the desired element.


// Access the root element
const root = document.documentElement;

// Update the CSS variable
root.style.setProperty('--primary-color', '#ff0000');

Advantages of CSS Variables

Maintainability: Change the variable’s value in one place, and it updates across the entire project.

Consistency: Promotes consistent design by centralizing styles.

Theme Switching: Simplify creating themes (e.g., light mode, dark mode).

Dynamic Updates: JavaScript can modify variables in real-time for interactive effects.

HTML Syntax

HTML syntax refers to the set of rules and conventions for writing HTML code, which is the standard language used to create web pages. It includes the structure and format of elements, tags, attributes, and their relationships. Here’s a breakdown of the core components of HTML syntax:

HTML Elements

An HTML document is composed of elements, each defined by tags.


<p>This is a paragraph.</p>

Explanations:

Opening tag: <p>
Content: This is a paragraph.
Closing tag: </p>

HTML Tags

Tags are enclosed in angle brackets (< >) and are case-insensitive (though lowercase is standard).

Example:


<h1>Main Heading</h1>

Explanations:

<h1> is a heading tag.

Attributes

Attributes provide additional information about elements and are included in the opening tag.

Example:


<img src="image.jpg" alt="An image">

Explanations:

src: Specifies the image file location.
alt: Provides alternative text for the image.

Nesting

Elements can be nested inside one another. Proper nesting ensures valid HTML.

Correct example:


<div>
    <p>Text inside a div.</p>
</div>

Incorrect example:


<div>
    <p>Text inside a div.</div>
</p>

Document Structure

HTML documents follow a specific structure:


<!DOCTYPE html>
<html>
<head>
    <title>Page Title</title>
</head>
<body>
    <h1>Welcome</h1>
    <p>This is a paragraph.</p>
</body>
</html>

Explanations:

<!DOCTYPE html>: Declares the document type.
<html>: Root element.
<head>: Contains metadata like title and styles.
<body>: Contains visible content.

Void Elements

Void elements do not have closing tags.

Examples:


<img src="image.jpg" alt="Image">
<br>
<input type="text">

Python Abstraction

Abstraction is a fundamental concept in object-oriented programming (OOP) that simplifies complex systems by hiding unnecessary details and exposing only the essential parts. In Python, abstraction allows us to define interfaces or abstract classes that specify methods or behaviors without implementing the specifics. This lets programmers focus on high-level functionality without understanding every underlying detail.

Key Concepts

Abstract Classes:

An abstract class is a blueprint for other classes. It defines methods that derived classes must implement but does not provide the complete implementation itself.

In Python, abstract classes are created using the abc (Abstract Base Class) module, which provides the ABC base class and the @abstractmethod decorator.

Abstract classes cannot be instantiated on their own; they are meant to be subclassed.

Interfaces:

Python does not have a formal interface construct, but abstract classes often serve this role by defining a set of methods that subclasses must implement.

These methods form a “contract,” ensuring that any class implementing this abstract class will provide certain methods.

@abstractmethod Decorator:

Methods decorated with @abstractmethod must be implemented by any subclass of the abstract class.

If a subclass does not implement all abstract methods, it cannot be instantiated.

Benefits of Abstraction

Code Reusability: Abstract classes provide a base structure that other classes can inherit and build upon.

Modularity: Different parts of a program can be developed independently by relying on abstractions.

Security: Abstraction hides the internal workings of a class, exposing only the necessary details, which helps prevent unintended interference.

Example:


from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        """Abstract method to define animal sounds"""
        pass

    @abstractmethod
    def move(self):
        """Abstract method to define animal movement"""
        pass

class Dog(Animal):
    def sound(self):
        return "Woof woof"

    def move(self):
        return "Runs on four legs"

class Bird(Animal):
    def sound(self):
        return "Chirp chirp"

    def move(self):
        return "Flies in the sky"

# Usage
animals = [Dog(), Bird()]

for animal in animals:
    print(f"{animal.__class__.__name__} sound: {animal.sound()}, movement: {animal.move()}")

Explanation of the Code:

Abstract Class Animal: The Animal class inherits from ABC (Abstract Base Class) and contains two abstract methods, sound and move. These methods serve as placeholders and must be implemented by any subclass.

Concrete Classes (Dog, Bird): These classes inherit from Animal and provide specific implementations for the sound and move methods.

Instantiation and Usage: Only Dog and Bird can be instantiated because they implement all abstract methods from Animal. The code iterates over each animal and calls their sound and move methods, demonstrating polymorphism.

Python Polymorphism

Polymorphism is an essential concept in object-oriented programming (OOP) that allows methods or functions to operate on objects of different classes as long as they follow a certain interface or structure. In Python, polymorphism enables the same function or method to be used for objects of different types, making the code more flexible and extensible.

Benefits of Polymorphism

Flexibility and Extensibility: Functions and methods can work with different types, making it easier to extend code without changing existing logic.

Reusability: Generic code can handle multiple types, reducing redundancy.

Readability: Polymorphic functions make the code more readable and concise.

Types of Polymorphism

Duck Typing:

In Python, if an object behaves like a certain type (has the required methods or attributes), it can be used as that type, regardless of its actual class. This concept is known as “duck typing.”

For example, if an object has a fly() method, it can be treated as a “flying” object, even if it’s not explicitly a subclass of a “FlyingObject” class.


class Bird:
    def fly(self):
        print("Bird is flying")

class Airplane:
    def fly(self):
        print("Airplane is flying")

def make_it_fly(flying_object):
    # Duck typing - works if the object has a fly() method
    flying_object.fly()

# Usage
bird = Bird()
airplane = Airplane()

make_it_fly(bird)       # Outputs: Bird is flying
make_it_fly(airplane)   # Outputs: Airplane is flying

Method Overriding


class Animal:
    def sound(self):
        print("Some generic animal sound")

class Dog(Animal):
    def sound(self):
        print("Woof woof")

class Cat(Animal):
    def sound(self):
        print("Meow")

# Usage
animals = [Dog(), Cat(), Animal()]

for animal in animals:
    animal.sound()    # Different sound based on the actual object type

Here, each subclass (Dog, Cat) provides its own version of the sound method, overriding the one defined in the Animal base class.

Operator Overloading


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

    def __add__(self, other):
        # Overloading the + operator to add two vectors
        return Vector(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"Vector({self.x}, {self.y})"

# Usage
v1 = Vector(2, 3)
v2 = Vector(4, 5)

result = v1 + v2
print(result)    # Outputs: Vector(6, 8)

The + operator is overloaded in the Vector class, allowing the addition of two Vector objects.

Python Encapsulation

Encapsulation is a fundamental principle in object-oriented programming (OOP) that restricts access to the internal state and behavior of an object. In Python, encapsulation is implemented by defining attributes and methods that can be accessed only within the class, providing control over how the data is modified or accessed.

Key Concepts of Encapsulation in Python

Private and Public Access Modifiers:

Public Attributes: Accessible from anywhere. In Python, any variable or method not prefixed with an underscore is public by default.

Private Attributes: Intended to be inaccessible from outside the class, although Python only provides a convention for indicating this (not strict enforcement).

  • Prefixing an attribute or method name with a single underscore (e.g., _attribute) suggests it should be treated as non-public.
  • Prefixing with a double underscore (e.g., __attribute) causes name mangling, making it harder to access from outside the class.

Getters and Setters:

These methods control access to private attributes by providing indirect ways to read or modify the value.

Useful for adding validation or logic when data is set or retrieved.

Encapsulation Using Properties:

In Python, the @property decorator can be used to make getters and setters more Pythonic. Properties allow attributes to be accessed like regular variables but managed by getters and setters in the background.

Example:


class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner                # Public attribute
        self.__balance = balance           # Private attribute using double underscore

    @property
    def balance(self):
        """Getter for balance."""
        return self.__balance

    @balance.setter
    def balance(self, amount):
        """Setter for balance with validation."""
        if amount < 0:
            print("Invalid amount! Balance cannot be negative.")
        else:
            self.__balance = amount

    def deposit(self, amount):
        """Method to deposit money into account."""
        if amount > 0:
            self.__balance += amount
            print(f"Deposited ${amount}. New balance is ${self.__balance}")
        else:
            print("Deposit amount must be positive.")

    def withdraw(self, amount):
        """Method to withdraw money from account with validation."""
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            print(f"Withdrew ${amount}. New balance is ${self.__balance}")
        else:
            print("Invalid withdrawal amount or insufficient funds.")

# Usage
account = BankAccount("John")
account.deposit(200)
account.withdraw(50)
print(account.balance)         # Accessing balance via the getter method
account.balance = -10          # Attempting to set balance to a negative value

Explanation

Private Attribute __balance: The balance is private to prevent direct modifications from outside the class.

@property and @balance.setter: The balance property allows controlled access to the private attribute __balance. When attempting to set a negative balance, a warning is shown.

Public Methods (deposit and withdraw): These methods encapsulate logic for modifying the balance, validating input, and maintaining account integrity.

Encapsulation helps to hide internal implementation details and protect data integrity by controlling access and ensuring data consistency.

Python iterators

In Python, iterators are objects that allow you to traverse through all the elements of a collection (such as lists, tuples, or dictionaries) one at a time. Iterators are a key part of Python’s data-handling capabilities, enabling efficient processing of data structures or even creating custom iteration logic.

Key Concepts of Iterators

Iterator Protocol: An object is considered an iterator if it implements two special methods:

  • __iter__() – Returns the iterator object itself and is used for initialization.
  • __next__() – Returns the next item in the sequence and raises StopIteration when there are no more items.

Iterable vs. Iterator:

Iterable: An object that can return an iterator, typically using the __iter__() method (e.g., lists, tuples, dictionaries).

Iterator: An object that keeps track of its current state and knows how to fetch the next element in the sequence. It is created by calling iter() on an iterable.

Example:

Consider a list, which is an iterable. You can get an iterator from it using the iter() function:


# List (Iterable)
numbers = [1, 2, 3]

# Getting an Iterator
iterator = iter(numbers)

# Using the Iterator
print(next(iterator))  # Output: 1
print(next(iterator))  # Output: 2
print(next(iterator))  # Output: 3

If you call next(iterator) after the last item, it will raise a StopIteration exception, signaling that the iteration is complete.

Creating a Custom Iterator

To create a custom iterator, you define a class with __iter__() and __next__() methods.

Here’s an example of a custom iterator that generates a sequence of numbers up to a specified limit:


class MyIterator:
    def __init__(self, max_value):
        self.max_value = max_value
        self.current = 0

    def __iter__(self):
        return self  # An iterator returns itself

    def __next__(self):
        if self.current < self.max_value:
            result = self.current
            self.current += 1
            return result
        else:
            raise StopIteration

# Using the custom iterator
iterator = MyIterator(5)
for number in iterator:
    print(number)  # Output: 0 1 2 3 4

In this example:

The __iter__() method returns the iterator instance itself.

The __next__() method increments the current attribute until it reaches max_value, after which it raises StopIteration.

Difference between Iterators vs Generators

While iterators and generators both allow you to iterate through data, they are slightly different:

Iterators require you to implement __iter__() and __next__().

Generators simplify iteration by using the yield keyword, automatically managing the state without needing to write __iter__() and __next__() methods.

Python generators

In Python, generators are a way to create iterators concisely and memory-efficiently. They are used to yield items one at a time, which is useful when working with large datasets or infinite sequences. Generators allow you to iterate over data without storing the entire sequence in memory.

Concepts

Generator Function: A function that uses the yield statement to return data one item at a time.

yield Statement: Similar to return, but instead of terminating the function, it pauses the function and saves its state. When the generator is called again, it resumes from where it left off.

Generator Expressions: Similar to list comprehensions, but they use parentheses instead of square brackets. They are a more memory-efficient way of creating sequences compared to list comprehensions.

Benefits of Using Generators

Memory Efficiency: Generators don’t store the entire sequence in memory; they generate items one at a time. This makes them ideal for handling large datasets or streaming data.

Lazy Evaluation: The values are generated only when needed. This is particularly useful in situations where generating all values at once would be wasteful or impossible.

Infinite Sequences: Generators can represent sequences with no end, like all even numbers or a sequence of random numbers.

Creating a Generator

A generator is created using a function with one or more yield statements.


def new_generator():
    yield 4
    yield 5
    yield 6

# Using the generator
gen = new_generator()
print(next(gen))  # Output: 4
print(next(gen))  # Output: 5
print(next(gen))  # Output: 6

When yield is encountered, the function pauses and returns the yielded value to the caller. When next() is called again, it resumes execution after the last yield statement.

Example: Generating a Sequence of Numbers

Here’s a generator that produces an infinite sequence of even numbers:


def evenNumbers():
    num = 0
    while True:
        yield num
        num += 2

# Using the generator
evens = evenNumbers()
print(next(evens))  # Output: 0
print(next(evens))  # Output: 2
print(next(evens))  # Output: 4

This generator will keep generating even numbers indefinitely, pausing each time it yields a value.

Generator Expressions

Generator expressions provide a quick and concise way to create generators. They look similar to list comprehensions but use parentheses instead of square brackets.

Example:


# List comprehension
squares_list = [x * x for x in range(10)]

# Generator expression
squares_gen = (x * x for x in range(10))

print(squares_list)   # Output: [0, 1, 4, 9, ..., 81]
print(next(squares_gen))  # Output: 0
print(next(squares_gen))  # Output: 1

Python Magic methods

Magic methods in Python (also known as dunder methods, short for “double underscore”) are special methods with double underscores before and after their names, like __init__ and __str__. They allow you to define behaviors for built-in operations on your custom objects. Magic methods enable customization of common operations such as adding, printing, or comparing objects, making code cleaner and more intuitive.

some of the most commonly used magic methods:

Initialization (__init__)

The __init__ method is the constructor in Python. It’s called when an object is created, allowing you to initialize attributes.


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("John", 35)  # __init__ is called here
print(p.name)            # Output: John

String Representation (__str__ and __repr__)

__str__: Defines the informal string representation of an object, shown when using print or str().

__repr__: Defines the official string representation, aimed at developers for debugging. It’s called by repr() and in the interactive shell.


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name}, {self.age} years old"

    def __repr__(self):
        return f"Person('{self.name}', {self.age})"

p = Person("John", 35)
print(str(p))    # Output: John, 35 years old
print(repr(p))   # Output: Person('John', 35)

Arithmetic Operators (__add__, __sub__, etc.)

Magic methods allow objects to interact with basic arithmetic operations.


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

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __str__(self):
        return f"({self.x}, {self.y})"

v1 = Vector(2, 3)
v2 = Vector(4, 5)
print(v1 + v2)   # Output: (6, 8)

Comparison Operators (__eq__, __lt__, __gt__, etc.)

These magic methods allow you to define custom behavior for comparison operations.


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

    def __lt__(self, other):
        return self.age < other.age

p1 = Person("John", 30)
p2 = Person("Bob", 25)
print(p1 == p2)  # Output: False
print(p1 < p2)   # Output: False

Length (__len__)

Defines behavior for the len() function.


class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def __len__(self):
        return self.pages

book = Book("My Python", 350)
print(len(book))  # Output: 350

Attribute Access (__getattr__, __setattr__, __delattr__)

Magic methods for managing attributes dynamically.

__getattr__: Called when trying to access an attribute that doesn’t exist

__setattr__: Controls how attributes are set.

__delattr__: Controls how attributes are deleted.


class Person:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, attr):
        return f"{attr} not found."

p = Person("John")
print(p.age)  # Output: age not found.

Callable (__call__)

Makes an instance of a class callable like a function.


class Greeter:
    def __init__(self, name):
        self.name = name

    def __call__(self, greeting):
        return f"{greeting}, {self.name}!"

greet = Greeter("John")
print(greet("Hello"))  # Output: Hello, John!

Context Management (__enter__ and __exit__)

Used for managing resources with with statements, like file handling or database connections.


class FileHandler:
    def __init__(self, filename):
        self.filename = filename

    def __enter__(self):
        self.file = open(self.filename, 'w')
        return self.file

    def __exit__(self, exc_type, exc_value, traceback):
        self.file.close()

with FileHandler("test.txt") as file:
    file.write("Hello, World!")

Iterator Protocol (__iter__ and __next__)

These methods allow an object to be used as an iterator.


class Counter:
    def __init__(self, max):
        self.max = max
        self.current = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.current < self.max:
            self.current += 1
            return self.current
        raise StopIteration

counter = Counter(3)
for num in counter:
    print(num)  # Output: 1 2 3

Python Inheritance

In Python, inheritance is a core concept of Object-Oriented Programming (OOP) that allows a class (called a child or subclass) to inherit attributes and methods from another class (called a parent or superclass). This helps to build upon existing code, promoting code reusability and a hierarchical class structure.

Benefits of Inheritance

Code Reusability: Reuse existing code without rewriting.

Hierarchical Class Structure: Establishes a clear class hierarchy and logical structure.

Modularity and Extensibility: Easily extend the functionality of existing classes.

Basic Inheritance

Inheritance allows the subclass to access and use the superclass’s properties and methods. The subclass can also override methods and add additional attributes or methods.

Example:


# Parent class
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound"

# Child class
class Dog(Animal):
    def speak(self):
        return f"{self.name} barks"

# Creating an instance of Dog
my_dog = Dog("Tommy")
print(my_dog.speak())  # Output: Tommy barks

In this example:

Animal is the parent class, with a method speak.

Dog is a child class that inherits from Animal and overrides the speak method.

Types of Inheritance

Single Inheritance

A child class inherits from only one parent class.


class Parent:
    pass

class Child(Parent):
    pass

Multiple Inheritance

A child class inherits from more than one parent class.


class Parent1:
    pass

class Parent2:
    pass

class Child(Parent1, Parent2):
    pass

Multilevel Inheritance

A chain of inheritance where a class is derived from another derived class.


class Grandparent:
    pass

class Parent(Grandparent):
    pass

class Child(Parent):
    pass

Using super() to Access the Parent Class

The super() function allows a subclass to call a method or constructor from its parent class. This is especially useful when you override methods in the subclass but still want to use the parent’s functionality.


class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} makes a sound"

class Cat(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # Call the parent constructor
        self.breed = breed

    def speak(self):
        return f"{self.name} meows"

# Creating an instance of Cat
my_cat = Cat("Whiskers", "Siamese")
print(my_cat.speak())         # Output: Whiskers meows
print(my_cat.breed)           # Output: Siamese

Method Overriding

In a child class, you can override a method from the parent class by defining it with the same name. This is useful for customizing inherited behavior.


class Animal:
    def speak(self):
        return "Some generic sound"

class Dog(Animal):
    def speak(self):
        return "Bark"

my_dog = Dog()
print(my_dog.speak())  # Output: Bark

Python attributes and methods

In Python, attributes and methods are integral to defining the properties and behaviors of objects within a class.

Attributes

Attributes are variables that hold data about the object. They can be of any data type (e.g., string, integer, list) and are usually initialized within the __init__ method (also known as the constructor) of a class.

Types of Attributes

Instance Attributes: Specific to each object instance and defined within the __init__ method.

Class Attributes: Shared across all class instances and defined outside any method within the class.

Example:


class Car:
    # Class attribute
    vehicle_type = "Car"

    def __init__(self, brand, color):
        # Instance attributes
        self.brand = brand
        self.color = color

create the Object


# Creating two objects of the Car class
car1 = Car("Toyota", "Red")
car2 = Car("Honda", "Blue")

print(car1.brand)         # Output: Toyota
print(car2.color)         # Output: Blue
print(car1.vehicle_type)  # Output: Car (shared class attribute)

Methods

Methods are functions defined within a class that describe the behavior of an object. They allow objects to perform actions and can interact with or modify the object’s attributes.

Types of Methods

Instance Methods: Operate on instance attributes and are defined with self as the first parameter.

Class Methods: Operate on class attributes and use @classmethod decorator with cls as the first parameter.

Static Methods: Do not modify class or instance attributes, usually defined with @staticmethod decorator, and don’t take self or cls as a parameter.

Example


class Car:
    vehicle_type = "Car"  # Class attribute

    def __init__(self, brand, color):
        self.brand = brand  # Instance attribute
        self.color = color  # Instance attribute

    # Instance method
    def start(self):
        print(f"The {self.color} {self.brand} is starting.")

    # Class method
    @classmethod
    def change_vehicle_type(cls, new_type):
        cls.vehicle_type = new_type

    # Static method
    @staticmethod
    def is_motor_vehicle():
        return True

create a Object


# Creating an instance
my_car = Car("Tata", "White")

# Calling instance method
my_car.start()  # Output: The White Tata is starting.

# Calling class method
Car.change_vehicle_type("Electric Car")
print(Car.vehicle_type)  # Output: Electric Car

# Calling static method
print(Car.is_motor_vehicle())  # Output: True

Python Classes and Objects

In Python, classes and objects are fundamental concepts of Object-Oriented Programming (OOP). They allow you to create reusable, organized code by structuring it into entities that represent real-world objects.

Classes

A class is like a blueprint for creating objects. It defines a set of attributes (data) and methods (functions) that the objects created from the class will have.

Example:


class Car:
    # Initializer or constructor to set up initial attributes
    def __init__(self, companyName, color):
        self.companyName = companyName  # attribute
        self.color = color  # attribute

    # Method
    def start(self):
        print(f"The {self.color} {self.companyName} car is starting.")

Objects

An object is an instance of a class. When you create an object, you are creating an individual item based on the class blueprint.


# Creating an object of the Car class
my_car = Car("Tata", "White")

# Accessing attributes
print(my_car.companyName)  # Output: Tata
print(my_car.color)  # Output: White

# Calling a method
my_car.start()  # Output: The White Tata car is starting.

Key Concepts

Attributes: These are the properties of an object, defined in the __init__ method of a class.

Methods: These are functions defined within a class that describe the behaviors of an object.

Constructor (__init__ method): This special method initializes the attributes of an object when it is created.

Self parameter: Refers to the instance of the class itself, allowing access to its attributes and methods.

Example of Classes and Objects


class Pet:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_name(self):
        return self.name

    def get_age(self):
        return self.age

# Creating an object
my_dog = Pet("Tommy", 7)

# Accessing methods
my_dog.name()         # Output: Tommy
print(my_dog.get_age())  # Output: 7

Why use class and Object?

Classes and objects are useful for creating organized, modular, and maintainable code in larger programs.

Modularity: Code is more modular, which makes it easier to manage.

Reusability: You can create multiple objects from a single class.

Encapsulation: Classes encapsulate data and functions, protecting and organizing them.

Python Error Handling

Error handling in Python is done with try, except, else, and finally statements. This structure allows you to gracefully handle errors and perform cleanup operations if needed.

Basic Error Handling


try:
    # Code that might raise an exception
    result = 10 / 0
except ZeroDivisionError:
    print("Error: Division by zero is not allowed.")

try: Block of code that might raise an exception.

except: Code to execute if an exception occurs. You can specify specific error types like ZeroDivisionError or use a general exception handler.

Catching Multiple Exceptions

You can catch multiple specific exceptions by listing them in a tuple or by using multiple except blocks.


try:
    result = int("abc")
except (ValueError, TypeError) as e:
    print(f"Error occurred: {e}")

Or:


try:
    result = int("abc")
except ValueError:
    print("Error: Cannot convert string to integer.")
except TypeError:
    print("Error: Invalid type used.")

Using a General Exception

To catch any exception, use except Exception or simply except. This can be helpful for logging or debugging, but it’s usually good to handle specific errors where possible.


try:
    result = 10 / 0
except Exception as e:
    print(f"An unexpected error occurred: {e}")

else Clause

The else block runs if no exceptions were raised in the try block.


try:
    result = 10 / 2
except ZeroDivisionError:
    print("Error: Division by zero.")
else:
    print("No errors occurred. Result is:", result)

finally Clause

The finally block runs no matter what, whether an exception occurred or not. It’s often used for cleanup actions (e.g., closing files or database connections).


try:
    file = open("example.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("Error: File not found.")
finally:
    if 'file' in locals():
        file.close()
        print("File closed.")

Raising Exceptions

You can use raise to throw an exception if a condition isn’t met.


def divide(a, b):
    if b == 0:
        raise ValueError("Cannot divide by zero.")
    return a / b

try:
    print(divide(10, 0))
except ValueError as e:
    print(e)

Custom Exception Classes

You can create custom exceptions by subclassing Python’s built-in Exception class.


class CustomError(Exception):
    pass

def risky_function():
    raise CustomError("This is a custom error.")

try:
    risky_function()
except CustomError as e:
    print(e)

Python working with JSON files

Python’s json module allows you to work with JSON (JavaScript Object Notation) files, which are widely used for data exchange.

Reading from a JSON File


import json

with open("data.json", "r") as file:
    data = json.load(file)
    print(data)

json.load(file): Reads the JSON file and converts it into a Python dictionary or list.

Writing to a JSON File


import json

data = {
    "name": "Alice",
    "age": 30,
    "city": "New York"
}

with open("data.json", "w") as file:
    json.dump(data, file, indent=4)

json.dump(data, file): Writes the data as JSON.

indent=4: Makes the JSON output formatted for readability.

Converting Between JSON Strings and Python Dictionaries

From JSON string to dictionary: json.loads(json_string)

From dictionary to JSON string: json.dumps(dictionary)


# Convert JSON string to Python dictionary
json_string = '{"name": "John", "age": 35, "city": "London"}'
data = json.loads(json_string)
print(data)

# Convert Python dictionary to JSON string
dictionary = {"name": "Tom", "age": 30, "city": "New York"}
json_str = json.dumps(dictionary, indent=4)
print(json_str)

Python working with csv files

Python has built-in libraries to handle CSV files, making it easy to read from, write to, and process these file formats. Here are examples of how to work with both CSV files.

Python’s csv module provides functionality to handle CSV (Comma-Separated Values) files.

Reading from a CSV File


import csv

with open("data.csv", "r") as file:
    reader = csv.reader(file)
    for row in reader:
        print(row)

csv.reader(file): Reads the CSV file line by line, where each row is a list.

Writing to a CSV File


import csv

data = [
    ["Name", "Age", "City"],
    ["John", 35, "London"],
    ["Tom", 30, "New York"],
]

with open("data.csv", "w", newline="") as file:
    writer = csv.writer(file)
    writer.writerows(data)

csv.writer(file): Creates a writer object.

writer.writerows(data): Writes multiple rows to the file.

Using a Dictionary with CSV Files

The DictReader and DictWriter classes allow you to work with CSV data as dictionaries, which can be more intuitive.

Reading with DictReader


import csv

with open("data.csv", "r") as file:
    reader = csv.DictReader(file)
    for row in reader:
        print(row)  # Each row is a dictionary

Writing with DictWriter


import csv

data = [
    {"Name": "John", "Age": 35, "City": "London"},
    {"Name": "Tom", "Age": 30, "City": "New York"},
]

with open("data.csv", "w", newline="") as file:
    fieldnames = ["Name", "Age", "City"]
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    writer.writeheader()  # Writes the field names as headers
    writer.writerows(data)

Python file exception handling

In Python, exceptions can be handled while working with files using try, except, and optionally finally. This is useful to catch potential errors such as FileNotFoundError, PermissionError, or IOError

Example:


try:
    with open("filename.txt", "r") as file:
        content = file.read()
        print(content)
except FileNotFoundError:
    print("Error: The file was not found.")
except PermissionError:
    print("Error: You do not have permission to access this file.")
except IOError:
    print("Error: An IO error occurred while handling the file.")

Explanation

FileNotFoundError: Raised when trying to open a file that does not exist.

PermissionError: Raised if there is a permission issue, such as trying to read a write-protected file.

IOError: A more general exception for input/output errors (covers cases like disk issues).

Using finally to Ensure File Closure

In cases where you need to manually handle file opening and closing (not using with), you can use finally to ensure the file is closed even if an exception occurs.


try:
    file = open("filename.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("Error: The file was not found.")
except PermissionError:
    print("Error: You do not have permission to access this file.")
except IOError:
    print("Error: An IO error occurred while handling the file.")
finally:
    if 'file' in locals() and not file.closed:
        file.close()
        print("File closed successfully.")

Handling Multiple Errors

You can handle multiple specific errors or use a general Exception for unexpected issues.


try:
    with open("filename.txt", "r") as file:
        content = file.read()
except (FileNotFoundError, PermissionError) as e:
    print(f"Error: {e}")
except Exception as e:
    print(f"An unexpected error occurred: {e}")

Python delete file

To delete a file in Python, you can use the os.remove() function from the os module.

Deleting a File


import os

file_path = "filename.txt"
if os.path.exists(file_path):
    os.remove(file_path)
    print(f"{file_path} has been deleted.")
else:
    print("The file does not exist.")

os.remove(file_path): Deletes the file at the specified path.

os.path.exists(file_path): Checks if the file exists before attempting to delete it, which helps avoid errors.

Using pathlib (Python 3.4+)

Alternatively, you can use the Path class from pathlib for a more object-oriented approach.


from pathlib import Path

file_path = Path("filename.txt")
if file_path.exists():
    file_path.unlink()  # Deletes the file
    print(f"{file_path} has been deleted.")
else:
    print("The file does not exist.")

Python file write

In Python, writing to a file can be done using the open() function with a write mode. Here are the common methods to write data to a file:

Writing Text to a File

To write text data to a file, use the "w" mode (write mode). This will overwrite the file if it already


with open("filename.txt", "w") as file:
    file.write("Hello, World!")

"filename.txt": The name of the file you want to write to.

"w": Opens the file in write mode. If the file exists, it will be overwritten; if it doesn’t exist, a new file is created.

file.write(): Writes a string to the file.

Appending Text to a File

To add data to the end of an existing file without overwriting it, use the "a" mode (append mode).


with open("filename.txt", "a") as file:
    file.write("\nAppending this line to the file.")

"a": Opens the file in append mode, adding content to the end of the file without erasing existing data.

Writing Multiple Lines to a File

To write multiple lines at once, you can use writelines(). You need to provide a list of strings, where each string represents a line.


lines = ["First line\n", "Second line\n", "Third line\n"]
with open("filename.txt", "w") as file:
    file.writelines(lines)

Writing with Path from pathlib (Python 3.4+)

Using the write_text() method from pathlib provides a convenient way to write text data.


from pathlib import Path
Path("filename.txt").write_text("This is written with pathlib!")

Python file read

In Python, you can read files using several methods, depending on how you want to handle the file’s contents. Below are common ways to read files in Python:

Reading the Entire File at Once

file.read() reads the entire file as a single string. This is useful for small files, but for large files, it’s better to read in chunks or lines.

Example:


with open("filename.txt", "r") as file:
    content = file.read()
    print(content)

Reading Line by Line (for Large Files)

This approach reads each line one at a time, which is efficient for larger files.


with open("filename.txt", "r") as file:
    for line in file:
        print(line.strip())  # `.strip()` removes extra whitespace

Reading All Lines as a List

file.readlines() returns a list of lines, where each line is an item in the list.


with open("filename.txt", "r") as file:
    lines = file.readlines()
    print(lines)  # Each line will be an item in the list

Reading in Chunks

For very large files, you can specify a chunk size (in bytes) to read portions of the file at a time.

Example:


with open("filename.txt", "r") as file:
    chunk_size = 1024  # For example, 1024 bytes
    while chunk := file.read(chunk_size):
        print(chunk)

This reads 1024 bytes at a time until the file is completely read.

Python File Handling

Python provides robust file handling capabilities, allowing you to create, read, update, and delete files. Here’s a breakdown of the essential file handling operations:

Opening a File

Python uses the open() function to open files, which returns a file object.

Syntax:


file = open("filename", "mode")

Common file modes include:

"r" – Read (default): Opens the file for reading; error if the file does not exist.

"w" – Write: Opens the file for writing, creates a new file if it doesn’t exist, or truncates the file if it exists.

"a" – Append: Opens the file for appending; creates the file if it doesn’t exist.

"r+" – Read and Write: Opens the file for both reading and writing.

Python pip

pip is the standard package manager for Python that allows you to install, upgrade, and manage external packages from the Python Package Index (PyPI) and other repositories. Here’s a quick guide on how to use pip:

Installing a Package

To install a package, use the command:


pip install package_name

Example:


pip install requests

Installing a Specific Version

To install a specific version of a package, specify the version number:


pip install package_name==version_number

Example:


pip install numpy==1.21.0

Upgrading a Package

To upgrade an already installed package to the latest version, use:


pip install --upgrade package_name

Example:


pip install --upgrade pandas

Uninstalling a Package

To remove an installed package:


pip uninstall package_name

Example:


pip uninstall requests

Listing Installed Packages

To list all installed packages and their versions:


pip list

Python Packages

A package is a collection of modules organized within a directory. This directory includes an __init__.py file, which indicates that the directory should be treated as a package.

Why use packages?

Packages allow for a structured organization of modules and help in building complex applications with different functionality split across various modules.

Example: Suppose you have a package named utilities with the following structure:


utilities/
├── __init__.py
├── file_ops.py
└── calculation.py

file_ops.py might contain file-related functions, while calculation.py could contain mathematical functions.

The __init__.py file can be empty or used to initialize the package.

Usage: You can import specific modules from a package like this:


from utilities import calculation
result = calculation.add(10, 5)

Or import everything from the package:


import utilities
result = utilities.calculation.add(10, 5)

Python Module

A module in Python is a single file (with a .py extension) that contains Python definitions and statements, such as functions, classes, and variables. Modules allow you to organize related code into separate files logically.

Creating and Importing a Module

To create a module, you simply write some Python code in a file with the .py extension. For example, let’s create a module named math_operations.py with a few functions:


# calculation.py

def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

Importing the Module

You can use import to bring the module into another file, making its functions available to use:


import calculation

result = calculation.add(5, 4)
print(result)  # Output: 9

Importing Specific Functions or Variables

You can also import specific functions or variables from a module:


from calculation import add

result = add(10, 7)
print(result)  # Output: 17

Using Aliases

You can use as to create an alias for a module or function:


import calculation as ca

result = ca.subtract(10, 5)
print(result)  # Output: 5

Standard Library Modules

Python’s standard library provides a wide range of built-in modules, such as math, datetime, and random. You can import and use these without additional setup.


import math

print(math.sqrt(25))  # Output: 5.0

Python remove items from sets

In Python, you can remove items from a set using several methods. Here’s how each one works:

Removing a Specific Item with remove()

The remove() method deletes a specific item from the set. If the item doesn’t exist, it raises a KeyError.


data_set = {1, 2, 3, 4, 5}
data_set.remove(4)
print(data_set)  # Output: {1, 2, 3, 5}

# Trying to remove an item that doesn't exist
# data_set.remove(6)  # Raises KeyError

Removing a Specific Item with discard()

The discard() method also deletes a specific item from the set, but if the item doesn’t exist, it doesn’t raise an error.


data_set = {1, 2, 3, 4, 5}
data_set.discard(4)
print(data_set)  # Output: {1, 2, 3, 5}

data_set.discard(6)  # No error if 6 isn't in the set

Removing and Returning a Random Item with pop()

The pop() method removes and returns a random item from the set. Since sets are unordered, you won’t know which item will be removed.


data_set = {10, 20, 30, 40, 50}
removed_item = data_set.pop()
print("Removed item:", removed_item) //50
print("Set after pop:", data_set)  // {10, 20, 30, 40}
# Output will vary because pop removes a random item

Removing All Items with clear()

The clear() method removes all items from the set, leaving it empty.


data_set = {1, 2, 3, 4, 5}
data_set.clear()
print(data_set)  # Output: set()

python retrieve items from sets

In Python, you can “fetch” or retrieve items from a set in various ways. Since sets are unordered collections of unique elements, they don’t support indexing, so you cannot directly access an element by position like you would in a list. Instead, you have to use iteration or other methods.

Fetching Elements by Iterating Over the Set

The most common way to access elements in a set is to loop through it with a for loop:


data_set = {1, 2, 3, 4, 5}

for item in data_set:
    print(item)

Output:

1
2
3
4
5

Fetching a Single Element Using pop()

The pop() method removes and returns a random element from the set. Since sets are unordered, you won’t know which element will be removed, but this is helpful if you just need any one item from the set.


data_set = {10, 20, 30, 40, 50}
item = data_set.pop()
print("Popped item:", item)     #Output: 50
print("Set after popping:", data_set) #Output: {10, 20, 30, 40}
# Output will vary since the element removed is chosen randomly

Checking for Specific Elements with in

You can check if a specific element is in the set using the in keyword.


data_set = {1, 2, 3, 4, 5}

if 3 in data_set:
    print("3 is in the set")  # Output: 3 is in the set
else:
    print("3 is not in the set")

Converting Set to List (for Indexed Access)

If you need to access specific elements by index, you can convert the set to a list or tuple first. However, this will change the order and removes the unordered characteristic of a set.


data_set = {10, 20, 30, 40, 50}
data_set = list(my_set)

# Now you can access elements by index
print(data_set [0])  # Output will vary due to unordered nature of sets

Python add set item

In Python, you can add elements to a set using the add() and update() methods.

Adding a Single Element with add()

The add() method adds a single element to the set. If the element is already in the set, it won’t be added again, as sets only contain unique items.


data_set = {1, 2, 3, 4, 5}
data_set.add(6)  # Adds 6 to the set
print(data_set)  # Output: {1, 2, 3, 4, 5, 6}

data_set.add(2)  # Adding a duplicate value; nothing changes
print(data_set)  # Output: {1, 2, 3, 4, 5, 6}

Example2:


# Initialize a set
fruits = {"apple", "banana"}

# Add a single element
fruits.add("orange")
print(fruits)  # Output: {"apple", "banana", "orange"}

Adding Multiple Elements with update()

To add multiple elements at once, use the update() method. You can pass any iterable (like a list, tuple, or another set), and all unique items from that iterable will be added to the set.


data_set = {1, 2, 3, 4, 5}
data_set.update([6, 7, 8])  # Adds 6, 7, and 8 to the set
print(data_set)  # Output: {1, 2, 3, 4, 5, 6, 7, 8}

# Adding elements from another set
data_set.update({7, 8, 9})
print(data_set)  # Output: {1, 2, 3, 4, 5, 6, 7, 8, 9}

# Adding elements from a string (unique characters will be added)
my_set.update("hello")
print(data_set)  # Output may include {1, 2, 3, 4, 5, 6, 7, 8, 9, 'h', 'e', 'l', 'o'}

Example2:


# Initialize a set
fruits = {"apple", "banana"}

# Add multiple elements
fruits.update(["mango", "orange"])
print(fruits)  # Output: {"apple", "banana", "mango", "orange"}

Python sets

In Python, a set is a collection data type that stores unordered, unique elements. It is useful when you need to eliminate duplicate values, perform mathematical set operations (like unions, intersections, etc.), or manage a collection of unique items efficiently.

Creating a Set

Creating a set in Python can be done using curly braces {} or the set() function. Sets are useful for storing unique, unordered elements.

1. Using Curly Braces {}

You can create a set directly by placing items inside curly braces:


# Creating a set with curly braces
data_set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
print(data_set)  # Output: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

2. Using the set() Function

You can also create a set using the set() function, especially helpful when creating a set from other iterables like lists, tuples, or strings.


# Creating a set from a list
data_set = set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(data_set)  # Output: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

# Creating a set from a string (breaks it into unique characters)
char_set = set("python")
print(char_set)  # Output: {'p', 'y', 't', 'h', 'o', 'n'}

Note: To create an empty set, you must use set() because {} initializes an empty dictionary.

Python remove dictionary item

In Python, you can remove items from a dictionary using various methods depending on the situation. Here’s a rundown of the most commonly used methods for removing items:

Using pop()

The pop() method removes the specified key and returns its corresponding value.

Raises a KeyError if the key doesn’t exist unless a default value is provided.


my_dict = {"name": "John", "age": 35, "city": "London"}

# Remove and return value of "age"
age = my_dict.pop("age")
print(age)          # Output: 35
print(my_dict)      # Output: {'name': 'John', 'city': 'London'}

# Using a default value if the key is not found
profession = my_dict.pop("profession", "Not Found")
print(profession)   # Output: Not Found

Using popitem()

The popitem() method removes and returns the last inserted key-value pair as a tuple.

This method raises a KeyError if the dictionary is empty.


my_dict = {"name": "John", "age": 35}

# Remove the last inserted item
last_item = my_dict.popitem()
print(last_item)    # Output: ('age', 35)
print(my_dict)      # Output: {'name': 'John'}

Using del Statement

The del statement can remove a specific key or delete the entire dictionary.

Raises a KeyError if the key doesn’t exist.


my_dict = {"name": "John", "age": 35, "city": "London"}

# Remove a specific key
del my_dict["city"]
print(my_dict)  # Output: {'name': 'John', 'age': 35}

# Delete the entire dictionary
del my_dict
# print(my_dict)  # Raises NameError because my_dict no longer exists

Using clear()

The clear() method removes all items from the dictionary, resulting in an empty dictionary.


my_dict = {"name": "John", "age": 35}
my_dict.clear()
print(my_dict)  # Output: {}

Removing Nested Dictionary Items

If you have nested dictionaries, you can remove items by specifying nested keys.


my_dict = {
    "name": "John",
    "details": {
        "age": 35,
        "city": "London"
    }
}

# Remove a key from the nested dictionary
del my_dict["details"]["city"]
print(my_dict)  # Output: {'name': 'John', 'details': {'age': 35}}

Python update Dictionary

In Python, you can update items in a dictionary by adding new key-value pairs, modifying existing ones, or merging another dictionary.

Updating an Existing Key-Value Pair

Assign a new value to an existing key using the = operator.


my_dict = {"name": "John", "age": 35, "city": "London"}
my_dict["age"] = 40  # Updating the age
print(my_dict)  # Output: {"name": "John", "age": 35, "city": "London"}

Adding a New Key-Value Pair

Assign a value to a new key that doesn’t yet exist in the dictionary.


my_dict["country"] = "England"
print(my_dict)  # Output: {"name": "John", "age": 35, "city": "London", 'country': 'England'}

Using the update() Method

The update() method merges another dictionary or iterable of key-value pairs into the original dictionary.

If a key exists, update() replaces its value; if it doesn’t, update() adds it.


my_dict = {"name": "John", "age": 35, "city": "London", "country": "England"}
# Merging another dictionary
my_dict.update({"age": 40, "profession": "Engineer"})
print(my_dict)  # Output: {"name": "John", "age": 40, "city": "London", "country": "England", 'profession': 'Engineer'}

# Using an iterable of key-value pairs (list of tuples)
my_dict.update([("hobby", "painting"), ("city", "Manchester")])
print(my_dict)  # Output: {"name": "John", "age": 40, "city": "Manchester", 'country': 'England', 'profession': 'Engineer', 'hobby': 'painting'}

Updating Nested Dictionaries

If your dictionary contains other dictionaries as values, you can update nested dictionaries by accessing the specific keys.


my_dict = {
    "name": "John",
    "details": {
        "age": 35,
        "city": "London"
    }
}

# Update a nested dictionary
my_dict["details"]["age"] = 40
my_dict["details"]["profession"] = "Engineer"
print(my_dict)  # Output: {'name': 'John', 'details': {'age': 35, 'city': 'London', 'profession': 'Engineer'}}

Using setdefault() for Conditional Updates

The setdefault() method updates the dictionary only if the key doesn’t exist. If the key is already present, it leaves it unchanged.


my_dict = {"name": "John", "age": 35}
my_dict.setdefault("age", 40)    # Won't update because "age" already exists
my_dict.setdefault("city", "London")  # Adds "city" because it's not in the dictionary
print(my_dict)  # Output: {'name': 'John', 'age': 40, 'city': 'London'}

Python Access Dictionary

In Python, there are several ways to access values stored in a dictionary. Here’s a quick overview of the main methods:

Using Bracket Notation []

You can access dictionary values directly by referencing their keys within square brackets.

Note: This method will raise a KeyError if the key does not exist.


my_dict = {"name": "John", "age": 35, "city": "London"}
print(my_dict["name"])  # Output: John

Using the get() Method

The get() method allows safe access to values. If the key doesn’t exist, it returns None or a default value you specify.

This is a safer option if you’re unsure whether a key exists in the dictionary.


my_dict = {"name": "John", "age": 35}

# Access existing key
print(my_dict.get("age"))        # Output: 35

# Access non-existent key with default value
print(my_dict.get("salary", 0))  # Output: 0

Accessing All Keys, Values, and Key-Value Pairs

Keys: Use keys() to get all keys in the dictionary.

Values: Use values() to get all values in the dictionary.

Key-Value Pairs: Use items() to get all key-value pairs as tuples.


my_dict = {"name": "John", "age": 35, "city": "London"}

# Get all keys
print(my_dict.keys())    # Output: dict_keys(['name', 'age', 'city'])

# Get all values
print(my_dict.values())  # Output: dict_values(['John', 35, 'London'])

# Get all key-value pairs
print(my_dict.items())   # Output: dict_items([('name', 'John'), ('age', 35), ('city', 'London')])

Looping Through Dictionary Values

You can loop over a dictionary to access each key and value.


my_dict = {"name": "John", "age": 35, "city": "London"}

# Loop through keys
for key in my_dict:
    print(key, my_dict[key])

# Loop through values only
for value in my_dict.values():
    print(value)

# Loop through key-value pairs
for key, value in my_dict.items():
    print(f"{key}: {value}")

Using Default Dictionary with collections.defaultdict

defaultdict from the collections module automatically assigns a default value if a key doesn’t exist, so you don’t have to check manually.


from collections import defaultdict

my_dict = defaultdict(lambda: "Not Found")
my_dict["name"] = "John"

print(my_dict["name"])   # Output: John
print(my_dict["age"])    # Output: Not Found (default value)

Python dictionary

In Python, a dictionary is a built-in data structure that stores data in key-value pairs. Dictionaries are mutable, meaning you can change, add, or remove items. Each key in a dictionary must be unique, immutable, and hashable (e.g., strings, numbers, or tuples).

Creating a Dictionary

Dictionaries can be created using curly braces {} with pairs of keys and values separated by a colon :.


dictData = {
    "name": "John",
    "age": 35,
    "city": "London"
}

Alternatively, you can create an empty dictionary and add items later:


dictData = {}
dictData ["name"] = "John"
dictData ["age"] = 35
dictData ["city"] = "London"

You can also use the dict() constructor


my_dict = dict(name="John", age=35, city="London")

Python tuple method

In Python, tuples have only two built-in methods since they are immutable and limited in functionality compared to lists. Here are the two tuple methods:

count()

The count() method returns the number of times a specified value appears in a tuple.

Syntax:


tuple.count(value)

Example:


my_tuple = (1, 2, 3, 4, 5, 5, 6, 7, 8, 5)
count_of_5 = my_tuple.count(5)
print(count_of_5)  # Output: 3 (because `5` appears three times)

index()

The index() method returns the index of the first occurrence of a specified value in the tuple.

If the value is not present, it raises a ValueError.

Syntax:


tuple.index(value)

Example:


my_tuple = (10, 20, 30, 20, 40)
index_of_20 = my_tuple.index(20)
print(index_of_20)  # Output: 1 (index of the first occurrence of `20`)

update tuple

Tuples in Python are immutable, meaning you can’t directly modify, add, or remove elements from them after they’re created. However, there are workarounds to effectively “update” a tuple if needed. Here’s how you can do it:

Convert the Tuple to a List, Modify It, and Convert Back

You can convert the tuple to a list, make changes, and then convert it back to a tuple.


my_tuple = (1, 2, 3)

# Convert to list
temp_list = list(my_tuple)

# Modify the list
temp_list[0] = 10  # Changing the first element to 10
temp_list.append(4)  # Adding a new element

# Convert back to tuple
my_tuple = tuple(temp_list)

print(my_tuple)  # Output: (10, 2, 3, 4)

Concatenate Tuples to “Add” Elements

You can create a new tuple by concatenating the existing tuple with another one containing the new elements.


my_tuple = (1, 2, 3, 4, 5)
my_tuple = my_tuple + (6, 7)
print(my_tuple)  # Output: (1, 2, 3, 4, 5, 6, 7)

Reassign the Tuple with Modified Elements

You can create a new tuple by reassigning elements around the ones you want to change.


my_tuple = (1, 2, 3)
my_tuple = (10,) + my_tuple[1:]  # Change the first element
print(my_tuple)  # Output: (10, 2, 3)

Using Slicing for Partial Reassignment

Slicing can be used to replace parts of a tuple.


my_tuple = (1, 2, 3, 4)
my_tuple = my_tuple[:1] + (10, 20) + my_tuple[2:]
print(my_tuple)  # Output: (1, 10, 20, 3, 4)

Using Nested Tuples for Logical Changes

If you frequently need to “update” certain parts of a tuple, consider using nested tuples where only a part of the structure may need changing.


my_tuple = ((1, 2), (3, 4))
my_tuple = (my_tuple[0], (5, 6))  # Changing the second tuple
print(my_tuple)  # Output: ((1, 2), (5, 6))

accessing a tuple

In Python, you can access elements in a tuple using indexing, slicing, or by unpacking. Here’s a quick guide:

Accessing Elements by Index

1. Each element in a tuple has an index starting from 0 (for the first element) to n-1 (for the last element).

2. You can access elements by referring to their index inside square brackets


my_tuple= (10, 20, 30, 40, 50)
print(my_tuple[0])  # Output: 10
print(my_tuple[3])  # Output: 40

Negative Indexing can be used to access elements from the end of the tuple.


tuplData= (10, 20, 30, 40, 50)
print(my_tuple[-1])  # Output: 50 (last element)
print(my_tuple[-2])  # Output: 30

Slicing a Tuple

Slicing allows you to access a range of elements in a tuple by specifying a start, stop, and optional step.

Syntax:


tuple[start:stop:step]

Example:


my_tuple = (10, 20, 30, 40, 50, 60)
print(my_tuple[1:4])    # Output: (20, 30, 40)
print(my_tuple[:3])     # Output: (10, 20, 30)
print(my_tuple[2:])     # Output: (30, 40, 50, 60)
print(my_tuple[::2])    # Output: (10, 30, 50) (every second element)

Unpacking Tuples

You can assign each element of a tuple to individual variables using tuple unpacking.


my_tuple = ("John", 35, "Engineer")
name, age, profession = my_tuple
print(name)       # Output: John
print(age)        # Output: 38
print(profession) # Output: Engineer

Extended Unpacking: You can also use * to unpack remaining elements into a list.


my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
a, b, *rest = my_tuple
print(a)     # Output: 1
print(b)     # Output: 2
print(rest)  # Output: [3, 4, 5, 6, 7, 8, 9, 10]

Accessing Nested Tuples

If a tuple contains other tuples, you can access nested elements by chaining indices.


nested_tuple = (1, (2, 3), (4, 5, 6))
print(nested_tuple[1])      # Output: (2, 3)
print(nested_tuple[1][1])   # Output: 3
print(nested_tuple[2][0])   # Output: 4

Using a Loop to Access Elements

You can also use a for loop to iterate over elements in a tuple.


my_tuple = (10, 20, 30, 40, 50)
for item in my_tuple:
    print(item)
# Output: 10 20 30 40 50

Python Tuple

In Python, a tuple is an immutable sequence, meaning that once it’s created, you can’t modify it (no adding, removing, or changing elements). Tuples often store multiple items in a single variable, especially when the data should remain constant throughout the program. Here’s a breakdown of key points about tuples:

Creating a Tuple with Multiple Elements

Tuples are created by placing values inside parentheses () separated by commas:


tupleData = (1, 2, 3, 4, 5)

Creating a Tuple Without Parentheses (Tuple Packing)

You can also create a tuple by simply listing values separated by commas without parentheses (this is called tuple packing):


tupleData = 1, 2, 3, 4, 5

Creating an Empty Tuple

An empty tuple can be created by using empty parentheses:


empty_tuple = ()

Creating a Tuple with a Single Element

To create a tuple with a single item, you need to add a comma after the element. Without the comma, Python treats it as a single value in parentheses:


single_item_tuple = (6,)  # This is a tuple with one element
not_a_tuple = (6)         # This is just an integer, not a tuple

Using the tuple() Constructor

You can create a tuple from other iterable types (like lists or strings) by using the tuple() constructor:


# From a list
my_list = [1, 2, 3, 4, 5]
tuple_from_list = tuple(my_list)

# From a string
string = "hello"
tuple_from_string = tuple(string)

Python list methods

Python list methods are built-in functions for performing operations on lists. Here’s a comprehensive list of common list methods:

1. append() method

Adds an element to the end of the list.

Example:


fruits = ['apple', 'banana', 'orange']
fruits.append('mango')
print(fruits)  // ['apple', 'banana', 'orange', 'mango']

2. extend() method

The extend() method in Python is used to add all elements from an iterable (like a list, tuple, or string) to the end of an existing list. It’s different from append(), which only adds a single item, because extend() allows multiple items to be added.

Example:


fruits = ['apple', 'banana', 'orange']
more_fruits = ['mango', 'grape']
fruits.extend(more_fruits)
print(fruits)   // ['apple', 'banana', 'orange', 'mango', 'grape']

3. insert() method

Inserts an element at a specified position.


fruits = ['apple', 'banana', 'orange']
fruits.insert(1, 'mango')
print(fruits)   // ['apple', 'mango', 'banana', 'orange']

4. remove() method

The remove() method in Python is used to delete the first occurrence of a specified value from a list. If the value is not found, it raises a ValueError.

Syntax


list.remove(element)

element: The value you want to remove from the list.

Example


fruits = ['apple', 'banana', 'orange']
fruits.remove('banana')
print(fruits)   // ['apple','orange']

5. pop() method

The pop() method in Python is used to remove and return an element from a specified index in a list. If no index is specified, it removes and returns the last element. If you try to pop() from an empty list, it raises an IndexError.

Syntax:


list.pop(index)

index (optional): The position of the element to remove. If omitted, pop() removes the last element.

Example1:


fruits = ['apple', 'banana', 'orange']
fruits.pop()
print(fruits)   // ['apple','banana']

Example2:


fruits = ['apple', 'banana', 'orange']
fruits.pop(1)
print(fruits)   // ['apple','orange']

6. clear() method

Removes all elements from the list.


fruits = ['apple', 'banana', 'orange']
fruits.clear()
print(fruits)   // []

7. index() method

The index() method in Python returns the index of the first occurrence of a specified element in a list. If the element is not found, it raises a ValueError.

Syntax


list.index(element, start, end)

element: The item you want to find in the list.

start (optional): The position to start searching from (default is the beginning of the list).

end (optional): The position to end the search (default is the end of the list).

Example:


fruits = ['apple', 'banana', 'orange']
fruits.index('banana')
print(fruits)   // 1

8. count() method

The count() method in Python returns the number of times a specified element appears in a list.

Syntax


list.count(element)

element: The item you want to count in the list.

Example:


# Initial list
fruits = ['apple', 'banana', 'cherry', 'banana', 'orange', 'banana']

# Count occurrences of 'banana'
banana_count = fruits.count('banana')
print(banana_count)  # Output: 3

9. sort() method

The sort() method in Python sorts the elements of a list in ascending order by default. This method modifies the existing list and does not return a new one list. Optionally, you can sort the list in descending order by setting the reverse parameter to True.

Syntax


list.sort(key=None, reverse=False)

key (optional): A function that serves as a key for the sort comparison, allowing for custom sorting.

reverse (optional): A Boolean value. Set it to True to sort the list in descending order.

Example:


# Initial list
numbers = [3, 1, 4, 1, 5, 9]

# Sort in ascending order (default)
numbers.sort()
print(numbers)  # Output: [1, 1, 3, 4, 5, 9]

# Sort in descending order
numbers.sort(reverse=True)
print(numbers)  # Output: [9, 5, 4, 3, 1, 1]

10. reverse() method

The reverse() method in Python reverses the elements of a list in place, meaning it modifies the original list without creating a new one. This method does not sort the list but simply reverses the order of the items.

Syntax


list.reverse()

Example1:


# Initial list
numbers = [1, 2, 3, 4, 5]

# Reverse the list
numbers.reverse()
print(numbers)  # Output: [5, 4, 3, 2, 1]

Example2:


fruits = ['apple', 'banana', 'orange']
fruits.reverse()
print(fruits)  # Output: ['orange', 'banana', 'apple']

11 . copy() method

The copy() method in Python creates a shallow copy of a list, meaning it generates a new list with references to the same elements as the original list. This method does not modify the original list.

Syntax


new_list = original_list.copy()

Example


# Original list
fruits = ['apple', 'banana', 'orange']

# Create a copy of the list
fruits_copy = fruits.copy()

print(fruits_copy)  # Output: ['apple', 'banana', 'orange']

Modifying the Original List

If you modify the original list, the copied list remains unchanged, as they are separate lists.


# Modify the original list
fruits.append('mango')

print(fruits)      # Output: ['apple', 'banana', 'orange', 'mango']
print(fruits_copy) # Output: ['apple', 'banana', 'orange']

Python list slicing

Python list slicing is a technique that allows you to extract a subset of elements from a list by specifying a range of indices. The syntax for slicing is list[start:end:step], where:

start: The index where the slice begins (inclusive).

end: The index where the slice ends (exclusive).

step (optional): The interval between each element in the slice.

Basic Slicing

list[start:end]: Extracts elements starting from the index start up to, but not including, the index end.

Example:


numbers = [0, 1, 2, 3, 4, 5, 6, 7]

# Extract elements from index 2 to 5 (excluding index 5)
subset = numbers[2:5]
print(subset)  # Output: [2, 3, 4]

Omitting start or end

Omitting start means the slice starts from the beginning.


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[:3]  # Returns [0, 1, 2]

Omitting end means the slice goes till the end of the list


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[2:]  # Returns [2, 3, 4, 5]

Using negative indices:

Negative indices start counting from the end of the list


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[-3:]  # Returns [3, 4, 5]

Using step:

the step parameter determines the number of indices to skip between each selected element.

A positive step value (e.g., 2) skips that many indices forward.

Example: step=2 means every second element is taken.


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[::2]  # Returns [0, 2, 4]

Example: step=3 means every third element is taken.


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[::3]  # Returns [0, 1, 3, 4]

A negative step value (e.g., -1) skips indices in reverse

Example: step=-2 selects every second element in reverse order.


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[::-2]  # Returns [5, 3, 1]

Example: step=-3 means every third element is taken in reverse order.


my_list = [0, 1, 2, 3, 4, 5]
sliced = my_list[::-3]  # Returns [5, 4, 2, 1]

Python List

In Python, a list is a mutable, ordered collection of items. Lists can hold a variety of data types (including other lists) and allow for easy manipulation and access to their elements.

Creating a List

You can create a list by placing items inside square brackets [], separated by commas.


# Empty list
empty_list = []

# List of integers
numbers_list = [1, 2, 3, 4, 5]

# List of mixed data types
mixed_list = [1, "Hello", 3.14, True]

Accessing List Elements

You can access elements in a list using indexing. Python uses zero-based indexing, so the first element has an index of 0.


numbers = [20, 30, 40, 50]

print(numbers[0])  # Output: 20
print(numbers[2])  # Output: 40

# Negative indexing accesses elements from the end of the list
print(numbers[-1])  # Output: 50 (last element)

Modifying a List

Since lists are mutable, you can modify, add, or remove elements after the list is created.

Changing elements:


numbers[1] = 35
print(numbers)  # Output: [20, 35, 40, 50]

Python boolean

In Python, a Boolean value represents one of two values: True or False. Booleans are typically used in conditional statements or comparisons to control the flow of a program.

There are only two Boolean values:

  1. True
  2. False

Booleans can be created in several ways:

1. Direct Assignment:


is_user_active = True
is_completed = False

2. Using Comparison Operators:

  • ==: Checks if two values are equal.
  • !=: Checks if two values are not equal.
  • >: Greater than.
  • <: Less than.
  • >=: Greater than or equal to.
  • <=: Less than or equal to.

Example:


a = 25
b = 30
print(a > b)  # False
print(a == 5)  # True

3. Using Logical Operators:

and: Returns True if both conditions are true.

or: Returns True if at least one condition is true.

not: Reverses the Boolean value.

Example:


x = True
y = False

print(x and y)  # False
print(x or y)   # True
print(not x)    # False

What is Truthy and Falsy Values

In Python, many values can be implicitly converted to True or False in a Boolean context:

Falsy values: None, 0, 0.0, '' (empty string), [] (empty list), () (empty tuple), {} (empty dictionary).

Truthy values: Anything that is not considered falsy, like non-zero numbers, non-empty strings, and collections.

Example:


if []:
    print("This won't be printed because an empty list is Falsy.")
else:
    print("This will be printed.")

Python string

In Python, a string is a sequence of characters enclosed in single quotes (' ') or double quotes (" "). Strings are used to represent text in Python, and they are immutable, meaning once created, they cannot be changed.

Creating a String

You can create a string by assigning a sequence of characters to a variable.


# Single quotes
name = 'John'

# Double quotes
greeting = "Hello, world!"

Multiline Strings

To create a multiline string, you can use triple quotes (''' or """):


multiline_string = '''This is
a multiline
string.'''

String Indexing and Slicing

Indexing allows you to access individual characters in a string, using a zero-based index. Negative indexing starts from the end.

Slicing allows you to extract a part of the string by specifying a range.


text = "Python"
print(text[0])  # Output: 'P' (first character)
print(text[-1])  # Output: 'n' (last character)
print(text[1:4])  # Output: 'yth' (characters from index 1 to 3)

Common String Methods

Python provides several built-in methods to manipulate and work with strings. Here are some common methods:

lower(): Converts the string to lowercase.

upper(): Converts the string to uppercase.

strip(): Removes leading and trailing spaces.

replace(old, new): Replaces occurrences of a substring.

split(delimiter): Splits the string into a list of substrings.

join(iterable): Joins elements of an iterable (like a list) into a single string.


text = "  Hello, Python!  "
print(text.lower())  # Output: "  hello, python!  "
print(text.upper())  # Output: "  HELLO, PYTHON!  "
print(text.strip())  # Output: "Hello, Python!"
print(text.replace("Python", "World"))  # Output: "  Hello, World!  "

String Concatenation

You can concatenate strings using the + operator or join multiple strings with spaces using join().


first_name = "John"
last_name = "Taylor"
full_name = first_name + " " + last_name  # Concatenation
print(full_name)  # Output: "John Taylor"

String Formatting

You can insert variables into strings using several methods:

1. F-strings (Python 3.6+):


name = "John"
age = 35
print(f"My name is {name} and I am {age} years old.")

2. format() method:


print("My name is {} and I am {} years old.".format(name, age))

3. Percent (%) formatting:


print("My name is %s and I am %d years old." % (name, age))

Python number

In Python, a number is a built-in data type used to represent numeric values. Python supports various types of numbers:

1. Integers (int)

These are whole numbers, positive or negative, without any fractional part.

Example


x = 20  # Integer
y = -10  # Negative integer

2. Floating-point numbers (float)

These are numbers with decimal points or in exponential notation.

Example:


x = 20.5  # Floating-point number
y = -5.7  # Negative float
z = 3.5e2  # 3.5 * 10^2 (Exponential notation, equivalent to 350.0)

3. Complex numbers (complex)

These numbers are written in the form a + bj, where a is the real part, and b is the imaginary part.

Example:


x = 3 + 4j  # Complex number
y = 2j  # Pure imaginary number

Type conversion between numbers

You can convert between different number types using the int(), float(), and complex() functions.

Example:


x = 5      # Integer
y = float(x)  # Converts x to 5.0 (float)

z = 3.6
w = int(z)  # Converts z to 3 (integer, truncating the decimal part)

c = complex(x, y)  # Converts to complex number (5+5.0j)

Python Recursion function

Recursion is a programming technique where a function calls itself to solve a problem. A recursive function generally has two main components:

1. Base Case: This stops the recursion when a certain condition is met.

2. Recursive Case: This part breaks the problem down into smaller instances of the same problem and makes a recursive call.

Example: recursive function to calculate the factorial of a number


def factorial(n):
    # Base case: if n is 1 or 0, return 1
    if n == 0 or n == 1:
        return 1
    # Recursive case: n * factorial of (n-1)
    else:
        return n * factorial(n - 1)

# Example usage:
print(factorial(5))  # Output will be 120

Explanation:

The base case is when n == 0 or n == 1, which returns 1.

The recursive case reduces the problem by calling factorial(n-1).

Python Lambda function

In Python, a lambda function is a small, anonymous function defined using the lambda keyword. It can take any number of arguments but can only have a single expression. The expression is evaluated and returned when the function is called.

Lambda functions are often used for short, simple operations where defining a full function using def might feel unnecessary.

Syntax:


lambda arguments: expression

arguments: Input to the lambda function (can be multiple arguments, separated by commas).

expression: The operation that will be performed and returned.

Example 1: Basic Lambda Function


# A lambda function that adds 20 to a given number
add = lambda x: x + 20
print(add(10))  # Output: 30

Explanation: The lambda function takes one argument (x) and returns x + 20.

Example 2: Lambda with Multiple Arguments


# A lambda function that adds two numbers
add = lambda a, b: a + b
print(add(5, 7))  # Output: 12

Explanation: The lambda function takes two arguments (a and b) and returns their sum.

Example 3: Using Lambda with map()

The map() function applies a function to every item in an iterable (like a list). You can use a lambda function here to apply a quick transformation.


numbers = [1, 2, 3, 4, 5]
squared = map(lambda x: x**2, numbers)
print(list(squared))  # Output: [1, 4, 9, 16, 25]

Explanation: The lambda function squares each number in the numbers list.

Example 4: Using Lambda with filter()

The filter() function filters elements from an iterable based on a condition provided by a lambda function.


numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers))  # Output: [2, 4]

Explanation: The lambda function filters out even numbers from the list.

Example 5: Lambda with Default Arguments

Just like regular functions, lambda functions can have default arguments.


multiply = lambda a, b=2: a * b
print(multiply(3))    # Output: 6 (3 * 2)
print(multiply(3, 4)) # Output: 12 (3 * 4)

When to Use Lambda Functions:

When you need a small, simple function that is used only once or for a short time.

When passing a function as an argument to higher-order functions like sorted(), map(), or filter().

Limitations:

Lambda functions are restricted to a single expression and cannot contain complex logic.

They are often used for simple operations but may reduce code readability if overused.

Python function

In Python, a function is a block of reusable code that performs a specific task. Functions are defined using the def keyword, followed by the function name, parentheses (), and a colon :. Optionally, functions can take parameters, return values, and include a return statement to pass back a result.

1. Defining a Function

Syntax:


def function_name(parameters):
    # write code block (body of the function)
    return some_value  # (optional)

2. Calling a Function

To call (or invoke) a function, you simply use its name followed by parentheses. If the function accepts parameters, you pass them inside the parentheses.


function_name(arguments)  # Call the function

Example1: Function without Parameters


def greet():
    print("Hello, world!")

greet()  # Function call

Output:

Hello, world!

Example 2: Function with Parameters


def greet(name):
    print(f"Hello, {name}!")

greet("John")  # Function call with an argument

Output:

Hello, John!

Example 3: Function with Return Value


def sumNumbers(a, b):
    return a + b

result = sumNumbers(10, 5)  # Function call and storing the return value
print(result)

Output:

15

Explanation: The sumNumbers function takes two arguments (a and b), adds them, and returns the result. The returned value is stored in result, which is then printed.

Example 4: Function with Default Parameters

You can define functions with default parameter values. If you don’t provide arguments for these parameters, the default values will be used.


def greet(name="John"):
    print(f"Hello, {name}!")

greet()  # Uses default parameter
greet("Tom")  # Overrides default parameter

Output:

Hello, John!
Hello, Tom!

Example 5: Function with Multiple Return Values

Python allows a function to return multiple values as a tuple.


def get_dimensions():
    length = 7
    width = 5
    return length, width  # Returning multiple values

l, w = get_dimensions()  # Unpacking the returned values
print(f"Length: {l}, Width: {w}")

Output:

Length: 7, Width: 5

Example 6: Function with Keyword Arguments

When calling a function, you can specify arguments by name, which allows you to pass them in any order.


def person(name, age):
    print(f"{name} is {age} years old.")

person(name="John", age=35)  # Keyword arguments

Output:

John is 35 years old.

Python pass statement

In Python, the pass statement is a null operation—it does nothing. It’s used as a placeholder in situations where code is required syntactically but you don’t want to execute anything. It can be useful when defining a structure that you will implement later, such as functions, loops, or classes.

Usage of pass

1. In an empty function:


def my_function():
    pass

Explanation: Here, pass is used because the function is defined but not yet implemented. Without pass, Python would throw an error.

2. In a loop:


for i in range(5):
    pass

Explanation: This loop does nothing; it just iterates from 0 to 4. The pass statement is a placeholder where the code could be added later.

3. In a class definition:


class MyClass:
    pass

Explanation: The pass statement allows you to define the class without implementing any methods or attributes.

4. With conditional statements:


x = 10
if x > 5:
    pass
else:
    print("x is 5 or less")

Explanation: If the condition is true (x > 5), the pass the statement is executed, meaning nothing happens. The else the block would still function normally.

When to use pass:

1. As a placeholder for code, you’ll implement it later.

2. To ensure the code is syntactically correct while you’re still planning or developing it.

Python nested loop

In Python, a nested loop refers to having one loop inside another. Both loops can be of the same type (for or while) or different types. Nested loops are useful for iterating over multi-dimensional data, such as matrices or lists of lists, or performing operations where one set of iterations is dependent on another.

Usage of nested loop

Example 1: Nested for loops


for i in range(3):  # Outer loop
    for j in range(2):  # Inner loop
        print(f"i = {i}, j = {j}")

Explanation: The outer loop (i) runs 3 times, and for each iteration of i, the inner loop (j) runs 2 times. This results in a total of 6 iterations in total.

Output:

i = 0, j = 0
i = 0, j = 1
i = 1, j = 0
i = 1, j = 1
i = 2, j = 0
i = 2, j = 1

Example 2: Nested while loop


i = 1
while i <= 3:  # Outer loop
    j = 1
    while j <= 2:  # Inner loop
        print(f"i = {i}, j = {j}")
        j += 1
    i += 1

Explanation: The outer while loop runs as long as i is less than or equal to 3, and the inner while loop runs twice for each value of i. Each iteration of the inner loop increments j, and after the inner loop completes, the outer loop increments i.

Python continue statement

In Python, the continue statement is used to skip the current iteration of a loop and proceed with the next one. Unlike break, which exits the loop entirely, continue simply jumps to the next iteration.

Usage of continue statement

continue in a while loop:


i = 0
while i < 10:
    i += 1
    if i % 2 == 0:  # Skip even numbers
        continue
    print(i)

Explanation: The loop increments i in each iteration, but when i is even, the continue the statement is triggered, skipping the rest of the loop body for that iteration. This prints only odd numbers.

continue in a for loop:


for i in range(1, 10):
    if i == 5:  # Skip the number 5
        continue
    print(i)

Explanation: The loop iterates through numbers from 1 to 10 but skips printing the number 5 due to the continue statement.

continue with if conditions:


for char in "hello world":
    if char == "o":  # Skip when 'o' is encountered
        continue
    print(char)

Explanation: When the letter 'o' is encountered in the string, the continue statement skips printing it and moves to the next character.

Python Break statement

In Python, you can use the break statement to exit a loop prematurely when a specific condition is met. It is commonly used in while or for loops. Here’s how you can use break conditions in different scenarios:

1. Using break in a while loop:


i = 0
while True:
    print(i)
    i += 1
    if i == 5:  # Break condition
        break

Explanation: This loop runs indefinitely (while True) but breaks when i reaches 5.

2. Using break in a for loop:


for i in range(5):
    if i == 3:  # Break condition
        break
    print(i)

Explanation: This loop iterates through numbers from 0 to 4 but stops (breaks) when i equals 3.

3. Using break with an if condition:


numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in numbers:
    if num > 5:  # Break condition
        break
    print(num)

Explanation: The loop prints numbers until it encounters a number greater than 5, then exits the loop.

Python while loop

A while loop in Python repeatedly executes a block of code as long as a specified condition is True. The loop will stop when the condition becomes False.

Syntax:


while condition:
    # Code to execute repeatedly

Example 1: Basic while loop


count = 0
while count < 4:
    print(count)
    count = counter + 1  # Increment the counter

Output:

0 1 2 3

In this example, the loop continues until count is no longer less than 4.

Example 2: while loop with break

You can use the break statement to exit the loop early.


i = 0
while i < 5:
    print(i)
    if i == 3:
        break  # Exit the loop when i is 3
    i += 1

Output:

0 1 2

Example 3: while loop with else

Similar to a for loop, you can use an else clause with a while loop. The else the block will run if the loop completes without encountering a break.


i = 0
while i < 3:
    print(i)
    i += 1
else:
    print("Loop finished!")

Output:

0
1
2

Example 4: Infinite loop

A while loop can become an infinite loop if the condition never becomes False.


while True:
    print("This is an infinite loop!")
    break  # Use a break to avoid an actual infinite loop

Without the break, this would print endlessly. Infinite loops are commonly used when waiting for specific conditions to occur.

Python for loop

In Python, a for loop is used to iterate over a sequence (such as a list, tuple, string, or range) and execute a block of code repeatedly for each item in the sequence.

Basic syntax of a for loop:


for item in sequence:
    # Code to execute for each item

Example 1: Loop through a list


fruits = ['apple', 'banana', 'orange']
for fruit in fruits:
    print(fruit)

Output:

apple
banana
orange

Example 2: Using range() to loop through a sequence of numbers


for i in range(5):
    print(i)

Output:

0
1
2
3
4

Note: range(5) generates a sequence of numbers from 0 to 4 (5 numbers).

Example 3: Loop through a string


name= "John"
for letter in name:
    print(letter)

Output:

J
o
h
n

Example 4: Using else with a for loop

The else clause is optional and will be executed after the loop finishes unless the loop is terminated by a break statement.


for i in range(3):
    print(i)
else:
    print("Finished!")

Output:

0
1
2
3
Finished!

Python conditions

In Python, an if-else condition allows the program to make decisions based on whether a certain condition is True or False. It determines which block of code to execute depending on the evaluation of a condition.

Syntax:


if condition:
    # Code to execute if condition is True
else:
    # Code to execute if condition is False

Explanation:

if condition: If the condition is True, the block of code inside the if the statement is executed.

else: If the if condition is False, the block of code inside the else the statement is executed.

Example:


age = 21

if age >= 21:
    print("You are Adult.")
else:
    print("You are not Adult.")

In this example:

If the age is 21 or more, the output will be "You are Adult."

Otherwise, the output will be "You are not Adult."

elif (else if) for Multiple Conditions:

You can also use elif to check multiple conditions:


x = 20

if x > 20:
    print("x is greater than 20")
elif x == 20:
    print("x is exactly 20")
else:
    print("x is less than 20")

Nested if statements:

You can also nest if statements within each other:


x = 20
y = 30

if x > 10:
    if y > 15:
        print("x is greater than 10 and y is greater than 15")

Short-hand if (Ternary Operator):


x = 10
print("x is greater than 5") if x > 5 else print("x is 5 or less")

Python operators

Python operators are special symbols or keywords used to perform operations on variables and values. Operators are used to manipulate data in various ways, such as performing arithmetic, comparisons, or logical operations. Python has several types of operators:

1. Arithmetic Operators:

These are used to perform basic mathematical operations like addition, subtraction, multiplication, etc.

Operator Description Example
+ Addition 10+2 →12
Subtraction 10-2 → 8
* Multiplication 10*2 → 20
/ Division (returns float) 10/2 → 5
// Floor Division (returns int) 11 // 2 → 5
% Modulus (remainder) 11 % 2 → 1
** Exponentiation (power) 10 ** 2 → 100

2. Comparison (Relational) Operators:

These operators compare two values and return a Boolean result (True or False).

Operator Description Example
== Equal to 10 == 10 → True
!= Not equal to 10 != 2 → True
> Greater than 10 > 2 → True
< Less than 10 < 2 → False
>= Greater than or equal 10 >= 2 → True
<= Less than or equal 10 <= 2 → False
** Exponentiation (power) 10 ** 2 → 100

3. Assignment Operators:

These operators are used to assign values to variables.

Operator Description Example
= Assign x = 10
+= Add and assign x += 10 → x = x + 10
-= Subtract and assign x -= 10 → x = x – 10
*= Multiply and assign x *= 10 → x = x * 10
/= Divide and assign x /= 10 → x = x / 10
% Modulus (remainder) 11 % 2 → 1
//= Floor divide and assign x //= 10 → x = x // 10
%= Modulus and assign x %= 10 → x = x % 10
**= Exponentiation and assign x **= 10 → x = x ** 10

4. Logical Operators:

These operators are used to perform logical operations and return Boolean values.

Operator Description Example
and Logical AND (both conditions must be True) True and False → False
or Logical OR (at least one condition is True) True or False → True
not Logical NOT (negates the condition) not True → False

5. Bitwise Operators:

These operators work on bits and perform bit-by-bit operations.

Operator Description Example
& Bitwise AND 5 & 3 → 1
or Logical OR (at least one condition is True) True or False → True
Bitwise OR
^ Bitwise XOR 5 ^ 3 → 6
~ Bitwise NOT ~5 → -6
<< Left Shift 5 << 1 → 10
>> Right Shift 5 >> 1 → 2

6. Identity Operators:

These operators check if two objects are identical (i.e., refer to the same memory location).

Operator Description Example
is True if both refer to the same object x is y
is not True if they refer to different objects x is not y

7. Membership Operators:

These operators test if a sequence (like a list, string, or tuple) contains a value.

Operator Description Example
in True if value is in sequence ‘a’ in [‘a’, ‘b’, ‘c’] → True
not in True if value is not in sequence ‘x’ not in [‘a’, ‘b’, ‘c’] → True

8. Ternary (Conditional) Operator:

This operator allows you to execute expressions conditionally in a single line.

Syntax:


result = value_if_true if condition else value_if_false

Example:


age = 20
status = "Adult" if age >= 18 else "Minor"
print(status)  # Output: Adult

Python input/output

In Python, input/output (I/O) refers to the way a program interacts with the user or external resources like files. It includes receiving data (input) from users, files, or other sources and displaying or writing data (output) to the screen or other external destinations.

1. Input in Python:

The input() function allows you to capture data entered by the user from the keyboard. The data entered is always returned as a string, so you might need to convert it to other types, such as int or float.

Example:


firstName = input("Enter your first name: ")  # Takes user input as a string
print("Hello, " + firstName )            # Output: Hello, 

Converting Input Data: If the user enters a number, you can convert it to an integer or float:


age = int(input("Enter your age: "))  # Convert input to an integer
print("You are", age, "years old.")

2. Output in Python:

Python uses the print() function to display output to the console. You can display strings, variables, and even formatted text.

Example


print("Hello, Friends!")  # Output: Hello, Friends!

Using Variables:


name = "John"
age = 35
print("Name:", name, "Age:", age)  # Output: Name: John Age: 35

Formatted Strings: You can use formatted strings (f-strings) to create more complex output.


name = "John"
age = 35
print(f"My name is {name} and I am {age} years old.")
# Output: My name is John and I am 35 years old.

Python TypeConversion

Type conversion in Python refers to converting a variable from one data type to another. Python provides two types of type conversion:

  1. Implicit Type Conversion (Automatic)
  2. Explicit Type Conversion (Type Casting)

Implicit Type Conversion (Automatic):

This occurs automatically when Python converts one data type to another without any explicit instruction from the user. Python does this to avoid data loss and errors when working with mixed types. Generally, Python will convert smaller types to larger types (e.g., int to float).


# Implicit type conversion from int to float
intNum = 20
floatNum = 15.0

# Adding int and float results in a float
result = intNum + floatNum 
print(result)          # Output: 35.0
print(type(result))    # Output: 

In the example above, Python automatically converts intNum (an integer) to a float because the result of adding an integer and a float must be a float.

Explicit Type Conversion (Type Casting):

This is when you manually convert one data type to another using Python’s built-in functions. You can convert data types such as integers, floats, strings, and more.

Common Type Casting Functions:

int(): Converts a value to an integer.


x = int(5)   # x will be 5
y = int(10.8) # y will be 10
z = int("20") # z will be 20

float(): Converts a value to a float.


x = float(5)     # x will be 5.0
y = float(10.8)   # y will be 10.8
z = float("4")   # z will be 4.0
w = float("5.2") # w will be 5.2

str(): Converts a value to a string.


x = str("Hello") # x will be 'Hello'
y = str(20)    # y will be '20'
z = str(30.0)  # z will be '30.0'

list(): Converts an iterable (e.g., a string or a tuple) to a list.


name = "hello"
my_list = list(name )
print(my_list)  # Output: ['h', 'e', 'l', 'l', 'o']

range to list type conversion: You can convert an range() object to a list.


my_range = range(1, 5)
my_list = list(my_range)
print(my_list)  # Output: [1, 2, 3, 4]

tuple(): Here’s how to convert other data types to a tuple in Python:

String to Tuple: You can convert a string to a tuple where each character becomes an element.


name= "hello"
my_tuple = tuple(name)
print(my_tuple)  # Output: ('h', 'e', 'l', 'l', 'o')

Range to Tuple: You can convert an range() object to a tuple.


my_range = range(1, 5)
my_tuple = tuple(my_range)
print(my_tuple)  # Output: (1, 2, 3, 4)

Python Data Types

In Python, data types define the type of data a variable can hold. Python has a rich set of built-in data types, categorized into several groups:

1. Numeric Types

int: Represents whole numbers (positive, negative, or zero) without decimals.
Example:


x = 20
y = -10
z = 0

float: Represents numbers with decimals (floating-point numbers).


x = 20.15
y = -10.57

complex: Represents complex numbers with a real and imaginary part.

Example:


z = 5 + 10j

2. Text Type

string (str): A sequence of characters, or simply text. Strings are enclosed in quotes.

Example:


name = "John"
greeting = 'Hello Friends'

3. Sequence Types(Ordered collections)

Collections of items that follow a specific order.

list: A flexible, ordered collection of items. You can add, remove, or change items in a list. It can hold different data types.
Example:


fruits = ["apple", "banana", "orange", 75]

Tuple: Similar to a list, but immutable (you can’t change it after it’s created). It’s faster and safer for fixed collections.

Example:


coordinates = (10.1, 20.5, 30.5)

Range: Represents a sequence of numbers. Commonly used in loops.

Example


range(0, 10) represents numbers from 0 to 9.

4. Mapping Type

Dictionary (dict): Stores key-value pairs. You use keys (unique) to access the values.


person = {"name": "John", "age": 35}

To access the name:


person["name"] gives "John".

5. Set Types (Unordered collections)

Collections where each item must be unique.

Set: An unordered collection of unique items (no duplicates allowed).


my_set = {1, 2, 3, "apple"}

Frozen Set: An immutable version of a set. Once created, it cannot be changed.


frozen_set = frozenset([1, 2, 3])

6. Boolean Type

Boolean (bool): Represents True or False

Example:


is_loggedin= true, is_open = false

7. Binary Types

binary types are used to handle and manipulate binary data (sequences of bytes). Binary data typically refers to raw data in its byte form rather than human-readable text. Python provides two primary binary types to represent and work with binary data:

Bytes:

1. An immutable sequence of bytes.

2. Each element in an bytes object is an integer ranging from 0 to 255, representing an 8-bit byte.

3. Useful for representing raw binary data, such as reading from or writing to a file in binary mode or handling binary network protocols.

4. You define bytes by prefixing a string literal with a b.


binary_data = b"hello"  # 'b' indicates this is a bytes object
print(binary_data)      # Output: b'hello'
print(binary_data[0])   # Output: 104 (ASCII value of 'h')

Bytearray:

1. A mutable sequence of bytes.

2. Similar to bytes, but allows for modification of its elements.

3. Often used when you need to manipulate or modify binary data, such as changing bytes or appending new data.

4. You can create a bytearray from an bytes object or from an iterable of integers.

Example:


mutable_binary_data = bytearray(b"hello")
print(mutable_binary_data)  # Output: bytearray(b'hello')

# Modify the first byte
mutable_binary_data[0] = 72  # ASCII value of 'H'
print(mutable_binary_data)   # Output: bytearray(b'Hello')

Memoryview:

1. A memoryview object exposes the buffer interface, allowing you to work with large datasets without copying the data.

2. Useful for slicing and modifying parts of large binary objects without creating new copies.

Example


binary_data = b"hello world"
mv = memoryview(binary_data)
print(mv[0:5])  # Output: 
print(mv.tobytes())  # Output: b'hello world'

8. None Type

None: Represents the absence of a value or a null value. It is often used as a default value or a placeholder.

Example:


result = None

Python variable

A Python variable is a name that refers to a value or object in memory. Variables are used to store data that can be manipulated or referenced throughout a program. In Python, you can assign values to variables without explicitly declaring their data types (Python is dynamically typed), and the type is inferred from the value assigned.

Variable Assignment

In Python, you can create a variable by simply assigning a value to it using the = operator.

Example:


x = 20          # Assigns integer 20 to variable x
name = "John"  # Assigns string "John" to variable name
pi = 3.14       # Assigns floating-point number 3.14 to variable pi

Rules for Naming Variables

1. Variable names must start with a letter (a-z, A-Z) or an underscore _.

2. They can contain letters, digits (0-9), and underscores.

3. Variable names are case-sensitive (name, Name, and NAME are different variables).

4. Python reserved keywords (such as if, while, for, etc.) cannot be used as variable names.

Example:


age = 38 # Integer variable
height = 6.2  # Float variable
is_employee = True  # Boolean variable
greeting = "Hello, Friends!"  # String variable

Reassigning Variables

Variables can be reassigned to new values at any time. Since Python is dynamically typed, the type of a variable can also change when reassigned.


x = 10      # x is an integer
x = "Hello!"  # Now x is a string

Variables are essential for storing and manipulating data in Python programs.

Python Repl

The Python Interactive Shell, also known as REPL (Read-Eval-Print Loop), is an interactive command-line environment where you can write and execute Python code line by line. It allows you to test out code snippets quickly, making it a useful tool for learning, debugging, and experimenting with Python code.

what each part of REPL means:

1. Read: The shell reads the user’s input (a Python statement or expression).

2. Eval: It evaluates the input, executing the Python code.

3. Print: It prints the result of the expression or statement to the console.

4. Loop: It then loops back to prompt the user for more input.

You can start the Python interactive shell by simply typing python (or python3 depending on your setup) in your terminal or command prompt.

Example: print Hello, Friends!


>>> print("Hello, Friends!")
Hello, World!

Example2: Add two numbers


>>> 5 + 7
12

Python comments

Python comments are used to add notes, explanations, or annotations within the code that the interpreter does not execute. They help improve the readability of the code by explaining what certain parts of the program do. Comments can also be used to temporarily disable code or provide instructions for other developers working on the code.

Types of Comments in Python:

1. Single-line Comments

Single-line comments start with the # symbol. Everything after the # on that line is considered a comment and is ignored by the Python interpreter.

Example:


# This is a single-line comment
x = 20  # This comment is after a statement

2. Multi-line Comments

Python doesn’t have an official multi-line comment syntax like some other programming languages. However, multi-line comments can be achieved using triple quotes (''' or """). While these are technically string literals, if they aren’t assigned to a variable or used in code, Python ignores them, effectively treating them as comments.


'''
This is a multi-line comment.
It spans multiple lines.
'''
print("Hello, World!")

Alternatively, multiple single-line comments can be used for multi-line comments:


# This is a comment
# that spans multiple lines
# using single-line comments.

When to Use Comments:

1. Explain Complex Logic: Use comments to clarify complicated or non-obvious parts of the code.


# Check if the user input is a positive integer
if number > 0:
    print("Positive number")

2. Provide Metadata: Comments can be used at the top of a script to describe metadata like the author’s name, date of creation, and purpose of the program.


# Author: John Taylor
# Date: 2023-07-05
# Description: This script performs basic operations.

3. Disable Code: Temporarily comment out code for debugging or testing purposes.


# print("This line is commented out and won't run.")
print("This line will run.")

Python Syntax

Python syntax refers to the set of rules that define how a Python program is written and interpreted. It dictates how you structure code using elements like variables, functions, loops, and conditionals. Here are some key aspects of Python syntax:

1. Case Sensitivity

Python is case-sensitive, meaning that Var and var are treated as different identifiers.

2. Indentation

Indentation is used to define the structure of code blocks like functions, loops, and conditionals.

Python does not use curly braces {} or keywords like begin/end; it relies on consistent indentation.


if x > 2:
    print("x is greater than 2")

3. Comments

Single-line comments start with a # symbol.


# This is a single-line comment

Multiline comments can be done using triple quotes ''' or """:


'''
This is a
multi-line comment
'''

4. Variables

Variables do not need to be declared with a specific type.

Python dynamically assigns types based on the value assigned.


x = 20      # Integer
y = 5.67    # Float
name = "John"  # String

rest parameter

In JavaScript, the rest parameter syntax allows a function to accept an indefinite number of arguments as an array. It’s useful when you want to handle multiple arguments without explicitly listing them. Three dots denote the rest parameter (...) followed by a parameter name.

Key points:

1. The rest parameter must be the last parameter in the function definition.

2. It collects all remaining arguments passed to the function into an array.

example:


function printValue(a, b, ...others) {
    console.log(a);       // First argument
    console.log(b);       // Second argument
    console.log(others);  // Rest of the arguments as an array
}

printValue(1, 2, 3, 4, 5, 6);
// Output:
// 1
// 2
// [3, 4, 5, 6]

Restrictions:

1. A function can only have one rest parameter.

2. It must be the last parameter in the function’s parameter list.

Example: Handling multiple arguments

You can use the rest parameter to accept a variable number of arguments and perform operations on them.


function multiplyNumber(multiplier, ...numbers) {
    return numbers.map(num => num * multiplier);
}
console.log(multiplyNumber(5, 1, 2, 3, 4, 5)); // Output: [5, 10, 15, 20, 25]

In this example, the multiplyNumber function takes a multiplier as the first argument and multiplies the rest of the arguments by this multiplier.

Python installation

Python is available for all major platforms, including Windows, macOS, and Linux. Here’s a guide to installing Python on each platform and setting it up properly.

1. Installing Python on Windows

Step 1: Download Python

1. Visit the official Python website: python.org/downloads.

2. Download the latest stable version of Python. The website usually detects your operating system automatically, so you’ll be offered the correct installer for Windows.

Step 2: Run the Installer

1. Locate the installer in your downloads folder and run it.

2. During installation, check the box that says “Add Python to PATH” (this is crucial for command line use).

3. Click on “Install Now” or customize the installation if needed.

4. Wait for the installation to complete and click “Close” when finished.

Step 3: Verify the Installation

1. Open the Command Prompt by typing cmd in the search bar.

2. Type the following command to check if Python is installed correctly


python --version

Note:- If Python is installed successfully, the version number will be displayed.

Step 4: Install pip (Python Package Installer)

Pip is bundled with modern Python installations, but you can verify by typing:


pip --version

Note: If installed, it will display the pip version. If not, you can download it from here.

2. Installing Python on Linux

Most Linux distributions come with Python pre-installed. However, if you need a specific version, you can install or upgrade Python.

Step 1: Update Package List

Open the Terminal and run:


sudo apt update

Step 2: Install Python

For Ubuntu or Debian based distributions, you can install Python using:


sudo apt install python3

Step 3: Verify Installation

After installation, verify it with:


python3 --version

Step 4: Install pip (if not installed)

To install pip, run:


sudo apt install python3-pip

3. Installing Python on macOS

Step 1: Install Homebrew (Optional but Recommended)

Homebrew is a package manager for macOS. It simplifies installing software, including Python. To install Homebrew:

1. Open the Terminal.

2. Run the following command


/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Step 2: Install Python using Homebrew

If you have Homebrew installed, open the Terminal and run:


brew install python

This will install the latest version of Python along with pip.

Step 3: Verify the Installation

After installation, verify it by running:


python3 --version

4. Setting up your development environment

Once Python is installed, you can set up your development environment. Here are a few popular tools:

Text Editors and IDEs:

VS Code: A lightweight, highly customizable code editor with Python support.

PyCharm: A full-fledged Python IDE with features like debugging, testing, and version control.

Sublime Text: A minimalistic text editor with Python plugins.

Setting Up a Virtual Environment:

It’s best practice to work within isolated environments for different projects. To create a virtual environment:

1. Install the venv module if not already available


python3 -m pip install --user virtualenv

2. Create a virtual environment


python3 -m venv project_name

3. Activate the virtual environment

On Windows


project_name\Scripts\activate

On macOS/Linux


source project_name/bin/activate

Installing Packages:

Once the environment is active, you can install Python packages using pip. For example:


pip install numpy

5. Running Python Programs

After setting up Python, you can run Python programs directly from the command line or your text editor.

Example:

Save the following code in a file named hello.py:


print("Hello, World!")

To run the program, navigate to the folder where the file is located and use the following command:


python hello.py

What is Python

Python is a high-level, interpreted programming language known for its simplicity and readability. Created by Guido van Rossum and first released in 1991, Python has grown into one of the most popular programming languages in the world due to its versatility and ease of use. Python emphasizes code readability, making it an excellent language for beginners, while its powerful libraries and frameworks also cater to advanced use cases.

Key Features of Python:

1. Simple and Readable Syntax: Python’s syntax is clean and easy to understand, making it accessible for both beginners and experienced developers.

2. Interpreted Language: Python code is executed line-by-line, allowing for quick testing and debugging without compilation.

3. Cross-Platform: Python runs on various platforms like Windows, macOS, and Linux without requiring changes to the source code.

4. Dynamically Typed: You don’t need to declare the type of a variable in advance. Python determines the data type at runtime, making the code more flexible.

5. Extensive Libraries and Frameworks: Python has a rich set of libraries (like NumPy, pandas, Matplotlib) and frameworks (like Django, and Flask) that simplify development across different domains.

6. Versatile: Python can be used in various fields including web development, data analysis, artificial intelligence, scientific computing, automation, and more.

Example: simple Python program that prints “Hello, World!”:


print("Hello, World!")

Key Concepts in Python:

Variables and Data Types: Python supports various data types like integers, floats, strings, lists, tuples, and dictionaries.

Control Flow: Python supports conditional statements (if-else) and loops (for, while).

Functions: Python allows the creation of reusable code blocks with functions using the def keyword.

Object-Oriented Programming: Python is an object-oriented language, that allows you to define classes and create objects.

map method

The map method in JavaScript is an array method used to create a new array by applying a provided function to each element in the original array. It’s particularly useful when you need to transform or process each element and return a new array with the results, leaving the original array unchanged.

Syntax:


const newArray = array.map(function(element, index, array) {
  // Return transformed element here
}, thisArg);

element: The current element being processed in the array.

index (optional): The index of the current element being processed.

array (optional): The array map was called upon.

thisArg (optional): A value to use as this when executing the callback function.

Characteristics of map

1. Returns a New Array: The map method returns a new array containing the results of applying the provided function to each element of the original array.

2. Original Array Unchanged: The original array remains unchanged after the map operation.

3. Transforms Each Element: The function provided to map transforms each element in the array, and the result of this transformation is stored in the new array.

4. Iterates Over Every Element: The function passed to map is called for every element in the array, even if the element is undefined, null, or an empty slot.

5. Chaining: Since map returns a new array, you can chain other array methods like filter, reduce, etc., on the result.

Example:


const numbers = [1, 2, 3, 4];
const updatedNumbers = numbers.map(num => num * 5);

console.log(updatedNumbers); // Output: [5, 10, 15, 20]
console.log(numbers); // Output: [1, 2, 3, 4] (original array is unchanged)

Extracting Specific Properties:


const users = [
    {name: 'John', age: 39},
    {name: 'Tom', age: 38},
    {name: 'Mathew', age: 35}
];

const names = users.map(user => user.name);

console.log(names); // Output: ['John', 'Tom', 'Mathew']

forEach method

The forEeach method in JavaScript is an array method used to execute a provided function once for each element in an array. It’s beneficial when you want to perform side effects such as logging, updating each element in place, or calling functions that do not require returning a new array.

Syntax:


array.forEach(function(element, index, array) {
  // Your code here
}, thisArg);

element: The current element being processed in the array.

index (optional): The index of the current element being processed.

array (optional): The array forEach was called upon.

thisArg (optional): A value to use as this when executing the callback function.

Characteristics of forEach

1. Does Not Return a Value: The forEach method always returns undefined. It doesn’t return a new array, unlike some other array methods like map.

2. Iterates Over Every Element: The function passed to forEach is executed for every element of the array, even if it is undefined, null, or an empty slot.

3. Cannot Be Interrupted: You cannot break out of a forEach loop using break or continue. If you need to stop iteration based on a condition, consider using a for loop or the some or every methods.

4. Modifying the Array: You can modify the original array within the forEach loop by directly altering its elements.

Example: Modify the array element


const numbers = [1, 2, 3, 4];
numbers.forEach((num, index, arr) => {
    arr[index] = num * 5; // multiply by 5 for each element in the original array
});
console.log(numbers); // Output: [5, 10, 15, 20]

find() method

In JavaScript, find methods is used to search for elements in an array based on a given condition.

KeyPoints:

1. The find method returns the value of the first element in the array that satisfies the provided testing function.

2. It returns the first element that matches the condition. If no elements match, it returns undefined.

Example:

1. Filtering Even Numbers:


const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 10];
const data = numbers.find(num => num % 2 === 0);
console.log(data); // Output: 2

2. Filtering string from the array


const fruits = ['Apple', 'Banana', 'Orange'];
const filteredData= fruits.find(fruit => fruit== 'Orange');
console.log(filteredData); // Output: Orange

3. Filtering Objects in an Array


const employees = [
  { name: 'John', age: 38 },
  { name: 'Tom', age: 37 },
  { name: 'Mathew', age: 35 },
  { name: 'Andrew', age: 30 },
];

const data = employees.find(employee => employee.age > 36);
console.log(data); // Output: { name: 'John', age: 38 }

filter() method

In JavaScript, filter is an array method used to search for elements in an array based on a given condition.

Key Points

1. The filter method creates a new array containing all elements that pass the test implemented by the provided function.

2. It returns an array of all elements that satisfy the condition. If no elements match the condition, it returns an empty array.

Example:

1. Filtering Even Numbers:


const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 10];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4, 6, 8 ,10]

2. Filtering Odd Numbers:


const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 10];
const oddNumbers = numbers.filter(num => num % 2 !== 0);
console.log(evenNumbers); // Output: [1, 3, 5, 7]

3. Filtering string from the array


const fruits = ['Apple', 'Banana', 'Orange'];
const filteredData= fruits.filter(fruit => fruit== 'Orange');
console.log(filteredData); // Output: ['Orange']

4. Filtering Objects in an Array


const employees = [
  { name: 'John', age: 38 },
  { name: 'Tom', age: 37 },
  { name: 'Mathew', age: 35 },
  { name: 'Andrew', age: 30 },
];

const data = employees.filter(employee => employee.age > 36);
console.log(data); // Output: [ { name: 'John', age: 38 }, { name: 'Tom', age: 37 } ]