Category: Uncategorized

  • Lesson 2: Generators

    Generators are a powerful feature in Python that allow you to create iterators in a more memory-efficient and readable way. Instead of returning all values at once like regular functions, generators produce items one at a time using the yield keyword. This makes them especially useful when dealing with large datasets or infinite sequences.

    In this lesson, we’ll cover:

    1. Using yieldto Create Generators
    2. Differences Between Generators and Functions

    1. Using yield to Create Generators

    What is a Generator?

    A generator is a special type of function that returns an iterator. Instead of using the return statement, a generator uses the yield keyword to produce a value and pause the function’s state. This means the function can be resumed later from where it left off.

    Why Use Generators?

    • Memory Efficient:They don’t store the entire sequence in memory.
    • Lazy Evaluation:Values are generated only when needed.
    • Readable Code:Cleaner syntax compared to managing state manually.

    Creating a Simple Generator:

    python
    def simple_generator():

    yield 1

    yield 2

    yield 3

     

    # Using the generator

    for value in simple_generator():

    print(value)

     

    Explanation:

    • The function simple_generatoryields three values: 1, 2, and 3.
    • Each time yieldis encountered, the function pauses, saving its current state.
    • The next iteration resumes from where it left off.

    Generator with Loop:

    python
    def count_up_to(n):

    count = 1

    while count <= n:

    yield count

    count += 1

     

    for num in count_up_to(5):

    print(num)

     

    Key Points:

    • The generator count_up_toyields numbers from 1 to n.
    • No list is created in memory, making it efficient for large ranges.

    2. Differences Between Generators and Functions

    Aspect Regular Functions Generators
    Keyword Used Uses return to send back a value. Uses yield to produce values one at a time.
    Memory Usage Stores entire data in memory. Generates values on the fly (memory efficient).
    Execution Runs to completion when called. Pauses at yield and resumes later.
    Return Value Returns a single value or data structure. Returns a generator object (iterator).
    State Retention Does not retain state between calls. Retains state between yield calls.

    Example: Function vs. Generator

    • Regular Function:
    python
    def get_numbers():

    return [1, 2, 3, 4, 5]

     

    for num in get_numbers():

    print(num)

     

    • Generator:
    python
    def generate_numbers():

    for i in range(1, 6):

    yield i

     

    for num in generate_numbers():

    print(num)

     

    Memory Usage:

    • The function get_numbers()creates and stores the entire list in memory.
    • The generator generate_numbers()yields one number at a time, reducing memory usage.

    Real-World Use Cases for Generators:

    1. Large Data Processing:Reading large files without loading the entire file into memory.
    2. Streaming Data:Processing data streams like logs, API responses, or real-time feeds.
    3. Infinite Sequences:Generating endless series (e.g., Fibonacci numbers) without memory overflow.

    Example – Reading Large Files Efficiently:

    python
    def read_large_file(file_path):

    with open(file_path, ‘r’) as file:

    for line in file:

    yield line.strip()

     

    for line in read_large_file(‘bigdata.txt’):

    print(line)

     

    Why It’s Efficient:

    • Only one line is loaded into memory at a time, making it scalable for large datasets.

    Summary

    • Generatorsallow you to write memory-efficient code using the yield
    • They differ from regular functions by pausing and resuming execution without losing state.
    • Generators are ideal for large data processing, streaming applications, and infinite sequences.

    In the next lesson, we’ll explore Context Managers to handle resource management more effectively.

  • Lesson 2: Inheritance

    In this lesson, we will explore Inheritance, a key concept in Object-Oriented Programming (OOP) that allows one class to inherit attributes and methods from another. Inheritance promotes code reusability and helps in creating a hierarchical relationship between classes. By understanding inheritance, you will be able to create more complex classes by extending existing ones and customizing them as needed.

    By the end of this lesson, you will have a strong understanding of:

    • Base and derived classes
    • Method overriding
    • Method Resolution Order (MRO)

    1. Base and Derived Classes

    In Inheritance, we have two types of classes:

    • Base Class (Parent Class): This is the class that provides the common attributes and methods to be inherited.
    • Derived Class (Child Class): This is the class that inherits from the base class and can have additional features or can modify the inherited behavior.
    Syntax:

    To define a derived class, you specify the base class in parentheses when defining the new class.

    python
    class DerivedClassName(BaseClassName):

    # Additional attributes or methods

     

    Example:
    python
    # Base Class (Parent)

    class Animal:

    def __init__(self, name):

    self.name = name

     

    def speak(self):

    print(f”{self.name} makes a sound”)

     

    # Derived Class (Child)

    class Dog(Animal):

    def __init__(self, name, breed):

    # Call the __init__ method of the base class

    super().__init__(name)

    self.breed = breed

     

    def speak(self):

    print(f”{self.name} barks”)

     

    In this example:

    • Animalis the base class with an __init__() method and a speak()
    • Dogis the derived class that inherits from Animal. It has an additional attribute breed, and the speak() method is overridden.
    Instantiating Objects:
    python
    # Creating an object of the derived class

    dog1 = Dog(“Buddy”, “Golden Retriever”)

     

    # Accessing inherited method

    dog1.speak()  # Output: Buddy barks

     

    • The Dogclass inherits the __init__() and speak() methods from the Animal However, the speak() method is overridden in the Dog class.

    2. Overriding Methods

    Method overriding is a feature that allows a derived class to provide a specific implementation of a method that is already defined in its base class. When a derived class defines a method with the same name as the one in the base class, the method in the derived class overrides the one in the base class.

    Example:
    python
    # Creating objects of derived classes

    dog1 = Dog()

    cat1 = Cat()

     

    # Calling the overridden methods

    dog1.speak()  # Output: Dog barks

    cat1.speak()  # Output: Cat meows

     

    • The Dogand Cat classes provide their own specific implementation of the speak() method, which overrides the base class’s speak()

    3. The super() Function

    The super() function is used to call a method from the base class inside the derived class. It allows you to call the constructor or any method of the base class, which is particularly useful when you want to extend the behavior of the base class without completely overriding it.

    Example:
    python
    class Animal:

    def __init__(self, name):

    self.name = name

     

    def speak(self):

    print(f”{self.name} makes a sound”)

     

    class Dog(Animal):

    def __init__(self, name, breed):

    # Using super() to call the __init__ method of Animal

    super().__init__(name)

    self.breed = breed

     

    def speak(self):

    print(f”{self.name} barks”)

     

    In this example:

    • Both Dogand Cat override the speak() method of the Animal
    Using the Overridden Methods:
    • The super().__init__(name)in the Dog class calls the __init__() method of the Animal class to initialize the name This prevents duplicating the __init__() method and promotes code reuse.

    4. Method Resolution Order (MRO)

    Method Resolution Order (MRO) defines the order in which methods are inherited in multiple inheritance scenarios. When a derived class inherits from multiple base classes, the MRO determines which method is called first.

    • Python uses an algorithm called C3 Linearizationto determine the method resolution order in the case of multiple inheritance.
    • The mro()method can be used to display the method resolution order of a class.
    Example of MRO:

    python

    CopyEdit

    class A:

    def speak(self):

    print(“A speaks”)

     

    class B(A):

    def speak(self):

    print(“B speaks”)

     

    class C(A):

    def speak(self):

    print(“C speaks”)

     

    class D(B, C):

    pass

     

    # Creating an object of class D

    obj = D()

    obj.speak()  # Output: B speaks

     

    # Displaying the Method Resolution Order

    print(D.mro())

     

    • In this example, Dinherits from both B and C, which in turn inherit from A. When speak() is called, Python uses the MRO to determine that the speak() method from B is called first, because B appears before C in the inheritance hierarchy.

    The output of D.mro() will show the order in which Python looks for methods when searching for an attribute or method. The MRO is:

    kotlin
    [<class ‘__main__.D’>, <class ‘__main__.B’>, <class ‘__main__.C’>, <class ‘__main__.A’>, <class ‘object’>]

     

    5. Multiple Inheritance

    Multiple inheritance allows a class to inherit from more than one base class. This can be useful when a class needs to combine behavior from different sources.

    Example of Multiple Inheritance:
    python
    class Animal:

    def speak(self):

    print(“Animal speaks”)

     

    class Flying:

    def fly(self):

    print(“Flying in the sky”)

     

    class Bird(Animal, Flying):

    pass

     

    # Creating an object of the Bird class

    bird = Bird()

     

    bird.speak()  # Output: Animal speaks

    bird.fly()  # Output: Flying in the sky

     

    • In this example, the Birdclass inherits from both Animal and Flying. As a result, Bird objects have access to both the speak() method from Animal and the fly() method from Flying.

    6. Conclusion

    In this lesson, we have covered the following key concepts:

    • Base and Derived Classes: A base class is the class being inherited from, while the derived class is the class that inherits the properties and methods.
    • Overriding Methods: You can override methods in a derived class to provide specific behavior.
    • The super()Function: The super() function allows you to call methods from the base class to reuse code and extend functionality.
    • Method Resolution Order (MRO): In multiple inheritance, MRO determines the order in which methods are resolved.
    • Multiple Inheritance: A class can inherit from multiple classes, gaining the combined features of all base classes.

    Inheritance is a powerful feature of Python’s object-oriented programming, enabling code reusability and a structured approach to modeling real-world entities. In the next lesson, we will explore more OOP concepts like Polymorphism and Encapsulation.

  • echo "Hello, World!";

  • What is Next.js

    Next.js is one of the framework of the React, where React (is a JS library) provide you to beautiful, scalable and fast single page application. Next.js is an open source framework which gives you the best developer experience such as hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching and more without No config needed.

    Benefits of Next.Js

    As we know Next.js built on top the React library so it benefits and provide some features such as:

    • Image Optimization
    • Provide Next.js Analytics
    • Zero Config
    • Hybrid SSG and SSR
    • Incremental Static Regeneration
    • TypeScript Support
    • Fast Refresh
    • File System Routing
    • API Routes
    • Built-in CSS Support
    • Code-splitting and Bundling

    Why choose Next.Js

    The very basic question in users mind why Next.js if there is already React library available. The question is general and valid because Next.js is built on top of React Js and React is used by many top Tech Giants.

    Some of Tech Giants using React are:

    • Facebook
    • Instagram
    • Discord
    • Netflix and
    • Uber and more…

    Next.js built on React and it inherits the features of React Js and it is more times powerful than ReactJS and SEO friendly.
    So the question is. Do we need to move from React to Next.js?
    As we discussed Next.js built on react so we do not move from React when using the Next.js because it has 90% similar syntax like React but Next.js provides you to more features rather than using ReactJs.

    Some Features of Next.js are:

    Static Site Generation:

    When your project is ready to host in production you will bundle up your project, during the bundling process, Next.js will make network request fetch all data, and build HTML pages for your project.

    Server Side Generation:

    Next.js provide you to the server side generation means in every page request the data will first loaded on the server then next the html will be sent with pre-loaded content, which is real time data.

    Code Splitting:

    Because of Next.js file system-based routing, when user visits a specific route, it only loads the JS and CSS for that particular page, which make site load faster. This increases performance as there is less for the user’s browser to download and the user benefits from seeing the page content quicker. Next.js also determines which JS is shared among pages and extracts that out into the chunks. This code splitting thing is available out of the box in Next.js without any extra setup.

    SEO:

    Next.js provides the best SEO for your product which make Next.js more powerful and fast. Next.js has a built-in component for adding meta-tags to the page. Google give more importance to page speed these days and it is easy to get 100 score in the Google page speed insight tool.

    Fast Refresh:

    Next.js by default provides and enabled the fast refresh for your all application. With fast refresh user get instantaneous feedback or visual within a second, without losing component state. For developer purpose, if you introduce an error, a modal will pop up, telling you exactly which line is to blame. And once you fix the error, the app will resume with the react state still preserved. This makes the debugging process much better.

    TypeScript and ESLint support:

    TypeScript and ESLint greatly improve the developer experience and helps maintain good quality code. And the best part of Next.js is that, it supports the both.

    Image Optimization:

    Next.js has built-in Image Component for optimizing the image. It has component which provide the automatic optimization. This allows for resizing, optimizing, and serving images in modern formats like WebP on supported on browsers. This avoids shipping large images to devices with a smaller viewport.

    We have considerable experience working on the Next.js open source development platform and you can contact us for any work related to Next.js such as dynamic websites with server-side rendering and static websites.

    Resources:

    Website – https://nextjs.org/
    Github – https://github.com/vercel
    Company – https://vercel.com/
    Note: Checkout bigindia.comIndia Directory , which is made on Next.js.