Be a responsible programmer when doing Object-Oriented Programming 🇬🇧

What you will learn

After a first tutorial on Object-Oriented Programming in Python, I feel like it’s necessary to write more on OOP because when you read source code of many popular frameworks (like requests), inevitably you will come across some more advanced concepts relevant to collaborative programming.

No, I’m not talking about stuff like inheritance and polymorphism. These convoluted terms are actually the easiest ones to understand and I’m sure that you’ll find tons of tutorials on these subjects.

What you’ll learn in this tutorial are:

  • decorator

  • name mangling

  • __str__ and __repr__ method

  • class and instance method

  • class and instance variable

  • getter and setter

There are the minimal vocabulary that you should master before participating in collaborative projects.

I will implement these concepts by simplifying the web scraping framework example built in the previous tutorial. Check here if interested.

Class and instance variable

The scraper class that I built last time is a scraper capable of adjusting its behaviour according the newspaper.

Let’s say that this scraper is created by a company which offers all sorts of service related to web. The CEO wants every object instantiated by this class to be endowed with a property called company so that the clients know where the scraper comes from.

That’s when the class variable comes into play.

[15]:
class Crawler:

    company = "Web Crawling®, nlpinfrench.fr"

print(Crawler.company)
Web Crawling®, nlpinfrench.fr

As you can see when people access this class variable there is no need to instantiate an object, contrary to a instance variable which can only be accessed inside an object.

Note that it’s also possible to access it in the instance variable way because a class variable is always a part of any instance, although it’s admittedly less chic.

[14]:
crawler_class_var = Crawler()
print(crawler_class_var.company)
Web Crawling®, nlpinfrench.fr

Be visible

Now imagine that the CEO is not satisfied with this branding strategy and he wishes something even more visible so that one doesn’t even need to know the company property. How can you achieve it?

Let’s observe the following code.

[8]:
print("I'm a string")
I'm a string

The output of print is what is called a string representation. It is implemented by the __str__ method (which is one of the magic methods/dunder methods in Python). Here is a minimal working example.

[13]:
class Crawler:

    company = "Web Crawling®, nlpinfrench.fr"

    def __str__(self):
        return Crawler.company

crawler_str = Crawler()
print(crawler_str)
crawler_str
Web Crawling®, nlpinfrench.fr
[13]:
<__main__.Crawler at 0x7f81181b6a50>

However it stills needs a call to print, is there a way to show the company name each time the user inspects an object? For the moment as you see from the previous code, the interpreter returns only a mysterious <main.Crawler at 0x7f81181d0790>`.

The __repr__ method creates a so-called printable representation visible each time one inspects an object.

[12]:
class Crawler:

    company = "Web Crawling®, nlpinfrench.fr"

    def __repr__(self):
        return Crawler.company

crawler_repr = Crawler()

crawler_repr
[12]:
Web Crawling®, nlpinfrench.fr

Be control freak

Now that your CEO is happy with his self-branding strategy, you show your code to the senior software engineer who objects, rightly so, that the company name can be changed by anyone!

Yes, one can easily changes the class variable to, I don’t know, tell others that the spirit of prank is still alive.

[18]:
class Crawler:

    company = "Web Crawling®, nlpinfrench.fr"

    def __repr__(self):
        return Crawler.company

crawler_control = Crawler()
Crawler.company = "April Fool!"
crawler_repr
[18]:
April Fool!

This kind of jokes are certainly not appreciated by everyone. But the message that I’m conveying here is the danger of an easily modifiable class’s property which can in turn break an entire program with a single line of code.

In order to avoir this kind of accidents, the common practice is to put one _ before a variable name to inform other programmers that this variable shouldn’t be modified.

And you are expected not to use class variable if you don’t want it to be modified.

However, as you see from below, a determined pranker could still access and modify it.

[29]:
class Crawler:

    def __init__ (self, company):
        self._company =  company

lemonde_crawler = Crawler("nlpinfrench.fr")
lemonde_crawler._company = "April fool again!"
print(lemonde_crawler._company)
April fool again!

Now this is troublesome. Can’t we get rid of this determinant pranker for good?

The answer lies somewhat between yes and no.

In Python people often name mangling to change automatically the variable’s name when an object is instantiated. Let’s demonstrate it by an example.

[39]:
class Crawler:

    def __init__ (self, company):
        self.__company =  company

lemonde_crawler = Crawler("nlpinfrench.fr")
print(lemonde_crawler.__company)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-39-66f93e520566> in <module>
      5
      6 lemonde_crawler = Crawler("nlpinfrench.fr")
----> 7 print(lemonde_crawler.__company)

AttributeError: 'Crawler' object has no attribute '__company'

As you see it’s no longer possible to access the __company because of the name mangling. However this safety trick is easy to hack. It’s always possible to access it by _Class__property, besides it’s always possible to modify the value of the mangled variable.

[41]:
class Crawler:

    def __init__ (self, company):
        self.__company =  company

lemonde_crawler = Crawler("nlpinfrench.fr")

# I can still access it!
print(lemonde_crawler._Crawler__company)

# and modify it!
lemonde_crawler.__company = "Hello! April fool x3 :DDDD"

# and now access it again
print(lemonde_crawler.__company)

nlpinfrench.fr
Hello! April fool x3 :DDDD

We’re all consenting adults here

So what’s the point of name mangling?

The objective is really informative rather than restrictive. In Python programming, there is a commonly used phrase that says “we’re all consenting adults here”. We are all expected to behave like an adult.

In practice you are not to expected to fully embrace these naming rules as novice. Just keep in mind that you should think twice before modifying values of a mangled variable if you are in an open-source project.

Time to become a senior engineer

Wrap-up

As always, I recommend the book by Mark Lutz if you want to dive deep into OOP :

Programming Python: Powerful Object-Oriented Programming