C
PY

C to Python

10 lessons

Progress0%
1Variables & Types2Functions3Arrays → Lists4Structs → Classes & Dicts5Memory Management6String Handling7File I/O8Object-Oriented Programming9Exceptions and Context Managers10Standard Library and Tools
All Mirror Courses
C
PY
Object-Oriented Programming
MirrorLesson 8 of 10
Lesson 8

Object-Oriented Programming

Python classes vs C structs + function pointers — encapsulation and inheritance

Introduction

In this lesson, you'll learn about object-oriented programming in Python. Coming from C, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
C
From C:

In C, you're familiar with python classes vs c structs + function pointers — encapsulation and inheritance.

PY
In Python:

Python has its own approach to python classes vs c structs + function pointers — encapsulation and inheritance, which we'll explore step by step.

The Python Way

Let's see how Python handles this concept. Here's a typical example:

PY
Python Example
import math
from abc import ABC, abstractmethod

# Abstract base class (= C interface via function pointers)
class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...
    @abstractmethod
    def perimeter(self) -> float: ...

    def describe(self) -> str:
        return f"Area={self.area():.2f}, P={self.perimeter():.2f}"

# Concrete class (= C struct + functions)
class Circle(Shape):
    def __init__(self, radius: float):
        self.radius = radius  # instance attribute

    def area(self) -> float:
        return math.pi * self.radius ** 2

    def perimeter(self) -> float:
        return 2 * math.pi * self.radius

class Rectangle(Shape):
    def __init__(self, w: float, h: float):
        self.w, self.h = w, h

    def area(self) -> float: return self.w * self.h
    def perimeter(self) -> float: return 2 * (self.w + self.h)

# Polymorphism — works on any Shape subclass
def print_shapes(shapes: list[Shape]) -> None:
    for s in shapes:
        print(s.describe())  # correct method called per type

print_shapes([Circle(5), Rectangle(3, 4)])

Comparing to C

Here's how you might have written similar code in C:

C
C (What you know)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

// Simulating OOP in C with structs and function pointers
typedef struct Shape Shape;
struct Shape {
    double (*area)(const Shape *self);
    double (*perimeter)(const Shape *self);
};

typedef struct {
    Shape base;   // "inherit" by embedding
    double radius;
} Circle;

double circle_area(const Shape *self) {
    const Circle *c = (const Circle *)self;
    return M_PI * c->radius * c->radius;
}

void circle_init(Circle *c, double radius) {
    c->radius = radius;
    c->base.area = circle_area;
}

int main(void) {
    Circle c;
    circle_init(&c, 5.0);
    // Polymorphism via function pointer
    printf("Area: %.2f\n", c.base.area((Shape*)&c));
    return 0;
}
Mirror Card
C
From C:

You may be used to different syntax or behavior.

PY
In Python:

Python classes have proper encapsulation — no embedding tricks like C

Mirror Card
C
From C:

You may be used to different syntax or behavior.

PY
In Python:

__init__ is the constructor; no explicit malloc needed

Mirror Card
C
From C:

You may be used to different syntax or behavior.

PY
In Python:

ABC + @abstractmethod is Python's formal interface — stricter than C function pointers

Mirror Card
C
From C:

You may be used to different syntax or behavior.

PY
In Python:

Python inheritance is straightforward: class Dog(Animal)

Mirror Card
C
From C:

You may be used to different syntax or behavior.

PY
In Python:

self is explicit in every method; C uses a pointer passed manually

Step-by-Step Breakdown

1. Class = Struct + Functions

A Python class combines C's struct (data) and its associated functions into one unit. Methods automatically receive self — the equivalent of a struct pointer.

C
C
typedef struct { double r; } Circle;
void circle_init(Circle *c, double r) { c->r = r; }
PY
Python
class Circle:
    def __init__(self, r: float):
        self.r = r     # like struct field
    def area(self):
        return math.pi * self.r**2  # self = struct pointer

2. Inheritance

Python inheritance is cleaner than C's embedding trick. class Dog(Animal) gives Dog all of Animal's methods automatically.

C
C
typedef struct { Animal base; char name[50]; } Dog;
// Must manually forward all Animal's methods
PY
Python
class Animal:
    def speak(self): return "..."

class Dog(Animal):
    def speak(self): return "Woof"  # override

d = Dog()
d.speak()  # "Woof" — no forwarding needed

3. Abstract Methods

ABC + @abstractmethod is Python's formal way to require subclasses to implement methods — like C's function pointer fields but compile-time checked.

C
C
// C: function pointer in struct — no compile-time check if left NULL
PY
Python
from abc import ABC, abstractmethod
class Shape(ABC):
    @abstractmethod
    def area(self) -> float: ...
# class Square(Shape): pass  # Error: must implement area()

4. Dunder Methods

Python's dunder (double-underscore) methods let classes define built-in behavior. __repr__, __str__, __eq__, __lt__ are the most useful.

C
C
// C: must write compare_circles(), print_circle() manually
PY
Python
class Point:
    def __init__(self, x, y): self.x, self.y = x, y
    def __repr__(self): return f"Point({self.x},{self.y})"
    def __eq__(self, o): return self.x==o.x and self.y==o.y
    def __lt__(self, o): return (self.x**2+self.y**2) < (o.x**2+o.y**2)

p = Point(1,2)
print(p)           # Point(1,2)
p == Point(1,2)    # True
points.sort()      # uses __lt__

Common Mistakes

When coming from C, developers often make these mistakes:

  • Python classes have proper encapsulation — no embedding tricks like C
  • __init__ is the constructor; no explicit malloc needed
  • ABC + @abstractmethod is Python's formal interface — stricter than C function pointers
Common Pitfall
Don't assume Python works exactly like C. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • Class = struct + functions; __init__ = constructor; self = the struct pointer
  • Inheritance: class Dog(Animal) — no embedding tricks; all parent methods available
  • ABC + @abstractmethod enforces method implementation — like required function pointers
  • Dunder methods (__repr__, __eq__, __lt__) customize built-in behavior
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your C code in Python to practice these concepts.
PreviousNext