#!/usr/bin/env python # coding: utf-8 # # The Tao of Python # ## The intricate relationship between "object" and "type" and how metaclasses, classes and instances are related # # # Contents of this talk # * Object-oriented relationships # * Relationship rules # * What is a Python object? # * Classes as objects # * Metaclasses # * What is `type`? # * What is `object`? # * How are `type` and `object` related? # * The Python objects map # # Most of this talk is based on [this article](http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html) by Shalabh Chaturvedi. # # Why is this talk useful? # * Actually it's not terribly useful # * Deep understanding of the Python object model # * Clarification of the role and behavior of classes, metaclasses and instances # * Appreciation of the language on different level # * Zen-like satisfying moment of understanding # * Bragging rights :D # # Object-oriented relationships # While we introduce many different objects, we only use two kinds of relationships: # 1. ***is a kind of*** (solid line): Also known as **specialization** or **inheritance**, this relationship exists between two objects when one (the subclass) is a specialized version of the other (the superclass). A snake is *a kind of* reptile. It has all the traits of a reptile and some specific traits which identify a snake. Terms used: *subclass of*, *superclass of*, *superclass-subclass* or simply *is a*. # 2. ***is an instance of*** (dashed line): Also known as **instantiation**, this relationship exists between two objects when one (the instance) is a concrete example of what the other specifies (the type). I have a pet snake named Squasher. Squasher is an instance of a snake. Terms used: *instance of*, *type of*. # # # Relationship rules # 1. If A is a subclass of B, and B is a subclass of C, then A is a subclass of C. # 2. If X is an instance of A, and A is a subclass of B, then X is an instance of B. # 3. If B is an instance of M, and A is a subclass of B, then A is an instance of M. # # # What is a Python object? # An object is an entity with the following characteristic properties: # 1. *Identity* (i.e. given two names we can say for sure if they refer to one and the same object, or not). # 2. A *value* - which may include a bunch of attributes (i.e. we can reach other objects through ```objectname.attributename```). # 3. A *type* - every object has exactly one *type*. For instance, the object ```2``` has a type ```int``` and the object ```"joe"``` has a type ```str```. # 4. One or more *bases*. A base is similar to a super-class or base-class in object-oriented lingo. # In[1]: two = 2 print(type(two)) # In[2]: print(type(type(two))) # In[3]: print(type(two).__bases__) # In[4]: print(dir(two)) # # Rule 1: Everything is an object # And I mean **everything**. Even things that are "primitive types" in other languages. # * You can store them in variables # * You can pass them as parameters to functions # * You can return them as the result of functions # * You can construct them at runtime # # And more importantly: You can treat *every* programming construct in a **uniform** and **consistent** way # # Functions as objects # * When you use the keyword `def`, Python creates a function object. # * Functions can be passed around as arguments to other functions. # * These functions that take other functions as arguments are called **higher order functions**. # * e.g. the `map` function takes a function and an iterable and applies the function to each item in the iterable. # # Classes as objects # 1. When you use the keyword ```class```, Python executes it and creates an object. # 2. This object (the *class*) is itself capable of **creating objects** (the *instances*), and this is why it's a class. # 3. Since classes are objects, they must be generated by something, this is *metaclasses*. # 4. Since metaclasses are also objects, they must also be generated by something, this is **again** *metaclasses*. # # Therefore: Objects are instances of classes, classes are instances of metaclasses and metaclasses are instances of themselves. # # Metaclasses (these are objects too!) # 1. Metaclasses are the "stuff" that creates classes. # 2. You define classes in order to create objects, right? # 3. We learned that Python classes are objects. # 4. Well, metaclasses are what creates these objects. They are the classes' classes, you can picture them this way: # - `MyClass = MetaClass()` # - `MyObject = MyClass()` # # >Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why). **Tim Peters** # # What is `type`? # 1. Remember the function `type`? The good old function that lets you know what type an object is. # 2. `type` can also create classes on the fly. `type` can take the description of a class as parameters, and return a class as `type(name, bases, dct)`. # - `name` is a string giving the name of the class to be constructed. # - `bases` is a tuple giving the parent classes of the class to be constructed. # - `dct` is a dictionary of the attributes and methods of the class to be constructed. # 3. Why the heck is it written in lowercase, and not `Type`? Consistency with `str`, the class that creates strings objects, and `int` the class that creates integer objects. `type` is just the class that creates `class` objects. # In[5]: class A: pass a = A() print(type(a)) print(type(A)) print(A.__bases__) # In[6]: A = type('A', (), {}) a = A() print(type(a)) print(type(A)) print(A.__bases__) print(isinstance(a, A), isinstance(a, object), issubclass(A, object)) # In[7]: def f(): """My name is f.""" pass print(type(f)) print(type(type(f))) print(type(f).__bases__) print(f.__doc__) # # The power of `type` # 1. Everything is an object in Python, and they are all either instances of classes or instances of metaclasses. # 2. `type` is the metaclass Python uses to create (i.e. *instantiate*) all classes and metaclasses, **including** `type` itself. # 3. `type` is actually its own metaclass. This is not something you could reproduce in pure Python, and is done by cheating a little bit at the implementation level. # # # What is `object`? # 1. `object` is the class that **all** classes inherit from. # 2. All classes **including** `object` are subclasses of themselves. # 3. All classes **including** `object` are subclasses of `object`. `object.__bases__` is an empty tuple. # 4. All classes **except** `object` will have `object` in `__bases__` in a class in their inheritance hierarchy. # # Kinds of objects # # * There are two kinds of objects in Python: # 1. *Type objects* - can create instances, can be subclassed. e.g. `type`, `object`, `int`, `str`, `list`. # 2. *Non-type objects* - cannot create instances, cannot be subclassed. e.g. `1`, `"hello"`, `[1, 2, 3]`. # * `type` and `object` are two primitive objects of the system. # * `objectname.__class__` exists for every object and points the type of the object. # * `objectname.__bases__` exists for every type object and points the superclasses of the object. It is empty only for `object`. # # # Recap # 1. All classes and metaclasses **including** `object` are *subclasses* of `object`. # 2. All classes and metaclasses **including** `type` are *instances* of `type`. # 3. All objects **including** `object` are *instances* of `object`. # In[8]: issubclass(type, object) # Recap rule #1 # In[9]: issubclass(object, object) # Recap rule #1 # In[10]: issubclass(object, type) # Recap rule #1 # In[11]: isinstance(object, type) # Recap rule #2 # In[12]: isinstance(type, type) # Recap rule #2 # In[13]: isinstance(type, object) # Recap rule #3 # In[14]: isinstance(object, object) # Recap rule #3 # # # # # # # In[15]: from IPython.display import Image, display display(Image(url='figures/mind_blown.gif', width=400)) # # The Python Objects Map # # # Rant: JavaScript # # # # Rant: Ruby # >Ruby inherited the Perl philosophy of having more than one way to do the same thing. I inherited that philosophy from Larry Wall, who is my hero actually. Y. Matsumoto, creator of Ruby. # # >Matz chose to sacrifice first-class functions *just* so he could make parentheses **optional**. # # >Methods are a fundamental part of Ruby's syntax, but they are not values that Ruby programs can operate on. That is, **Ruby's methods are not objects** in the way that strings, numbers, and arrays are. From [The Ruby Programming Language Book](https://www.amazon.com/Ruby-Programming-Language-Everything-Need/dp/0596516177). # # # # References # 1. [Shalabh Chaturvedi - Python Types and Objects](http://www.cafepy.com/article/python_types_and_objects/python_types_and_objects.html) # * [StackOverflow - What is a metaclass in Python?](http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python) # * [All about metaclasses in Python](https://pythontips.com/2013/09/20/all-about-the-metaclasses-in-python/) # # # Thank you! #