Python classes bundle data and behavior so you can model real objects cleanly. Class definitions use the class keyword and typically include an initializer and one or more methods. Below are two practical ways to create classes, starting with the standard approach and followed by a concise data-focused option.

Method 1: Define a standard class

Step 1: Declare a class with the class keyword.

class Dog:
    pass  # temporary placeholder

Step 2: Add an initializer __init__ to set instance attributes.

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

Step 3: Instantiate the class to create an object.

buddy = Dog("Buddy", 3)

Step 4: Add an instance method to define behavior.

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

    def bark(self):
        return f"{self.name} says woof!"

Step 5: Call the method on the instance.

print(buddy.bark())  # Buddy says woof!

Step 6: Provide a readable string with __str__ (used by print()).

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

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

Step 7: Add a class attribute for data shared by all instances.

class Dog:
    species = "Canis familiaris"  # class attribute shared by all dogs

    def __init__(self, name, age):
        self.name = name          # instance attribute
        self.age = age

Step 8: Modify an instance attribute to change object state.

buddy.age = 4
print(buddy.age)  # 4

Step 9: Optionally manage validation with a property.

Use a property when an attribute needs checks or transformation without changing your public API. The built-in decorator is documented in the documentation.

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age  # routed via the property setter below if preferred

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if not isinstance(value, (int, float)) or value < 0:
            raise ValueError("age must be a non-negative number")
        self._age = value

Method 2: Use @dataclass for concise data containers

When a class mainly stores data, @dataclass cuts boilerplate by auto‑generating __init__, __repr__, equality, and more. Options are detailed in the documentation.

Step 1: Import and annotate fields in a data class.

from dataclasses import dataclass

@dataclass
class Dog:
    name: str
    age: int

Step 2: Instantiate and use the generated methods.

buddy = Dog("Buddy", 3)
print(buddy)          # Dog(name='Buddy', age=3)
print(buddy == Dog("Buddy", 3))  # True

Step 3: Add defaults or computed behavior as needed.

from dataclasses import dataclass

@dataclass
class Dog:
    name: str
    age: int = 0            # default value
    species: str = "Canis familiaris"

    def bark(self) -> str:
        return f"{self.name} says woof!"

Key tips

  • Use instance attributes for per-object state and class attributes for shared constants.
  • Put all required fields in __init__ (or data class fields) to keep objects valid from creation.
  • Prefer properties to add validation later without changing attribute access syntax.
  • Give objects a clear string form with __str__ for users and __repr__ for developers.

That’s all you need to create practical Python classes—start with the standard pattern for behavior, or choose data classes when you mainly store fields and want less boilerplate.