DEV Community

Cover image for No One Expects the self._spanish_inquisition (Because it’s protected)
Charlotte Bush
Charlotte Bush

Posted on • Edited on

No One Expects the self._spanish_inquisition (Because it’s protected)

A member of Monty Python sits at a newscasters' desk outdoors. Text says: "And now for something completely _different."

Phase three of bootcamp has been an intro to Python. I’ve appreciated so much about it, from its relatively humane syntax to its modularity, to the way list comprehensions let me make this joke:

A wild-eyed man with a bushy beard. Text says "There are some who call me [Tim for Tim in self.Tim]"

As I delved into learning about Object Oriented Programming, I started noticing a pattern in the error messages I was getting, and I got curious. Whenever I created a property with a setter, a getter, or attributes, it would throw an error and return an infinite loop.

A sample of "recursion limit exceeded" pytest error messages.

(A sample photo of the errors I got on a mock code challenge last week)

In this sample, the code I was writing that threw the error was:

@property
  def price(self):
    return self.price
Enter fullscreen mode Exit fullscreen mode

Eventually, after a very frustrated 20 minutes of trial and error, I added a protected attribute indicator, like so:

    @property
      def price(self):
       return self._price
Enter fullscreen mode Exit fullscreen mode

and the recursion errors stopped, but I was left with the lingering question of why. Why did all attributes in setters and getters in Python OOP want to be private or protected? I’d heard that in languages like Java, the private keyword does what __ does in Python (private is two underscores, and protected is one) and if the folklore says that Python was created to solve Java’s problems, what problem does the __ and _ solve?

An image of Monty Python doing the Spanish Inquisition sketch. Text says "Nobody expects _protected attributes."

Well, __private and _protected attributes do a few things, some more obvious than others. Their chief weapons (other than fear and surprise, of course) are:

  1. Preventing direct access to an attribute without additional logic being performed. This could be really useful if you need users to do additional validation or logic before setting a value, like a password.
  2. Ensuring the attribute is always used in a consistent way, or returned in a specific format.
  3. Making it easier to change the internal implementation of a class without breaking something external, like a database record or an API. (I suspect this wasn’t the problem I ran into in my mock code challenge, though.)

I also bumped into a principle of OOP called Encapsulation, which aside from sounding like a molecular gastronomy technique, boils down to the idea that keeping data and their methods in one place makes them harder to access (and therefore modify and otherwise futz with) unintentionally. It forces modularity and intention, both of which are hallmarks of OOP. It also reminded me of all my adventures with functional and local scope in JavaScript, which was a fun blast of nostalgia.

A member of Monty Python dressed as a Viking. Text says "And I OOP."

A Digression:

For the curious at home, I followed the rabbit hole to find the other principles of OOP in Python. Other than Encapsulation, we have:

  • Inheritance: the idea that an object can inherit methods and properties from another, as in the case of parent and child classes. An example of this:

    
 


        # parent class
        
        class Person():
          def __init__(self, name, age):
            self.name = name
            self.age = age
    
          def display(self):
            print(self.name, self.age)
    
        # child class
        class Student(Person):
          def __init__(self, name, age):
            self.sName = name
            self.sAge = age
    
  • Polymorphism: the idea that a function or object can be used in multiple ways. A simple example of this is that you can use the + operator on both integers and strings in Python. When you see polymorphism in OOP, it’s generally paired with inheritance, allowing child class methods to override methods inherited from the parent when called with the child class object (aka method overriding.) In doing this, you can still access the methods of the parent, but you’ve effectively modified it to better meet the needs of the child. And finally:

  • Abstraction: the core of this principle is streamlining processes by only showing users details necessary to their experience. This usually gets achieved in OOP by having a base class and subclasses.

So I know now why Python wouldn’t want me to use a public attribute with a getter or a setter, and why it’d throw errors, but why was my code throwing those infinite recursion errors and not just regular syntax errors?

Turns out, the recursion happens when you try to access a public attribute from within the getter or setter method of a property. This is because Python will try to call the getter or setter method again, which will lead to infinite recursion. The reason for this? The names of both method and attribute are the same, so the infinite recursion is from Python thinking it needs to call itself over and over and over.

Check out this example:

           class MyClass:
            def __init__(self):
                self.name = "Charlotte"

            @property
            def name(self):
                return self.name
             @name.setter
                    def name(self, name):
                        self.name = name

        my_class = MyClass()
        print(my_class.name)
Enter fullscreen mode Exit fullscreen mode

This throws the “recursion limit exceeded” error from earlier, because name is the name of the method AND the attribute. Python gets confused (much like me) when names are messy like this. So throwing in a private or protected marker differentiates the attribute from the method, and prevents the infinite, test-breaking recursion. You’d fix the code in the above example like so:

          class MyClass:
            def __init__(self):
                self.__name = "Charlotte"

            @property
            def name(self):
                return self.__name

            @name.setter
            def name(self, name):
                self.__name = name

        my_class = MyClass()
        print(my_class.name)
Enter fullscreen mode Exit fullscreen mode

By making it private in this case, my_class.name becomes non-recursive!

Monty Python members dressed as peasants. Text says "Public attributes in a setter or getter: help! help! I'm being repressed!

And my final attribute privacy question: Is it better to use a private or a protected attribute?

This led me to exploring the difference between private and protected attributes. After the recursion error merry-go-round, I was expecting something weird as an answer, but it turns out the key difference is one of inheritability.

As Justin Ellis defines on his blog: “private (_)means that the attribute/method can only be used inside the class where it is defined. Protected () means that the attribute/method can only be used in the class where it is defined or its subclasses.” That subclass access makes all the difference, and I can see the appeal of protected attributes in the appropriate context.

Whew! If you’ve made it this far, thanks for reading this long, strange trip of a blog. Tldr;

  1. What does making attributes and classes private (or protected) actually do?
  2. What are the 4 principles of OOP in Python?
  3. Why do public attributes in setters and getters throw recursion errors (and regular not syntax errors)?
  4. Are private or protected attributes better to use?

I hope this helps!

A member of Monty Python doing a silly walk. Text says "this walk is def silly, __init__."

Works Used:

  1. “Image tagged in what is your name,” Imgflip. https://imgflip.com/i/7zztzs (accessed Sep. 22, 2023).
  2. “Nobody Expects the Spanish Inquisition Monty Python,” Imgflip. https://imgflip.com/i/803cpn (accessed Sep. 22, 2023).
  3. “Encapsulation in Python - GeeksforGeeks,” GeeksforGeeks, Oct. 15, 2019. https://www.geeksforgeeks.org/encapsulation-in-python /
  4. “Python: The four pillars of Object-Oriented Programming,” www.linkedin.com. https://www.linkedin.com/pulse/python-four-pillars-object-oriented-programming-benjamin-b-phiri/ (accessed Sep. 22, 2023).
  5. “Access Modifiers in Python,” Justin A. Ellis, Jan. 15, 2022. https://jellis18.github.io/post/2022-01-15-access-modifiers-python/#:~:text=Private%20vs%20Protected&text=It%20will%20actually%20raise%20an (accessed Sep. 22, 2023).
  6. “Image tagged in and now for something completely different,” Imgflip. https://imgflip.com/i/803c57 (accessed Sep. 22, 2023).
  7. “Help! Help! I’m being repressed!,” Imgflip. https://imgflip.com/i/803ci0 (accessed Sep. 22, 2023).
  8. “Ministry of Silly Walks,” Imgflip. https://imgflip.com/i/803d5k (accessed Sep. 22, 2023).
  9. “Image tagged in monty python viking,” Imgflip. https://imgflip.com/i/803gu1 (accessed Sep. 22, 2023).
  10. “BBC Radio 4 - Front Row, Terry Gilliam and Edward Gardner, Joshua Ferris, Clean Bandit, Terry Gilliam Animations for Monty Python’s Flying Circus - Artwork by Terry Gilliam of a woman looking in a mirror.,” BBC. https://www.bbc.co.uk/programmes/p02067rh/p02067gc (accessed Sep. 22, 2023).

Top comments (0)