10 Defining Functions and Classes
- Be able to define your own functions and classes
- Be aware of the object-oriented programming paradigm
- Understand local and global variables
10.1 Defining Functions in Python
Functions in Python are blocks of reusable code designed to perform a specific task. They help organise code, promote reusability, and make programs more readable.
Syntax of a Function
The basic syntax for defining a function in Python is:
def function_name(parameters):
"""Docstring explaining the function"""
# Function body
= 1
value return value
def
: This keyword is used to define a function.function_name
: The name you choose for the function.parameters
: Optional. Values that the function accepts as inputs. If there are multiple, they are separated by commas.return
: Optional. The function can return a value using thereturn
statement.
Example 1: Function with No Parameters
def welcome():
print("Hello, World!")
Example 2: Function with Parameters
def welcome_person(name):
print(f"Hello, {name}!")
This function accepts a single parameter, name.
It uses that parameter to personalize the greeting. Calling the function:
welcome_person("Kavi")
Output:
Hello, Kavi!
Example 3: Function with Multiple Parameters and a Return Value
def add_numbers(a, b):
return a + b
This function takes two parameters, a and b. It returns the sum of the two numbers. Calling the function:
result = add_numbers(5, 10)
print(result)
Output:
15
Example 4: Function with Default Parameters
You can set default values for parameters so that a function can be called with fewer arguments than defined.
def welcome_person(name="Guest"):
print(f"Hello {name}, nice to meet you!")
Calling the function with and without the parameter:
welcome_person("Tom") # Uses the provided argument
welcome_person() # Uses the default value
output
Hello Tom, nice to meet you!
Hello Guest, nice to meet you!
Example 5: Function with Multiple Outputs
A function can return more than one value using tuples or other data structures.
def calculate(a, b):
sum_val = a + b
product_val = a * b
return sum_val, product_val
Calling the function and unpacking values:
sum_1, product_1 = calculate(3, 4)
print(sum_1, product_1)
Output:
7 12
Functions Summary
Functions in Python are defined using the def keyword. Inputs (parameters) can be passed into functions, and outputs can be returned using the return statement. Functions can have default parameters, accept multiple arguments, and return multiple values.
10.2 Classes in Python
In Python, a class is a standard way of creating objects (instances). It allows you to bundle data (attributes) and functions (methods) into a single unit. Classes define the structure and behaviour of objects in an object-oriented programming style.
Basic Syntax of a Python Class:
class MyClass:
# Constructor to initialize the object
def __init__(self, attribute1, attribute2):
self.attribute1 = attribute1 # Instance variable
self.attribute2 = attribute2 # Instance variable
# Method (function) within the class
def my_method(self):
print(f"Attribute 1: {self.attribute1}")
print(f"Attribute 2: {self.attribute2}")
Key Concepts:
__init__()
Method: This is the constructor method that gets called when a new object of the class is created. It initializes the object’s attributes.- Attributes: Variables that belong to an object, defined by
self.attribute_name
. - Methods: Functions defined inside a class that operate on the object’s attributes.
- Instance: An individual object created from the class.
Example for biologists:
class BioComponent:
def __init__(self, name, id, sequence, function):
self.name = name
self.id = id
self.sequence = sequence
self.length = len(sequence)
self.function = function
# Method to pad the sequence with user input on either side
def pad(self, left_pad, right_pad):
self.sequence = left_pad + self.sequence + right_pad
self.length = len(self.sequence) # Update the length
print(f"Padded sequence: {self.sequence}, New length: {self.length}")
# Method to perform a point mutation (replace a character at a specified position)
def point_mutate(self, position, new_char):
if position < 0 or position >= self.length:
print("Position out of bounds.")
return
self.sequence = self.sequence[:position] + new_char + self.sequence[position + 1:]
print(f"Mutated sequence: {self.sequence}")
# Inherited class Gene that restricts mutations to A, T, C, G only
class Gene(BioComponent):
def point_mutate(self, position, new_char):
if new_char not in "ATCG":
print("Error: Invalid base. Must be one of A, T, C, G.")
return
super().point_mutate(position, new_char) # Call parent class method
# Example Usage
# Create an instance of BioComponent
component = BioComponent(name="ProteinX", id=101, sequence="MKTFFY", function="Signalling")
print(f"Initial sequence: {component.sequence}, Length: {component.length}")
# Use the pad method
component.pad("AAA", "BBB")
# Perform a point mutation at position 2 (index starts at 0)
component.point_mutate(2, "Q")
# Create an instance of Gene (inherits from BioComponent)
gene = Gene(name="GeneX", id=202, sequence="ATGCGT", function="Coding for protein")
print(f"Initial gene sequence: {gene.sequence}, Length: {gene.length}")
# Perform a valid point mutation with a valid base (A, T, C, G)
gene.point_mutate(3, "A")
# Attempt an invalid point mutation with an invalid base (not A, T, C, G)
gene.point_mutate(2, "X")
Explanation:
BioComponent Class:
- The
__init__
method initializes thename
,id
,sequence
,length
(calculated automatically), andfunction
of the component. - The
pad
method adds user-inputted strings (left_pad
,right_pad
) on either side of the sequence and updates the sequence’s length accordingly. - The
point_mutate
method allows any character to replace one at a specific position in the sequence, updating the sequence accordingly.
Gene Class:
- Inherits from
BioComponent
and overrides thepoint_mutate
method to ensure that only valid DNA bases (A, T, C, G) can be used for mutation. - Calls the parent class’s
point_mutate
method if the mutation is valid.
Example Usage:
- A BioComponent (e.g., a protein) can be padded and mutated freely with any character.
- A Gene restricts mutations to valid DNA bases and throws an error if any invalid base is provided.
- The
super().point_mutate(position, new_char)
call in the Gene class refers to invoking thepoint_mutate
method from its parent class (BioComponent). Thesuper()
function is used to give access to methods of the parent class from within a child class. - When the Gene class overrides the
point_mutate
method, it restricts the mutations to DNA bases (A, T, C, G). After checking that the new character (new_char
) is valid, thesuper().point_mutate(position, new_char)
call allows the Gene class to reuse the logic ofpoint_mutate
from BioComponent to actually mutate the sequence.
How super()
Works in This Case:
super()
looks up the parent class of Gene (which is BioComponent).super().point_mutate(position, new_char)
calls thepoint_mutate
method of BioComponent with theposition
andnew_char
arguments.- This lets Gene use the existing logic of
point_mutate
defined in BioComponent for sequence mutation, without having to reimplement it.
10.3 Local and Global Variables, instance and class attributes
In Python, variables can be classified into two main categories based on their scope: local and global variables.
Local Variables
- A local variable is a variable that is defined inside a function and is only accessible within that function. Once the function exits, the local variable is destroyed, and its value cannot be accessed from outside the function.
Example
def my_name():
name = "Kavi" # Local variable
print("Inside the function my name is:", x)
my_name()
print(name) # This would raise an error because x is not accessible outside the function.
Global Variables
- A global variable is a variable that is defined outside of any function and is accessible throughout the program, including within functions (unless specifically overridden within a function). Global variables maintain their values until the program terminates.
Eaxample:
number = 1 # Global variable
def my_number():
print("Inside the function the number is:", number)
my_number() # Can access the global variable
print("Outside the function:", number) # Global variable accessible here as well
Modifying Global Variables Inside a Function
- If you want to modify a global variable within a function, you need to explicitly declare it as
global
using theglobal
keyword. Without theglobal
keyword, Python would treat the variable as a new local variable inside the function, and the global variable would remain unchanged.
Example:
age = 30 # Global variable
def my_age():
global age
age += 1 # Modifying the global variable
print("Inside the function:", age)
my_age()
print("Outside the function:", age) # Global variable now changed
__main__
in Python
- In Python,
__main__
refers to the environment where the top-level code is being executed. When a Python script is run directly, Python assigns the special name"__main__"
to the__name__
variable in that script.
if __name__ == "__main__":
- This construct is used to check if a script is being run directly or being imported as a module in another script. The code under this condition will only be executed if the script is run directly.
Example:
# script.py
def welcome():
print("Hello, world!")
if __name__ == "__main__":
welcome()
Purpose of __main__
:
- Testing and Debugging: It allows you to write test code in the same file without it running when the file is imported elsewhere.
- Modular Code: It helps keep code modular, where functions and classes are defined for reuse, but certain behaviours (like running a function) are executed only when the file is executed directly.
Local and Global Variables in Classes
When dealing with classes in Python, the concept of local and global variables applies similarly, but there are additional layers related to instance and class attributes.
Instance and Class Attributes In addition to local and global variables, classes introduce two other types of variables: instance attributes and class attributes.
Instance Attributes: These are specific to an instance (object) of a class. Each object has its own separate copy of these attributes.
Class Attributes: These are shared among all instances of a class and are declared outside the
__init__()
method.
Example
class MyClass:
class_variable = 5 # Class attribute, shared by all instances
def __init__(self, value):
self.value = value # Instance attribute
obj1 = MyClass(10)
obj2 = MyClass(20)
print(obj1.class_variable) # 5
print(obj2.class_variable) # 5
print(obj1.value) # 10
print(obj2.value) # 20
Summary of variables:
Local variables are declared inside a function and only exist within that function.
Global variables are declared outside of functions and can be accessed anywhere in the script unless shadowed by a local variable.
The
__name__ == "__main__"
construct is used to determine if a script is being run directly or imported, allowing control over code execution in different contexts.Instance attributes are tied to specific objects of a class.
Class attributes are shared among all instances of a class.
Generally speaking, I think it is always a good idea to clearly add inputs and outputs to a function in any complex function. This includes global variables, as you may want to use those functions in other scripts that do not have the same global variables.
10.4 Summary
Now you have had a look at building functions and classes. They are key parts of programming in python. Imagine living in a world without being able to define and call functions in python. Just think of the amount of code you would have to write and sift through!
Functions: Reusable blocks of code that perform a specific task. They help you concisely repeat different kinds of tasks.
Classes: Blueprints for creating objects (instances) that bundle data (attributes) and behaviour (methods). They are part of the Object-Oriented Programming paradigm. Both are useful for improving code structure, making it more modular, maintainable, and easier to debug.
Variables: Local and global variables, and instance and class attributes are important to manage. Being explicit with input and output variables in functions is often better.