Overview

Python classes and objects hold a fascinating array of attributes that provide insight into their inner workings. We'll unravel the mysteries behind these attributes, making the journey into the world of classes and objects even more approachable.

So, in this section, we will learn about the inner life of classes and objects.

  • The inner life of classes and objects refers to the way that classes and objects are implemented in the computer's memory. When you create a class, the computer creates a data structure that stores information about the class, such as its name, its methods, and its variables. When you create an object, the computer creates another data structure that stores information about the object, such as its state (the values of its variables) and its references to other objects.

Getting Acquainted with __dict__ and __name__

You're already familiar with one such attribute: __dict__. It's like a dictionary that lets you peek into an object's capabilities. But what about methods? Let's dive into an example:

Example:

class Coffee:
    price = 1.00
    def __init__(self):
        self.p = 1.50

    def show(self):
        pass
    def __hide(self):
        pass

drink = Coffee()
print(drink.__dict__)
print(Coffee.__dict__)

Output:

Object drink:
 {'p': 1.5}
Class Coffee:
 {'__module__': '__main__',
 'price': 1.0,
 '__init__': <function Coffee.__init__ at 0x0000018755288430>,
 'show': <function Coffee.show at 0x00000187552884C0>,
 '_Coffee__hide': <function Coffee.__hide at 0x0000018755288550>,
 '__dict__': <attribute '__dict__' of 'Coffee' objects>,
 '__weakref__': <attribute '__weakref__' of 'Coffee' objects>,
 '__doc__': None}

Running this code helps us see the methods and attributes. Pay close attention to where they exist – whether inside the object or the class itself.

There's another handy built-in attribute: __name__. It holds the name of the class. Simple as that. Just remember, it only exists within classes, not objects.

Determining the Class with type()

If you're ever curious about the class of an object, you can use type(). This function does a neat trick: it finds the class that an object was instantiated from. Let's take a look:

class Coffee:
    pass

print(Coffee.__name__)
drink = Coffee()
print(type(drink).__name__)

Output:

OUTPUT:

Keep in mind that trying something like print(drink.__name__) will result in an error.

Unveiling __module__ and __bases__

The __module__ attribute is also a string. It carries the module's name where the class definition resides. Take a peek at this code:

class Coffee:
    pass

print(Coffee.__module__)
drink = Coffee()
print(drink.__module__)

Output:

OUTPUT:

Remember, when a module is named __main__, it represents the currently executed file.

__bases__, on the other hand, is a tuple. Inside this tuple, you'll find classes – the direct superclasses of the class. They're listed in the order you've defined them within the class. To emphasize how inheritance works, here's a basic example:

class Milk:
    pass

class Coffee:
    pass

class Latte (Milk,Coffee):
    pass

def showInfo (cls):
    print('( ', end='')

    for x in cls.__bases__:
        print(x.__name__, end=' ')
    print(')')

showInfo(Milk)     #( object )
showInfo(Coffee)   #( object )
showInfo(Latte)    #( Milk Coffee )

Output:

OUTPUT:

A noteworthy point: if a class lacks explicit superclasses, it implicitly inherits from the object class, a fundamental Python class.

In conclusion, by understanding these attributes – __dict__, __name__, __module__, and __bases__ – you're well-equipped to navigate the intricacies of Python classes and objects. These insights not only unravel the hidden aspects but also enhance your ability to create sophisticated and structured code.