Python: A Deep Dive into Instances, Class Methods, and Instance Methods
Tags: python, instances, backenddevelopment, classmethods
Published: 2023-12-06 20:13:11

In Python, classes and instances play a fundamental role in object-oriented programming (OOP). In this blog, we’ll explore the concepts of instances, class methods or @classmethod, and instance methods (isinstance) in Python to understand their purposes and use cases.

## Instances in Python:

**Definition: **An instance is an individual occurrence of an object created from a class. It represents a specific realization of the class, with its own unique attributes and behaviors.

**Use Case: **Instances allow you to model and work with specific entities in your program. For example, if you have a Person class, instances of that class could represent individual people, each with their own name, age, and other attributes.

**Example:**
python
```
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
```

In this example, person1 and person2 are instances of the Person class.

## Instance Methods in Python:

**Definition:** Instance methods are methods that are bound to an instance of the class. They take self as the first parameter, representing the instance on which the method is called.
**Use Case:** Instance methods are used for operations that involve a specific instance of the class. They can access and manipulate the attributes of the instance.
```
class Car:
	def __init__(self, brand, model):
    	self.brand = brand
    	self.model = model

	def display_info(self):
    	print(f"{self.brand} {self.model}")

car_instance = Car("Toyota", "Camry")
car_instance.display_info()
```

In this example, display_info is an instance method that accesses and prints information specific to the car_instance.

## Class Methods with @classmethod
**Definition:** In Python, a class method is a method bound to the class rather than the instance of the class. Decorated with @classmethod, these methods take the class itself as the first parameter, conventionally named cls. One practical use case for class methods is creating instances of a class based on external data, such as database rows.

**Use Case:** Class methods are useful for operations that involve the class but not a specific instance. Commonly, they are used for tasks such as creating instances based on external data, such as from an instance_from_db method.

**Practical Example:** instance_from_db
Check out the following class method named instance_from_db:
```
@classmethod
def instance_from_db(cls, row):
    user = cls(row[0], row[1]) if row else None
    return user
```

Here, instance_from_db is designed to create an instance of the class it belongs to (cls) based on data retrieved from a database. It assumes that the order of data in the row corresponds to the attributes of the class.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5pho7zffxne82i1615zz.png)

Note: maintaining the column name and the order in which it is presented is important
```
@classmethod
   def find_by_symbol(cls, symbol):
       sql = """
           SELECT *
           FROM crypto_coins
           WHERE symbol = ?
       """
       row = CURSOR.execute(sql, (symbol,)).fetchone()
       return cls.instance_from_db(row) if row else None
@classmethod
   def instance_from_db(cls, row):
       return cls(row[0], row[1], row[2]) if row else None
```

This method is valuable in scenarios where you retrieve data from a database and want to convert it into instances of your class. It encapsulates the logic of creating an object from raw database data, providing a clean and centralized way to handle such conversions, adhering to rules of reusability and readability. 

### Application in find_by_name

Let’s see how instance_from_db is utilized in a practical scenario. Consider the following method, part of a User class, that retrieves a user from a database based on their username:
python
```
@classmethod
def find_by_name(cls, username):
    sql = """
        SELECT *
        FROM users
        WHERE username = ?
    """
    row = CURSOR.execute(sql, (username,)).fetchone()
    return cls.instance_from_db(row) if row else None
```
In this method, the find_by_name function executes an SQL query to fetch a user's data from a database. It then uses instance_from_db to convert the retrieved database row into an instance of the User class. If no row is found, it returns None.

This design choice allows for a clean separation of concerns and promotes code readability. The logic for creating instances from database rows is encapsulated within the class, making the code more modular and maintainable.

## Checking Object Types with isinstance

**Definition:** The isinstance function is a built-in Python utility used to check if an object is an instance of a particular class or a tuple of classes. It takes two parameters: the object to be checked and the class or tuple of classes to check against.

**Use Case:** Object Type Validation
Consider a scenario where you have different classes representing animals, such as Dog and Cat. You can use isinstance to check the type of an object:
```
class Dog:
    pass

class Cat:
    pass

dog_instance = Dog()
cat_instance = Cat()

print(isinstance(dog_instance, Dog))  # True
print(isinstance(cat_instance, Cat))  # True

print(isinstance(dog_instance, Cat))  # False
print(isinstance(cat_instance, Dog))  # False
```
In this example, isinstance is employed to verify whether dog_instance is an instance of the Dog class (which it is) and if cat_instance is an instance of the Cat class (which it is). It returns False when checking against classes to which the object does not belong.

### Handling Multiple Types
isinstance can also handle cases where an object might be an instance of any class in a tuple:
```
print(isinstance(dog_instance, (Dog, Cat)))  # True
print(isinstance(cat_instance, (Dog, Cat)))  # True
```
In this case, it returns True for both instances because they are instances of at least one of the classes in the tuple.

## 
Why Use Class Methods and Instance Methods? (Recap)
**Class Methods:** Class methods are useful when you need to perform operations related to the class itself rather than a specific instance. For example, class methods are bound to the class rather than an instance.

It is defined by the “@classmethod” decorator, and taking the ‘cls’ as its first parameter, and is useful for initializing and modifying class attributes.
```
class MyClass:
	class_variable = "I am a class variable"

	@classmethod
	def class_method(cls):
    	print(cls.class_variable)

MyClass.class_method()
```

**Instance Methods:** Instance methods are essential for working with and manipulating specific instances of a class. They take “self” as its parameter, and are powerful because they have access to both class-level attributes and instance-specific attributes.
```
class Dog:
	def __init__(self, name):
    	self.name = name

	def bark(self):
    	print(f"{self.name} says Woof!")

my_dog = Dog("Buddy")
my_dog.bark()
```

Incorporating @classmethod and isinstance into your Python codebase enhances the clarity and flexibility of your object-oriented design. Class methods simplify the creation of instances based on external data, promoting modular and maintainable code. Meanwhile, isinstance empowers you to perform type checking, allowing for robust and versatile program behavior.

By using these concepts, you can achieve a more organized structure in your Python projects. Now go code your next Project! 

Here is my Phase 3 Project for your own personal Crypto Portfolio which uses the above concepts, for reference: 
[
https://github.com/BookmDan/phase3_crypto](url)

Once you fork, and git clone: 
1. download _install pip_
2. “pipenv install’ and “pipenv shell” to start your environment
3. and then run “python lib/cli.py” and the command line menu should pop up