This repository talks about type, metclasses etc. features existing in python.
Lets declare a class in python and create object from it as below:
class ObjectCreator(object):
pass
>>> my_obj = ObjectCreator()
>>> print(my_obj)
<__main__.ObjectCreator object at 0x0000027ADB911B70>
We say classes are pieces of code that describe how to produce an object and thats true in python too. However, classes are more than that in python. Classes are objects themselves in python world.
Now since classs is an object:-
- we can assign class to a variable
- we can copy it
- we can add attributes to it
- we can pass it as a function parameter
Please refer to these code files here which make use of python classes as objects.
Since classes are objects in python world, they themselves must be generated by something. 🤔
So, when we use class keyword, python creates this class object automatically. But as with most things in Python, it gives you a way to do it manually. 🙂
Now type function is used to tell type of the object.
print(type(1)) #<type 'int'>
print(type("1")) #<type 'str'>
print(type(ObjectCreator)) #<type 'type'>
print(type(ObjectCreator())) #<class '__main__.ObjectCreator'>
However, besides above type has the ability to create classes on the fly i.e. dynamically. type as a class creator takes 3 parameters:
- name: name of the class
- bases: tuple of parent classes. It can be empty
- attrs: dictionary containing atrributes names and values
Lets take an example:
class MyShinyClass(object):
pass
Now MyShinyClass can be created dynamically(on the fly) as:
MyShinyClass = type("MyShinyClass", (), {})
print(MyShinyClass) //<class '__main__.MyShinyClass'>
Lets take another example:
class Foo:
bar = True
Now above class can be created dynamically as:
Foo = type('Foo', (), {"bar": True})
Continuing with Foo class, we can create a new class using Foo as a parent class:
class FooChild(Foo):
pass
Now FooChild can be created dynamically as:
FooChild = type("FooChild", (Foo,), {})
Now lets add methods to our FooChild class. For this, we will create a function with peoper signature and assign it as an attribute to the class.
def echo_bar(self):
print(self.bar)
FooChild = type("FooChild", (Foo,), {"echo_bar": echo_bar})
So we see classes are objects and can be created on the fly using type method. Python does that automatically when it sees class keyword. This automation comes from using metaclass feature in python.
Now we define classes in python in order to create objects(instances of classes). The main class which is actually responsible for creating objects in memory is object class because it has been blesses with some special attributes to do its work. All classes we define subclass this object class otherwise our objects would not be created.
However, these built-in(predefined) classes or user-defined classes are themselves objects in python world. Something in python is handling the creation of these classes. This something is called metaclass.
type is a predefined class in python that is responsible for creating classes in memory as seen above. This is the most basic class which is created by nothing. Even object class in python is also created by type. type class is also called metaclass since it is used to create other classes. type class can be subclassed. Any other class that wants to create classes must subclass type. All classes subclassing type will be called metaclasses.
Concluding this, type and object are doing the same thing. Both are basic classes in what they do. Now, object is the class that is actually used to create in memory objects and is created by type itself. type is class that is actually used to create in memory classes and hence its a metaclass.
the metaclass attribute (Python2):
class Foo(Object):
__metaclass__ = something
When we write class Foo(object)
, python does not create class. It will first look for metaclass attribute in class definition. If it finds it, it will use it to create Foo, otherwise type is used to create Foo because type is used to object class.
Lets take another example:
class Foo(Bar):
pass
- Now, python will look for metaclass in class defintion, does it find it? No, what next then?
- It will look for metaclass attribute at module level. lets say it does not find it. what next?
- It will use the metaclass used to create its parent class Bar which will be given by Bar.class and not metaclass attribute found in Bar.
Note- metaclass attribute is deprecated in python3. To use metaclass feature in python3, use the following syntax: class Foo(object, metaclass=something)
.
Now lets divert our focus to details of object creation in python:
new is a special method of any class in python that is actually responsible for creating object in memory. It always takes class as the first argument.
Lets take some examples:
now class is an object factory.
new_obj = Object.__new__(Object)
new_obj is an empty object created in memory which has no significance. What is sent as argument to new becomes the type of newly created object i.e. type(new_obj) will output <class 'object'>.
now lets say we create new_obj using the following syntax:
new_obj = Object()
This syntax also creates new_obj but this time firstly new was run and after that init was run implicitly.
now we know type is metaclass. It is a class factory. Therefore,
new_cls = type.__new__(type, "Foo", (), {})
Here new_cls is the class created in memory which has type <type 'type'> and name Foo. if,
new_cls = type()
here, along with new, init is also run.