Blog

  • Exception Chaining

    Exception Chaining

    Exception chaining is a technique of handling exceptions by re-throwing a caught exception after wrapping it inside a new exception. The original exception is saved as a property (such as cause) of the new exception.

    During the handling of one exception ‘A’, it is possible that another exception ‘B’ may occur. It is useful to know about both exceptions in order to debug the problem. Sometimes it is useful for an exception handler to deliberately re-raise an exception, either to provide extra information or to translate an exception to another type.

    In Python 3.x, it is possible to implement exception chaining. If there is any unhandled exception inside an except section, it will have the exception being handled attached to it and included in the error message.

    Example

    In the following code snippet, trying to open a non-existent file raises FileNotFoundError. It is detected by the except block. While handling another exception is raised.

    try:open("nofile.txt")except OSError:raise RuntimeError("unable to handle error")

    It will produce the following output −

    Traceback (most recent call last):
      File "/home/cg/root/64afcad39c651/main.py", line 2, in <module>
    open("nofile.txt")
    FileNotFoundError: [Errno 2] No such file or directory: 'nofile.txt'
    
    During handling of the above exception, another exception occurred:
    
    Traceback (most recent call last):
      File "/home/cg/root/64afcad39c651/main.py", line 4, in <module>
    
    raise RuntimeError("unable to handle error")
    RuntimeError: unable to handle error

    The raise . . from Statement

    If you use an optional from clause in the raise statement, it indicates that an exception is a direct consequence of another. This can be useful when you are transforming exceptions. The token after from keyword should be the exception object.

    try:open("nofile.txt")except OSError as exc:raise RuntimeError from exc
    

    It will produce the following output −

    Traceback (most recent call last):
      File "/home/cg/root/64afcad39c651/main.py", line 2, in <module>
    
    open("nofile.txt")
    FileNotFoundError: [Errno 2] No such file or directory: 'nofile.txt' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/home/cg/root/64afcad39c651/main.py", line 4, in <module>
    raise RuntimeError from exc
    RuntimeError

    The raise . . from None Statement

    If we use None in from clause instead of exception object, the automatic exception chaining that was found in the earlier example is disabled.

    try:open("nofile.txt")except OSError as exc:raise RuntimeError fromNone

    It will produce the following output −

    Traceback (most recent call last):
     File "C:\Python311\hello.py", line 4, in <module>
      raise RuntimeError from None
    RuntimeError
    

    The __context__ and __cause__ Expression

    Raising an exception in the except block will automatically add the captured exception to the __context__ attribute of the new exception. Similarly, you can also add __cause__ to any exception using the expression raise … from syntax.

    try:try:raise ValueError("ValueError")except ValueError as e1:raise TypeError("TypeError")from e1
    except TypeError as e2:print("The exception was",repr(e2))print("Its __context__ was",repr(e2.__context__))print("Its __cause__ was",repr(e2.__cause__))

    It will produce the following output −

    The exception was TypeError('TypeError')
    Its __context__ was ValueError('ValueError')
    Its __cause__ was ValueError('ValueError')
  • Raising Exceptions

    Raising Exceptions in Python

    In Python, you can raise exceptions explicitly using the raise statement. Raising exceptions allows you to indicate that an error has occurred and to control the flow of your program by handling these exceptions appropriately.

    Raising an exception refers to explicitly trigger an error condition in your program. This can be useful for handling situations where the normal flow of your program cannot continue due to an error or an unexpected condition.

    In Python, you can raise built-in exceptions like ValueError or TypeError to indicate common error conditions. Additionally, you can create and raise custom exceptions.

    Raising Built-in Exceptions

    You can raise any built-in exception by creating an instance of the exception class and using the raise statement. Following is the syntax −

    raise Exception("This is a general exception")

    Example

    Here is an example where we raise a ValueError when a function receives an invalid argument −

    defdivide(a, b):if b ==0:raise ValueError("Cannot divide by zero")return a / b
    
    try:
       result = divide(10,0)except ValueError as e:print(e)

    Following is the output of the above code −

    Cannot divide by zero
    

    Raising Custom Exceptions

    In addition to built-in exceptions, you can define and raise your own custom exceptions by creating a new exception class that inherits from the base Exception class or any of its subclasses −

    classMyCustomError(Exception):passdefrisky_function():raise MyCustomError("Something went wrong in risky_function")try:
       risky_function()except MyCustomError as e:print(e)

    Output of the above code is as shown below −

    Something went wrong in risky_function
    

    Creating Custom Exceptions

    Custom exceptions is useful for handling specific error conditions that are unique to your application, providing more precise error reporting and control.

    To create a custom exception in Python, you define a new class that inherits from the built-in Exception class or any other appropriate built-in exception class. This custom exception class can have additional attributes and methods to provide more detailed context about the error condition.

    Example

    In this example −

    • We define a custom exception class “InvalidAgeError” that inherits from “Exception”.
    • The __init__() method initializes the exception with the invalid age and a default error message.
    • The set_age() function raises “InvalidAgeError” if the provided age is outside the valid range.
    classInvalidAgeError(Exception):def__init__(self, age, message="Age must be between 18 and 100"):
    
      self.age = age
      self.message = message
      super().__init__(self.message)defset_age(age):if age &lt;18or age &gt;100:raise InvalidAgeError(age)print(f"Age is set to {age}")try:
    set_age(150)except InvalidAgeError as e:print(f"Invalid age: {e.age}. {e.message}")

    The result obtained is as shown below −

    Invalid age: 150. Age must be between 18 and 100
    

    Re-Raising Exceptions

    Sometimes, you may need to catch an exception, perform specific actions (such as logging, cleanup, or providing additional context), and then re-raise the same exception to be handled further up the call stack

    This is useful when you want to ensure certain actions are taken when an exception occurs, but still allow the exception to propagate for higher-level handling.

    To re-raise an exception in Python, you use the “raise” statement without specifying an exception, which will re-raise the last exception that was active in the current scope.

    Example

    In the following example −

    • The process_file() function attempts to open and read a file.
    • If the file is not found, it prints an error message and re-raises the “FileNotFoundError” exception.
    • The exception is then caught and handled at a higher level in the call stack.
    defprocess_file(filename):try:withopen(filename,"r")asfile:
    
         data =file.read()# Process dataexcept FileNotFoundError as e:print(f"File not found: {filename}")# Re-raise the exceptionraisetry:
    process_file("nonexistentfile.txt")except FileNotFoundError as e:print("Handling the exception at a higher level")

    After executing the above code, we get the following output −

    File not found: nonexistentfile.txt
    Handling the exception at a higher level
  •  The tryfinally Block

    Python Try-Finally Block

    In Python, the try-finally block is used to ensure that certain code executes, regardless of whether an exception is raised or not. Unlike the try-except block, which handles exceptions, the try-finally block focuses on cleanup operations that must occur, ensuring resources are properly released and critical tasks are completed.

    Syntax

    The syntax of the try-finally statement is as follows −

    try:# Code that might raise exceptions
       risky_code()finally:# Code that always runs, regardless of exceptions
       cleanup_code()

    In Python, when using exception handling with try blocks, you have the option to include either except clauses to catch specific exceptions or a finally clause to ensure certain cleanup operations are executed, but not both together.

    Example

    Let us consider an example where we want to open a file in write mode (“w”), writes some content to it, and ensures the file is closed regardless of success or failure using a finally block −

    try:
       fh =open("testfile","w")
       fh.write("This is my test file for exception handling!!")finally:print("Error: can\'t find file or read data")
       fh.close()

    If you do not have permission to open the file in writing mode, then it will produce the following output −

    Error: can't find file or read data
    

    The same example can be written more cleanly as follows −

    try:
       fh =open("testfile","w")try:
    
      fh.write("This is my test file for exception handling!!")finally:print("Going to close the file")
      fh.close()except IOError:print("Error: can\'t find file or read data")</pre>

    When an exception is thrown in the try block, the execution immediately passes to the finally block. After all the statements in the finally block are executed, the exception is raised again and is handled in the except statements if present in the next higher layer of the try-except statement.

    Exception with Arguments

    An exception can have an argument, which is a value that gives additional information about the problem. The contents of the argument vary by exception. You capture an exception's argument by supplying a variable in the except clause as follows −

    try:
       You do your operations here
       ......................except ExceptionType as Argument:
       You can print value of Argument here...

    If you write the code to handle a single exception, you can have a variable follow the name of the exception in the except statement. If you are trapping multiple exceptions, you can have a variable follow the tuple of the exception.

    This variable receives the value of the exception mostly containing the cause of the exception. The variable can receive a single value or multiple values in the form of a tuple. This tuple usually contains the error string, the error number, and an error location.

    Example

    Following is an example for a single exception −

    # Define a function here.deftemp_convert(var):try:returnint(var)except ValueError as Argument:print("The argument does not contain numbers\n",Argument)# Call above function here.
    temp_convert("xyz")

    It will produce the following output −

    The argument does not contain numbers
    invalid literal for int() with base 10: 'xyz'
  • The try-except Block

    Python Try-Except Block

    In Python, the try-except block is used to handle exceptions and errors gracefully, ensuring that your program can continue running even when something goes wrong. This tutorial will cover the basics of using the try-except block, its syntax, and best practices.

    Exception handling allows you to manage errors in your code by capturing exceptions and taking appropriate actions instead of letting the program crash. An exception is an error that occurs during the execution of a program, and handling these exceptions ensures your program can respond to unexpected situations.

    The try-except block in Python is used to catch and handle exceptions. The code that might cause an exception is placed inside the try block, and the code to handle the exception is placed inside the except block.

    Syntax

    Following is the basic syntax of the try-except block in Python −

    try:# Code that might cause an exception
       risky_code()except SomeException as e:# Code that runs if an exception occurs
       handle_exception(e)

    Example

    In this example, if you enter a non-numeric value, a ValueError will be raised. If you enter zero, a ZeroDivisionError will be raised. The except blocks handle these exceptions and prints appropriate error messages −

    try:
    number =int(input("Enter a number: "))
    result =10/ number
    print(f"Result: {result}")except ZeroDivisionError as e:print("Error: Cannot divide by zero.")except ValueError as e:print("Error: Invalid input. Please enter a valid number.")
    Handling Multiple Exceptions

    In Python, you can handle multiple types of exceptions using multiple except blocks within a single try-except statement. This allows your code to respond differently to different types of errors that may occur during execution.

    Syntax

    Following is the basic syntax for handling multiple exceptions in Python −

    try:
    # Code that might raise exceptions
    risky_code()
    except FirstExceptionType:
    # Handle the first type of exception
    handle_first_exception()
    except SecondExceptionType:
    # Handle the second type of exception
    handle_second_exception()
    # Add more except blocks as needed for other exception types
    Example

    In the following example −

    If you enter zero as the divisor, a "ZeroDivisionError" will be raised, and the corresponding except ZeroDivisionError block will handle it by printing an error message.
    If you enter a non-numeric input for either the dividend or the divisor, a "ValueError" will be raised, and the except ValueError block will handle it by printing a different error message.
    try:
    dividend = int(input("Enter the dividend: "))
    divisor = int(input("Enter the divisor: "))
    result = dividend / divisor
    print(f"Result of division: {result}")
    except ZeroDivisionError:
    print("Error: Cannot divide by zero.")
    except ValueError:
    print("Error: Invalid input. Please enter valid integers.")
    Using Else Clause with Try-Except Block

    In Python, the else clause can be used in conjunction with the try-except block to specify code that should run only if no exceptions occur in the try block. This provides a way to differentiate between the main code that may raise exceptions and additional code that should only execute under normal conditions.

    Syntax

    Following is the basic syntax of the else clause in Python −

    try:
    # Code that might raise exceptions
    risky_code()
    except SomeExceptionType:
    # Handle the exception
    handle_exception()
    else:
    # Code that runs if no exceptions occurred
    no_exceptions_code()
    Example

    In the following example −

    If you enter a non-integer input, a ValueError will be raised, and the corresponding except ValueError block will handle it.
    If you enter zero as the denominator, a ZeroDivisionError will be raised, and the corresponding except ZeroDivisionError block will handle it.
    If the division is successful (i.e., no exceptions are raised), the else block will execute and print the result of the division.
    try:
    numerator = int(input("Enter the numerator: "))
    denominator = int(input("Enter the denominator: "))
    result = numerator / denominator
    except ValueError:
    print("Error: Invalid input. Please enter valid integers.")
    except ZeroDivisionError:
    print("Error: Cannot divide by zero.")
    else:
    print(f"Result of division: {result}")
    The Finally Clause

    The finally clause provides a mechanism to guarantee that specific code will be executed, regardless of whether an exception is raised or not. This is useful for performing cleanup actions such as closing files or network connections, releasing locks, or freeing up resources.

    Syntax

    Following is the basic syntax of the finally clause in Python −

    try:
    # Code that might raise exceptions
    risky_code()
    except SomeExceptionType:
    # Handle the exception
    handle_exception()
    else:
    # Code that runs if no exceptions occurred
    no_exceptions_code()
    finally:
    # Code that always runs, regardless of exceptions
    cleanup_code()
    Example

    In this example −

    If the file "example.txt" exists, its content is read and printed, and the else block confirms the successful operation.
    If the file is not found (FileNotFoundError), an appropriate error message is printed in the except block.
    The finally block ensures that the file is closed (file.close()) regardless of whether the file operation succeeds or an exception occurs.
    try:
    file = open("example.txt", "r")
    content = file.read()
    print(content)
    except FileNotFoundError:
    print("Error: The file was not found.")
    else:
    print("File read operation successful.")
    finally:
    if 'file' in locals():
    file.close()
    print("File operation is complete.")

  • Exceptions Handling

    Exception Handling in Python

    Exception handling in Python refers to managing runtime errors that may occur during the execution of a program. In Python, exceptions are raised when errors or unexpected situations arise during program execution, such as division by zero, trying to access a file that does not exist, or attempting to perform an operation on incompatible data types.

    Python provides two very important features to handle any unexpected error in your Python programs and to add debugging capabilities in them −

    • Exception Handling − This would be covered in this tutorial. Here is a list of standard Exceptions available in Python: Standard Exceptions.
    • Assertions − This would be covered in Assertions in Python tutorial.

    Assertions in Python

    An assertion is a sanity-check that you can turn on or turn off when you are done with your testing of the program.

    The easiest way to think of an assertion is to liken it to a raise-if statement (or to be more accurate, a raise-if-not statement). An expression is tested, and if the result comes up false, an exception is raised.

    Assertions are carried out by the assert statement, the newest keyword to Python, introduced in version 1.5.

    Programmers often place assertions at the start of a function to check for valid input, and after a function call to check for valid output.

    The assert Statement

    When it encounters an assert statement, Python evaluates the accompanying expression, which is hopefully true. If the expression is false, Python raises an AssertionError exception.

    The syntax for assert is −

    assert Expression[, Arguments]

    If the assertion fails, Python uses ArgumentExpression as the argument for the AssertionError. AssertionError exceptions can be caught and handled like any other exception using the try-except statement, but if not handled, they will terminate the program and produce a trace back.

    Example

    Here is a function that converts a temperature from degrees Kelvin to degrees Fahrenheit. Since zero degrees Kelvin is as cold as it gets, the function bails out if it sees a negative temperature −

    defKelvinToFahrenheit(Temperature):assert(Temperature >=0),"Colder than absolute zero!"return((Temperature-273)*1.8)+32print(KelvinToFahrenheit(273))print(int(KelvinToFahrenheit(505.78)))print(KelvinToFahrenheit(-5))

    When the above code is executed, it produces the following result −

    32.0
    451
    Traceback (most recent call last):
    File "test.py", line 9, in <module>
    print (KelvinToFahrenheit(-5))
    File "test.py", line 4, in KelvinToFahrenheit
    assert (Temperature >= 0),"Colder than absolute zero!"
    AssertionError: Colder than absolute zero!
    

    What is Exception?

    An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program’s instructions. In general, when a Python script encounters a situation that it cannot cope with, it raises an exception. An exception is a Python object that represents an error.

    When a Python script raises an exception, it must either handle the exception immediately otherwise it terminates and quits.

    Handling an Exception in Python

    If you have some suspicious code that may raise an exception, you can defend your program by placing the suspicious code in a try: block. After the try: block, include an except: statement, followed by a block of code which handles the problem as elegantly as possible.

    • The try: block contains statements which are susceptible for exception
    • If exception occurs, the program jumps to the except: block.
    • If no exception in the try: block, the except: block is skipped.

    Syntax

    Here is the simple syntax of try…except…else blocks −

    try:
       You do your operations here
       ......................except ExceptionI:
       If there is ExceptionI, then execute this block.except ExceptionII:
       If there is ExceptionII, then execute this block.......................else:
       If there is no exception then execute this block.

    Here are few important points about the above-mentioned syntax −

    • A single try statement can have multiple except statements. This is useful when the try block contains statements that may throw different types of exceptions.
    • You can also provide a generic except clause, which handles any exception.
    • After the except clause(s), you can include an else clause. The code in the else block executes if the code in the try: block does not raise an exception.
    • The else block is a good place for code that does not need the try: block’s protection.

    Example

    This example opens a file, writes content in the file and comes out gracefully because there is no problem at all.

    try:
       fh =open("testfile","w")
       fh.write("This is my test file for exception handling!!")except IOError:print("Error: can\'t find file or read data")else:print("Written content in the file successfully")
       fh.close()

    It will produce the following output −

    Written content in the file successfully
    

    However, change the mode parameter in open() function to “w”. If the testfile is not already present, the program encounters IOError in except block, and prints following error message −

    Error: can't find file or read data
    

    Example

    This example tries to open a file where you do not have write permission, so it raises an exception −

    try:
       fh =open("testfile","r")
       fh.write("This is my test file for exception handling!!")except IOError:print("Error: can\'t find file or read data")else:print("Written content in the file successfully")

    This produces the following result −

    Error: can't find file or read data
    

    The except Clause with No Exceptions

    You can also use the except statement with no exceptions defined as follows −

    try:
       You do your operations here;......................except:
       If there isany exception, then execute this block.......................else:
       If there is no exception then execute this block.

    This kind of a try-except statement catches all the exceptions that occur. Using this kind of try-except statement is not considered a good programming practice though, because it catches all exceptions but does not make the programmer identify the root cause of the problem that may occur.

    The except Clause with Multiple Exceptions

    You can also use the same except statement to handle multiple exceptions as follows −

    try:
       You do your operations here;......................except(Exception1[, Exception2[,...ExceptionN]]]):
       If there isany exception from the given exception list, 
       then execute this block.......................else:
       If there is no exception then execute this block.

    The try-finally Clause

    You can use a finally: block along with a try: block. The finally block is a place to put any code that must execute, whether the try-block raised an exception or not. The syntax of the try-finally statement is this −

    try:
       You do your operations here;......................
       Due to any exception, this may be skipped.finally:
       This would always be executed.......................

    You cannot use else clause as well along with a finally clause.

    Example

    try:
       fh =open("testfile","w")
       fh.write("This is my test file for exception handling!!")finally:print("Error: can\'t find file or read data")

    If you do not have permission to open the file in writing mode, then this will produce the following result −

    Error: can't find file or read data
    

    Same example can be written more cleanly as follows −

    try:
       fh =open("testfile","w")try:
    
      fh.write("This is my test file for exception handling!!")finally:print("Going to close the file")
      fh.close()except IOError:print("Error: can\'t find file or read data")</pre>

    When an exception is thrown in the try block, the execution immediately passes to the finally block. After all the statements in the finally block are executed, the exception is raised again and is handled in the except statements if present in the next higher layer of the try-except statement.

    Argument of an Exception

    An exception can have an argument, which is a value that gives additional information about the problem. The contents of the argument vary by exception. You capture an exception's argument by supplying a variable in the except clause as follows −

    try:
       You do your operations here;......................except ExceptionType, Argument:
       You can print value of Argument here...

    If you write the code to handle a single exception, you can have a variable follow the name of the exception in the except statement. If you are trapping multiple exceptions, you can have a variable follow the tuple of the exception.

    This variable receives the value of the exception mostly containing the cause of the exception. The variable can receive a single value or multiple values in the form of a tuple. This tuple usually contains the error string, the error number, and an error location.

    Example

    Following is an example for a single exception −

    # Define a function here.deftemp_convert(var):try:returnint(var)except ValueError as Argument:print("The argument does not contain numbers\n", Argument)# Call above function here.
    temp_convert("xyz")

    This produces the following result −

    The argument does not contain numbers
    invalid literal for int() with base 10: 'xyz'
    

    Raising an Exceptions

    You can raise exceptions in several ways by using the raise statement. The general syntax for the raise statement is as follows.

    Syntax

    raise [Exception [, args [, traceback]]]
    

    Here, Exception is the type of exception (for example, NameError) and argumentis a value for the exception argument. The argument is optional; if not supplied, the exception argument is None.

    The final argument, trace back, is also optional (and rarely used in practice), and if present, is the traceback object used for the exception.

    Example

    An exception can be a string, a class or an object. Most of the exceptions that the Python core raises are classes, with an argument that is an instance of the class. Defining new exceptions is quite easy and can be done as follows −

    deffunctionName( level ):if level <1:raise"Invalid level!", level
    
      # The code below to this would not be executed# if we raise the exception</pre>

    Note: In order to catch an exception, an "except" clause must refer to the same exception thrown either class object or simple string. For example, to capture above exception, we must write the except clause as follows −

    try:
       Business Logic here...except"Invalid level!":
       Exception handling here...else:
       Rest of the code here...

    User-Defined Exceptions

    Python also allows you to create your own exceptions by deriving classes from the standard built-in exceptions.

    Here is an example related to RuntimeError. Here, a class is created that is subclassed from RuntimeError. This is useful when you need to display more specific information when an exception is caught.

    In the try block, the user-defined exception is raised and caught in the except block. The variable e is used to create an instance of the class Networkerror.

    classNetworkerror(RuntimeError):def__init__(self, arg):
    
      self.args = arg

    So once you defined above class, you can raise the exception as follows −

    try:raise Networkerror("Bad hostname")except Networkerror,e:print(e.args)

    Standard Exceptions

    Here is a list of Standard Exceptions available in Python −

    Sr.No.Exception Name & Description
    1ExceptionBase class for all exceptions
    2StopIterationRaised when the next() method of an iterator does not point to any object.
    3SystemExitRaised by the sys.exit() function.
    4StandardErrorBase class for all built-in exceptions except StopIteration and SystemExit.
    5ArithmeticErrorBase class for all errors that occur for numeric calculation.
    6OverflowErrorRaised when a calculation exceeds maximum limit for a numeric type.
    7FloatingPointErrorRaised when a floating point calculation fails.
    8ZeroDivisionErrorRaised when division or modulo by zero takes place for all numeric types.
    9AssertionErrorRaised in case of failure of the Assert statement.
    10AttributeErrorRaised in case of failure of attribute reference or assignment.
    11EOFErrorRaised when there is no input from either the raw_input() or input() function and the end of file is reached.
    12ImportErrorRaised when an import statement fails.
    13KeyboardInterruptRaised when the user interrupts program execution, usually by pressing Ctrl+c.
    14LookupErrorBase class for all lookup errors.
    15IndexErrorRaised when an index is not found in a sequence.
    16KeyErrorRaised when the specified key is not found in the dictionary.
    17NameErrorRaised when an identifier is not found in the local or global namespace.
    18UnboundLocalErrorRaised when trying to access a local variable in a function or method but no value has been assigned to it.
    19EnvironmentErrorBase class for all exceptions that occur outside the Python environment.
    20IOErrorRaised when an input/ output operation fails, such as the print statement or the open() function when trying to open a file that does not exist.
    21IOErrorRaised for operating system-related errors.
    22SyntaxErrorRaised when there is an error in Python syntax.
    23IndentationErrorRaised when indentation is not specified properly.
    24SystemErrorRaised when the interpreter finds an internal problem, but when this error is encountered the Python interpreter does not exit.
    25SystemExitRaised when Python interpreter is quit by using the sys.exit() function. If not handled in the code, causes the interpreter to exit.
    26TypeErrorRaised when an operation or function is attempted that is invalid for the specified data type.
    27ValueErrorRaised when the built-in function for a data type has the valid type of arguments, but the arguments have invalid values specified.
    28RuntimeErrorRaised when a generated error does not fall into any category.
    29NotImplementedErrorRaised when an abstract method that needs to be implemented in an inherited class is not actually implemented.
  • Syntax Errors

    Python Syntax Errors

    In Python, syntax errors are among the most common errors encountered by programmers, especially those who are new to the language. This tutorial will help you understand what syntax errors are, how to identify them, and how to fix them.

    What is a Syntax Error?

    A syntax error in Python (or any programming language) is an error that occurs when the code does not follow the syntax rules of the language. Syntax errors are detected by the interpreter or compiler at the time of parsing the code, and they prevent the code from being executed.

    These errors occur because the written code does not conform to the grammatical rules of Python, making it impossible for the interpreter to understand and execute the commands.

    Common Causes of Syntax Errors

    Following are the common causes of syntax errors −Missing colons (:) after control flow statements (e.g., if, for, while) − Colons are used to define the beginning of an indented block, such as in functions, loops, and conditionals.

    # Error: Missing colon (:) after the if statementifTrueprint("This will cause a syntax error")

    Incorrect indentation − Python uses indentation to define the structure of code blocks. Incorrect indentation can lead to syntax errors.

    # Error: The print statement is not correctly indenteddefexample_function():print("This will cause a syntax error")

    Misspelled keywords or incorrect use of keywords.

    # Error: 'print' is misspelled as 'prnt'
    prnt("Hello, World!")

    Unmatched parentheses, brackets, or braces − Python requires that all opening parentheses (, square brackets [, and curly braces { have corresponding closing characters ), ], and }.

    # Error: The closing parenthesis is missing.print("This will cause a syntax error"

    How to Identify Syntax Errors

    Identifying syntax errors in Python can sometimes be easy, especially when you get a clear error message from the interpreter. However, other times, it can be a bit tricky. Here are several ways to help you identify and resolve syntax errors effectively −

    Reading Error Messages

    When you run a Python script, the interpreter will stop execution and display an error message if it encounters a syntax error. Understanding how to read these error messages is very important.

    Example Error Message

    File "script.py", line 1print("Hello, World!"^
    SyntaxError: EOL while scanning string literal
    

    This error message can be broken down into parts −

    • File “script.py”: Indicates the file where the error occurred.
    • line 1: Indicates the line number in the file where the interpreter detected the error.
    • print(“Hello, World!”: Shows the line of code with the error.
    • ^: Points to the location in the line where the error was detected.

    Using an Integrated Development Environment (IDE)

    IDEs are helpful in identifying syntax errors as they often provide real-time feedback. Here are some features of IDEs that helps in identifying syntax errors −

    • Syntax Highlighting: IDEs highlight code syntax in different colors. If a part of the code is incorrectly colored, it may indicate a syntax error.
    • Linting: Tools like pylint or flake8 check your code for errors and stylistic issues.
    • Error Underlining: Many IDEs underline syntax errors with a red squiggly line.
    • Tooltips and Error Messages: Hovering over the underlined code often provides a tooltip with a description of the error.

    Popular IDEs with these features include PyCharm, Visual Studio Code, and Jupyter Notebook.

    Running Code in Small Chunks

    If you have a large script, it can be useful to run the code in smaller chunks. This can help isolate the part of the code causing the syntax error.

    For example, if you have a script with multiple functions and you get a syntax error, try running each function independently to narrow down where the error might be.

    Using Version Control

    Version control systems like Git can help you track changes to your code. If you encounter a syntax error, you can compare the current version of the code with previous versions to see what changes might have introduced the error.

    Fixing Syntax Errors

    Fixing syntax errors in Python involves understanding the error message provided by the interpreter, identifying the exact issue in the code, and then making the necessary corrections. Here is a detailed guide on how to systematically approach and fix syntax errors −

    Read the Error Message Carefully

    Pythons error messages are quite informative. They indicate the file name, line number, and the type of syntax error −

    Example Error Message

    Assume we have written a print statement as shown below −

    print("Hello, World!"

    The following message indicates that there is a syntax error on line 1, showing that somewhere in the code, a parenthesis was left unclosed, which leads to a syntax error.

    File "/home/cg/root/66634a37734ad/main.py", line 1print("Hello, World!"^
    SyntaxError:'(' was never closed
    

    To fix this error, you need to ensure that every opening parenthesis has a corresponding closing parenthesis. Here is the corrected code −

    print("Hello, World!")

    Locate the Error

    To locate the error, you need to go to the line number mentioned in the error message. Additionally, check not only the indicated line but also the lines around it, as sometimes the issue might stem from previous lines.

    Understand the Nature of the Error

    To understand the nature of the error, you need to identify what type of syntax error it is (e.g., missing parenthesis, incorrect indentation, missing colon, etc.). Also, refer to common syntax errors and their patterns.

    Correct the Syntax

    Based on the error type, fix the code.

  •  Reflection

    In object-oriented programming, reflection refers to the ability to extract information about any object in use. You can get to know the type of object, whether is it a subclass of any other class, what are its attributes, and much more. Python’s standard library has several functions that reflect on different properties of an object. Reflection is also sometimes called introspect.

    Following is the list of reflection functions in Python −

    • type() Function
    • isinstance() Function
    • issubclass() Function
    • callable() Function
    • getattr() Function
    • setattr() Function
    • hasattr() Function
    • dir() Function

    The type() Function

    We have used this function many times. It tells you which class an object belongs to.

    Example

    Following statements print the respective class of different built-in data type objects

    print(type(10))print(type(2.56))print(type(2+3j))print(type("Hello World"))print(type([1,2,3]))print(type({1:'one',2:'two'}))

    Here, you will get the following output −

    <class 'int'>
    <class 'float'>
    <class 'complex'>
    <class 'str'>
    <class 'list'>
    <class 'dict'>
    

    Let us verify the type of an object of a user-defined class −

    classtest:pass
       
    obj = test()print(type(obj))

    It will produce the following output −

    <class '__main__.test'>

    The isinstance() Function

    This is another built-in function in Python which ascertains if an object is an instance of the given class.

    Syntax

    isinstance(obj,class)

    This function always returns a Boolean value, true if the object is indeed belongs to the given class and false if not.

    Example

    Following statements return True −

    print(isinstance(10,int))print(isinstance(2.56,float))print(isinstance(2+3j,complex))print(isinstance("Hello World",str))

    It will produce the following output −

    True
    True
    True
    True
    

    In contrast, these statements print False.

    print(isinstance([1,2,3],tuple))print(isinstance({1:'one',2:'two'},set))

    It will produce the following output −

    False
    False
    

    You can also perform check with a user defined class

    classtest:pass
       
    obj = test()print(isinstance(obj, test))

    It will produce the following output −

    True
    

    In Python, even the classes are objects. All classes are objects of object class. It can be verified by following code −

    classtest:passprint(isinstance(int,object))print(isinstance(str,object))print(isinstance(test,object))

    All the above print statements print True.

    The issubclass() Function

    This function checks whether a class is a subclass of another class. Pertains to classes, not their instances.

    As mentioned earlier, all Python classes are subclassed from object class. Hence, output of following print statements is True for all.

    classtest:passprint(issubclass(int,object))print(issubclass(str,object))print(issubclass(test,object))

    It will produce the following output −

    True
    True
    True
    

    The callable() Function

    An object is callable if it invokes a certain process. A Python function, which performs a certain process, is a callable object. Hence callable(function) returns True. Any function, built-in, user-defined, or method is callable. Objects of built-in data types such as int, str, etc., are not callable.

    Example

    deftest():passprint(callable("Hello"))print(callable(abs))print(callable(list.clear([1,2])))print(callable(test))

    string object is not callable. But abs is a function which is callable. The pop method of list is callable, but clear() is actually call to the function and not a function object, hence not a callable

    It will produce the following output −

    False
    True
    True
    False
    True
    

    A class instance is callable if it has a __call__() method. In the example below, the test class includes __call__() method. Hence, its object can be used as if we are calling function. Hence, object of a class with __call__() function is a callable.

    classtest:def__init__(self):passdef__call__(self):print("Hello")
    
      
    obj = test() obj()print("obj is callable?",callable(obj))

    It will produce the following output −

    Hello
    obj is callable? True
    

    The getattr() Function

    The getattr() built-in function retrieves the value of the named attribute of object.

    Example

    classtest:def__init__(self):
    
      self.name ="Manav"
      
    obj = test()print(getattr(obj,"name"))

    It will produce the following output −

    Manav
    

    The setattr() Function

    The setattr() built-in function adds a new attribute to the object and assigns it a value. It can also change the value of an existing attribute.

    In the example below, the object of test class has a single attribute − name. We use setattr() to add age attribute and to modify the value of name attribute.

    classtest:def__init__(self):
    
      self.name ="Manav"
      
    obj = test()setattr(obj,"age",20)setattr(obj,"name","Madhav")print(obj.name, obj.age)

    It will produce the following output −

    Madhav 20
    

    The hasattr() Function

    This built-in function returns True if the given attribute is available to the object argument, and false if not. We use the same test class and check if it has a certain attribute or not.

    classtest:def__init__(self):
    
      self.name ="Manav"
      
    obj = test()print(hasattr(obj,"age"))print(hasattr(obj,"name"))

    It will produce the following output −

    False
    True
    

    The dir() Function

    If this built-in function is called without an argument, return the names in the current scope. For any object as an argument, it returns a list of the attributes of the given object and attributes reachable from it.

    • For a module object − the function returns the module’s attributes.
    • For a class object − the function returns its attributes, and recursively the attributes of its bases.
    • For any other object − its attributes, its class’s attributes, and recursively the attributes of its class’s base classes.

    Example

    print("dir(int):",dir(int))

    It will produce the following output −

    dir(int): ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
    

    Example

    print("dir(dict):",dir(dict))

    It will produce the following output −

    dir(dict): ['__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__ior__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__ror__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
    

    Example

    classtest:def__init__(self):
    
      self.name ="Manav"
    obj = test()print("dir(obj):",dir(obj))

    It will produce the following output −

    dir(obj): ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name']
  •  Enums

    Enums in Python

    In Python, the term enumeration refers to the process of assigning fixed constant values to a set of strings so that each string can be identified by the value bound to it. The Enum class included in enum module (which is a part of Python’s standard library) is used as the parent class to define enumeration of a set of identifiers − conventionally written in upper case.

    Example

    In the below code, “subjects” is the enumeration. It has different enumeration members and each member is an object of the enumeration class subjects. These members have name and value attributes.

    # importing enum from enum import Enum
    
    classsubjects(Enum):
       ENGLISH =1
       MATHS =2
       SCIENCE =3
       SANSKRIT =4
    
    obj = subjects.MATHS
    print(type(obj))

    It results in following output −

    <enum 'subjects'> 
    

    An enum class cannot have the same member appearing twice, however, more than one member may be assigned the same value. To ensure that each member has a unique value bound to it, use the @unique decorator.

    Example

    In this example, we are using the @unique decorator to restrict duplicacy.

    from enum import Enum, unique
    
    @uniqueclasssubjects(Enum):
       ENGLISH =1
       MATHS =2
       GEOGRAPHY =3
       SANSKRIT =2

    This will raise an exception as shown below −

       @unique
    
    ^^^^^^
    raise ValueError('duplicate values found in %r: %s' % ValueError: duplicate values found in <enum 'subjects'>: SANSKRIT -> MATHS

    The Enum class is a callable class, hence you can use its constructor to create an enumeration. This constructor accepts two arguments, which are the name of enumeration and a string consisting of enumeration member symbolic names separated by a whitespace.

    Example

    following is an alternative method of defining an enumeration −

    from enum import Enum
    subjects = Enum("subjects","ENGLISH MATHS SCIENCE SANSKRIT")print(subjects.ENGLISH)print(subjects.MATHS)print(subjects.SCIENCE)print(subjects.SANSKRIT)

    This code will give the following output −

    subjects.ENGLISH
    subjects.MATHS
    subjects.SCIENCE
    subjects.SANSKRIT

    Accessing Modes in Enums

    Members of an enum class can be accessed in two modes −

    Value − In this mode, value of the enum member is accessed using the "value" keyword followed by object of the enum class.
    Name − Similarly, we use the "name" keyword to access name of the enum member.
    Example

    The following example illustrates how to access value and name of the enum member.

    from enum import Enum

    class subjects(Enum):
    ENGLISH = "E"
    MATHS = "M"
    GEOGRAPHY = "G"
    SANSKRIT = "S"

    obj = subjects.SANSKRIT
    print(type(obj))
    print(obj.name)
    print(obj.value)
    It will produce the following output −

    <enum 'subjects'>
    SANSKRIT
    S
    Iterating through Enums

    You can iterate through the enum members in the order of their appearance in the definition, with the help of a for loop.

    Example

    The following example shows how to iterate through an enumeration using for loop −

    from enum import Enum

    class subjects(Enum):
    ENGLISH = "E"
    MATHS = "M"
    GEOGRAPHY = "G"
    SANSKRIT = "S"

    for sub in subjects:
    print (sub.name, sub.value)
    It will produce the following output −

    ENGLISH E
    MATHS M
    GEOGRAPHY G
    SANSKRIT S
    We know that enum member can be accessed with the unique value assigned to it, or by its name attribute. Hence, subjects("E") as well as subjects["ENGLISH"] returns subjects.ENGLISH member.

  • Wrapper Classes

    function in Python is a first-order object. A function can have another function as its argument and wrap another function definition inside it. This helps in modifying a function without actually changing it. Such functions are called decorators.

    This feature is also available for wrapping a class. This technique is used to manage the class after it is instantiated by wrapping its logic inside a decorator.

    Example

    defdecorator_function(Wrapped):classWrapper:def__init__(self,x):
    
         self.wrap = Wrapped(x)defprint_name(self):return self.wrap.name
    return Wrapper @decorator_functionclassWrapped:def__init__(self,x):
      self.name = x
      
    obj = Wrapped('TutorialsPoint')print(obj.print_name())

    Here, Wrapped is the name of the class to be wrapped. It is passed as argument to a function. Inside the function, we have a Wrapper class, modify its behavior with the attributes of the passed class, and return the modified class. The returned class is instantiated and its method can now be called.

    When you execute this code, it will produce the following output −

    TutorialsPoint
  •  Singleton Class

    In Python, a Singleton class is the implementation of singleton design pattern which means this type of class can have only one object. This helps in optimizing memory usage when you perform some heavy operation, like creating a database connection.

    If we try to create multiple objects for a singleton class, the object will be created only for the first time. After that, the same object instance will be returned.

    Creating Singleton Classes in Python

    We can create and implement singleton classes in Python using the following ways −

    • using __init__
    • using __new__

    Using __init__

    The __init__ method is an instance method that is used for initializing a newly created object. Its automatically called when an object is created from a class.

    If we use this method with a static method and provide necessary checks i.e., whether an instance of the class already exists or not, we can restrict the creation of a new object after the first one is created. 

    Example

    In the below example, we are creating a singleton class using the __init__ method.

    classSingleton:
      __uniqueInstance =None@staticmethoddefcreateInstance():if Singleton.__uniqueInstance ==None:
    
      Singleton()return Singleton.__uniqueInstance
    
    def__init__(self):if Singleton.__uniqueInstance !=None:raise Exception("Object exist!")else:
          Singleton.__uniqueInstance = self
           
    obj1 = Singleton.createInstance()print(obj1) obj2 = Singleton.createInstance()print(obj2)

    When we run the above code, it will show the following result −

    <__main__.Singleton object at 0x7e4da068a910>
    <__main__.Singleton object at 0x7e4da068a910>
    

    Using __new__

    The __new__ method is a special static method in Python that is called to create a new instance of a class. It takes the class itself as the first argument and returns a new instance of that class.

    When an instance of a Python class is declared, it internally calls the __new__() method. If you want to implement a Singleton class, you can override this method. 

    In the overridden method, you first check whether an instance of the class already exists. If it doesnt (i.e., if the instance is None), you call the super() method to create a new object. At the end, save this instance in a class attribute and return the result.

    Example

    In the following example, we are creating a singleton class using the __new__ method.

    classSingletonClass:
       _instance =Nonedef__new__(cls):if cls._instance isNone:print('Creating the object')
    
         cls._instance =super(SingletonClass, cls).__new__(cls)return cls._instance
      
    obj1 = SingletonClass()print(obj1) obj2 = SingletonClass()print(obj2)

    The above code gives the following result −

    Creating the object
    <__main__.SingletonClass object at 0x000002A5293A6B50>
    <__main__.SingletonClass object at 0x000002A5293A6B50>