With the current implementation of my class when I try to get the value of a private attribute using class method I get None as the output. Any Ideas on where I'm going wrong?
Code
from abc import ABC, abstractmethod
class Search(ABC):
@abstractmethod
def search_products_by_name(self, name):
print('found', name)
class Catalog(Search):
def __init__(self):
self.__product_names = {}
def search_products_by_name(self, name):
super().search_products_by_name(name)
return self.__product_names.get(name)
x = Catalog()
x.__product_names = {'x': 1, 'y':2}
print(x.search_products_by_name('x'))
What's happening in this code?
The code above seems fine, but has some behaviour that might seem unusual. If we type this in an interactive console:
Then the result is this:
That's pretty weird! In our class definition, we didn't give any attribute the name
_Catalog__product_names. We named one attribute__product_names, but that attribute appears to have been renamed.What's going on
This behaviour isn't a bug — it's actually a feature of python known as private name mangling. For all attributes that you define in a class definition, if the attribute name begins with two leading underscores — and does not end with two trailing underscores — then the attribute will be renamed like this. An attribute named
__fooin classBarwill be renamed_Bar__foo; an attribute named__spamin classBreakfastwill be renamed_Breakfast__spam; etc, etc.The name mangling only occurs for when you try to access the attribute from outside the class. Methods within the class can still access the attribute using its "private" name that you defined in
__init__.Why would you ever want this?
I personally have never found a use case for this feature, and am somewhat sceptical of it. Its main use cases are for situations where you want a method or an attribute to be privately accessible within a class, but not accessible by the same name to functions outside of the class or to other classes inheriting from this class.
Some use cases here: What is the benefit of private name mangling?
And here's a good YouTube talk that includes some use cases for this feature about 34 minutes in.
(N.B. The YouTube talk is from 2013, and the examples in the talk are written in python 2, so some of the syntax in the examples is a little different from modern python —
printis still a statement rather than a function, etc.)Here is an illustration of how private name mangling works when using class inheritance:
The methods defined in the base class
Fooare able to access the private attribute using its private name that was defined inFoo. The methods defined in the subclassBar, however, are only able to access the private attribute by using its mangled name; anything else leads to an exception.collections.OrderedDictis a good example of a class in the standard library that makes extensive use of name-mangling to ensure that subclasses ofOrderedDictdo not accidentally override certain methods inOrderedDictthat are important to the wayOrderedDictworks.How do I fix this?
The obvious solution here is to rename your attribute so that it only has a single leading underscore, like so. This still sends a clear signal to external users that this is a private attribute that should not be directly modified by functions or classes outside of the class, but does not lead to any weird name mangling behaviour:
Another solution is to roll with the name mangling, either like this:
Or — and this is probably better, since it's just a bit weird to be accessing an attribute using its mangled name from outside the class — like this: