For global variables that are mutable and freely available through imports, a lowercase letter with an underscore should be used when they do not need to be protected. If a variable shouldn't be used and modified outside of its origin module we consider it a private member of that module. A leading underscore, in that case, can mark the variable as a private element of the package, as shown in the following code:
_observers = []
def add_observer(observer):
_observers.append(observer)
def get_observers():
"""Makes sure _observers cannot be modified."""
return tuple(_observers)
Variables that are located in functions, and methods, follow the same rules as public variables and are never marked as private since they are local to the function context.
For class or instance variables, you should use the private marker (the leading underscore) if making the variable a part of the public signature does not bring any useful information, or is redundant. In other words, if the variable is used only internally for the purpose of some other method that provides an actual public feature, it is better to make it private.
For instance, the attributes that are powering a property are good private citizens, as shown in the following code:
class Citizen(object):
def __init__(self, first_name, last_name):
self._first_name = first_name
self._last_name = last_name
@property
def full_name(self):
return f"{self._first_name} {self._last_name}"
Another example would be a variable that keeps some internal state that should not be disclosed to other classes. This value is not useful for the rest of the code, but participates in the behavior of the class:
class UnforgivingElephant(object):
def __init__(self, name):
self.name = name
self._people_to_stomp_on = []
def get_slapped_by(self, name):
self._people_to_stomp_on.append(name)
print('Ouch!')
def revenge(self):
print('10 years later...')
for person in self._people_to_stomp_on:
print('%s stomps on %s' % (self.name, person))
Here is what you'll see in an interactive session:
>>> joe = UnforgivingElephant('Joe') >>> joe.get_slapped_by('Tarek') Ouch! >>> joe.get_slapped_by('Bill') Ouch! >>> joe.revenge()
10 years later... Joe stomps on Tarek Joe stomps on Bill
Let's take a look at naming styles for functions and methods in the next section.