lettura simple

Type Class in Python

In Python, everything is an object, including classes. Classes themselves are created by another class called type.

The type class is the default metaclass in Python. A metaclass is essentially a class of classes: a class that creates other classes and defines their behavior.

In other words, classes create objects (instances), but classes themselves are instances, meaning they are objects too.

For example, let's create a custom class:

class Foo:
    pass

This `Foo` class is actually an instance of `type`.

You can easily verify this.

print(type(Foo))

<class 'type'>

This confirms that the `Foo` class was created by the metaclass `type`.

In general, all Python classes are instances of the `type` class.

This includes Python's built-in classes as well. For example, the `list` class is also an instance of `type`:

print(isinstance(list, type))

True

The same applies to all other built-in Python classes, like `int()`, `float()`, `str()`, etc.

An interesting fact is that the `type` class is also an instance of itself:

print(isinstance(type, type))

True

This might seem "strange" since no set can contain all sets (as it would have to contain itself), but it's perfectly acceptable when it comes to classes.

Dynamic Class Creation

The `type` class can be used not only to get the type of an object but also to create new classes dynamically.

When you call type() with a single argument, you get the type of the object:

myList = [1, 2, 3]
print(type(myList))

<class 'list'>

However, if you call `type` with three arguments, you create a new class.

The three arguments are the name of the new class, a tuple of the base classes it inherits from, and a dictionary of the class attributes.

For example, let’s define a simple class:

class Foo:
    pass

Internally, Python does something like this:

Foo = type('Foo', (), {})

In this case, `type` takes three arguments:

  1. The name of the class: `'Foo'`
  2. A tuple of base classes: `()` is empty here because there is no inheritance.
  3. A dictionary of attributes and methods: `{}` is empty because we haven’t defined any attributes or methods for this class.

This shows that defining a class is just creating an instance of `type` that follows these specifications.

Example 2

Here’s a more complex example:

Let's create a new class using `type`:

Foo = type('Foo', (), {'year': 2020})

In this case:

  • `'Foo'` is the name of the new class.
  • `()` indicates that `Foo` does not inherit from other classes.
  • `{'year': 2020}` is a dictionary containing an attribute named `year` with the value `2020`.

You can now use this class like any other:

For example, you can create an instance:

obj = Foo()

Once the object is created, you can access its attributes:

print(obj.year)

2020

Example 3 (Attributes and Methods)

With the `type` class, you can also dynamically create classes with attributes and methods.

To add methods to a class, pass functions as values in the dictionary.

For example, define a function called `my_function` that will be used as the `__init__` method for the new class:

def my_function(self, year):
    self.year = year

The function defines an attribute named `year`.

Now, create a new class 'Foo' and link `my_function` to the `__init__` method:

Foo = type('Foo', (), {'__init__': my_function})

In this case, the dictionary passed to `type` includes the `__init__` method, which sets a `year` attribute on the instance.

You can now create an instance of this new class:

obj = Foo(2020)

Finally, you can access the object's attributes:

print(obj.year)

2020

Example 4 (Inheritance)

For instance, let’s create these classes:

class MyClass1:
    pass

class MyClass2:
    pass

Then, create a new class 'MyClass3' that inherits from 'MyClass1' and 'MyClass2' and has an attribute `language` set to 'python'.

The second argument of `type` specifies the inheritance hierarchy:

MyClass3 = type('MyClass3', (MyClass1, MyClass2), {'language': 'python'})

The result is equivalent to defining a new class.

You can now create instances of 'MyClass3':

obj = MyClass3()
print(isinstance(obj, MyClass3))

True

When you create a class with multiple base classes, Python determines the order in which it searches for attributes and methods using the tuple of classes provided.

This order is stored in the class’s __mro__ (Method Resolution Order) attribute.

print(MyClass3.__mro__)

(<class '__main__.MyClass3'>, <class '__main__.MyClass1'>, <class '__main__.MyClass2'>, <class 'object'>)

I hope this clarifies the concept! If you have any questions, feel free to ask.




Report a mistake or post a question




FacebookTwitterLinkedinLinkedin