Friday, 27 February 2026

Python and Prototype-based languages

In my previous post I explained how class level attributes and the lookup mechanism in Python allow us sharing an attribute between instances through the class while it's only being read, and then getting it added to the instance when it's written through the instance rather than the class.

So we share the continent attribute between instances in Python like this:



>>> class Person:
...     continent: str = "Europe"
...     def __init__(self, name):
...         self.name = name
...         
>>> p1 = Person("Francois")
>>> p2 = Person("Iyan")
>>> print(p1.continent)
Europe
>>> print(p2.continent)
Europe
>>> p2.continent = "Asia"
>>> print(p1.continent)
Europe
>>> print(p2.continent)
Asia


This this not the first time I see this behaviour, I've known since many years ago that it exists in JavaScript, via the prototype chain, like this:


let person_proto = { continent: "Europe" }
let p1 = { name: "Francois" }
Object.setPrototypeOf(p1, person_proto);

let p2 = { name: "Iyan" }
Object.setPrototypeOf(p2, person_proto);

console.log(p1.continent); // Europe
console.log(p2.continent); // Europe

p2.continent = "Asia"

console.log(p1.continent); // Europe
console.log(p2.continent); // Asia

Notice that we can do it either directly setting the [[Prototype]] via Object.setPrototypeOf, or indirectly by setting the value in the prototype of a function and using that function as constructor:


console.log("-------------------")
function Person(name) {
    this.name = name;
}
Person.prototype.continent = "Europe";
p1 = new Person("Francois")
p2 = new Person("Iyan")

console.log(p1.continent); // Europe
console.log(p2.continent); // Europe

p2.continent = "Asia"

console.log(p1.continent); // Europe
console.log(p2.continent); // Asia

This feels like a feature of Prototype-based languages (JavaScript is by far the most widely used Prototype-based language), so one could wrongly wonder if somehow Python is also a Prototype-based language. Maybe it's like modern JavaScript, where classes are just syntactic sugar on top of prototypes. No, it's not. Python is a class based language, only that it behaves like a prototype‑based system in how it resolves attributes, but the defining point is that Python is not a prototype‑based language in how it constructs inheritance chains .

From the wikipedia article:

In prototype-based languages that use delegation, the language runtime is capable of dispatching the correct method or finding the right piece of data simply by following a series of delegation pointers (from object to its prototype) until a match is found. All that is required to establish this behavior-sharing between objects is the delegation pointer.

In JavaScript these "delegation pointers" make up the prototype chain. Each object is linked to it's prototype (via the [[Prototype]] internal property), and the attribute look up mechanism uses that chain to search for attributes. In Python that "delegation chain" is the MRO (Method Resolution Order) that depends on the __class__ and __bases__ attributes. Python dynamism allows us setting dynamically the class of an object (same as in JavaScript we can alter the prototype chain of an object) by setting its __class__ attribute to a different class. I made use of this technique in this previous post. So, having said this, why is it that Python is not considered as a Prototype based language?

Because there is a limitation. The __class__ attribute can only point to classes (objects that are instances of type), not to simple objects (because in Python there's a clear difference between objects that are classes (or metaclasses), that is, types, and objects that are not. If we try to set __class__ to "non type" object, we get an exception: TypeError: __class__ must be set to a class, not 'xxxx' object


class Animal:
	pass
	
class Person:
	pass
	
a1 = Animal()
p1 = Person()

a1.__class__ = Person

a1.__class__ = p1
Traceback (most recent call last):
  File "", line 1, in 
    a1.__class__ = p1
    ^^^^^^^^^^^^
TypeError: __class__ must be set to a class, not 'Person' object


That's the key, only classes can be used as "prototypes", so we're in a class based language.

No comments:

Post a Comment