Programmer's Python - Objects Become Classes
Written by Mike James   
Monday, 04 July 2022
Article Index
Programmer's Python - Objects Become Classes
Instance Attributes
Functions Become Methods

Everything in Python might well be an object, but if this is the case we need some way of creating new custom objects. How  objects become classes and more is explained in this extract from my book, Programmer's Python: Everything is an Object, Second Edition.

Programmer's Python
Everything is an Object
Second Edition

Is now available as a print book: Amazon

pythonObject2e360

Contents

  1. Get Ready For The Python Difference
  2. Variables, Objects and Attributes
  3. The Function Object
  4. Scope, Lifetime and Closure
  5. Advanced Functions
  6. Decorators
  7. Class, Methods and Constructors
      Extract 1: Objects Become Classes ***NEW!
  8. Inside Class
  9. Meeting Metaclasses
  10. Advanced Attributes
  11. Custom Attribute Access
  12. Single Inheritance
  13. Multiple Inheritance
  14. Class and Type
  15. Type Annotation
  16. Operator Overloading
  17. Python In Visual Studio Code

 Extracts from the first edition

Advanced Attributes

Everything in Python might well be an object, but if this is the case we need some easier and flexible way of creating new custom objects. We used the class keyword to create one-off objects in Chapter 2, but this doesn’t solve the problem of what to do if we need tens or even hundreds of copies of an object. In fact, class is more a solution to this problem than creating a single one-off object. When used in its more expanded and usual role, its behavior is more sophisticated.

In this chapter we look at the general idea of an object factory and in particular the object constructor. Python implements what looks like an object constructor that you might find in other class-based languages, but it has some interesting differences which might confuse you if you already know a more traditional approach to the problem as found in Java, C++ or C#.

The Classical Constructor

It might help to look first at how class-based languages deal with the problem of creating instances. If you know about how class-based languages work just skip to the next section.

In class-based languages, instances of a class are generally created using the constructor which implicitly uses the class definition to manufacture an instance of the class. For example, in languages like Java, C++ or C# you would write:

MyClass myObject = new myClass();

where myClass() is the constructor.

The constructor is a function that returns an instance of the class. This is a fact that is often overlooked by programmers in class-based languages because they don't actually have to write anything in the constructor for it to do its job. That is, an empty or default constructor will automatically do the job of creating an instance. This results in programmers thinking that constructors are really only needed when something has to be initialized or set up in some way.

The idea of a constructor can be extended to the idea of an object factory. An object factory is an object that will create another object, perhaps another instance of its own type or another completely different type. The idea of a function which creates objects is core to class-based languages and, in fact, to all object-oriented languages and Python in particular.

The Class Object

The class object is an example of a Python callable. The function object is also a Python callable and basically any callable can be treated as a variation on a function object.

You can use the class keyword to create a custom class object as described in Chapter 2. It creates a new object with the attributes you define. That is:

class myClass:
    myAttribute = 1

creates a new class object with the single attribute. You can now use myClass as if it was a custom object, which it is, and it has a single attribute, myAttribute. For example:

myClass.myAttribute = 2

is perfectly valid and it sets the myAttribute to 2 as you would expect. However, you can also call the myClass object as if it was a function and, if you do this, by default it will seem to create a copy of itself – an instance of the class. What actually happens is a little more subtle and it is important you know how it works, but for the moment let’s just see it in action:

myObject=myClass()
print(myClass.myAttribute)
print(myObject.myAttribute)

If you try this out you will discover that you now have a myClass object and a myObject object and they both have a myAttribute attribute. The myObject object is an instance of myClass and, initially at least, every instance seems to have the same attributes as myClass. In this sense myClass is a specification for its instances, but in Python it is also an object in its own right. You can use myClass to create as many instances as you require. It looks as if myObject is a complete copy of myClass, but this isn’t the case. In fact myObject is an empty object.

All of the attributes that an object has are stored in its built-in __dict__ attribute, which as you can guess is a dictionary of name, value pairs. For example, if you look at the __dict__ attribute of myClass you will see:

{'__module__': '__main__', 'myAttribute': 1, 
'__dict__': <attribute '__dict__' of 'myClass' objects>,
'__weakref__': <attribute '__weakref__' of 'myClass' objects>,'__doc__': None}

which contains the built in attributes and myAttribute and its value of 1.

When myObject is first created is has no custom attributes and hence its __dict__ is empty and it has no attributes of its own. When you try to access an attribute on the myObject its local dictionary object is searched i.e. myObject.__dict__ and then its class __dict__, myClass.__dict__ is searched. Every instance object also has has a __class__ attribute that references the instance class and this allows the system to search the class __dict__ for the attribute.

In this case when you use:

myObject.myAttribute

the system first looks in myObject.__dict__ and when it doesn’t find it then it looks in myObject.__class__.__dict__

class1

To be clear, when first created an instance of a class shares all of the class’s attributes and has none of its own. That is when you use:

myObject.myAttribute

you are actually accessing:

myClass.myAttribute

If you know JavaScript you will recognize that the class object is acting as a prototype for the instance object. You can see that this is true if you try:

myObject = myClass()
print(myClass.myAttribute)
myClass.myAttribute = 2
print(myObject.myAttribute)

The final print displays 2 because changing myClass.myAttribute changes the shared attribute.



Last Updated ( Wednesday, 06 July 2022 )