Explaining the use of underscored variables in init method in Python
The aim of this explainer💡 is to analyze underscored vars in python initializers — also known as backing field. In OOP languages, * backing field (or backing store) is a private field that stores the data exposed by a public property. Etymologically, it seems, it’s comparable with backing music, meaning providing support for the main performer
2 min readJun 9, 2023
1. Backing fields can be used to avoid name clash with methods and to hide implementation details
- why
_number
?
- avoid name clash with the method
number()
- there is a widely accepted convention that implementation details for an object not intended for consumption/manipulation by clients of an object do start with an
_
- you can access them in the REPL with underscore, too
- very handy for debugging in early testing
- not recommended for prod code
class Flight:
def __init__(self, number):
self._number = number
def number(self):
return self._number
- in this example, you receive a flight number by calling its instance method
.number()
or by calling the instance variable._numbers
- see https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
- for more on this convention, see Explaining Properties in OOP Python > …Java-style getters methods (aka accessors)
2. Backing fields can also be used accompanied with None
as their default value in cases a method that is backed should run only ONCE
- Look at the following code — Will
read_only_keys
run only once? I need that. It creates a unique key that I don't want to change each time the property is accessed!
class Client:
def __init__(self, client_tag: str, ticket_id: str):
self.client_tag = client_tag
self.org_id = handlers.get_org_id(client_tag)
self.ticket_id = ticket_id
self.dev_server = f"{self.client_tag.replace('_', '-')}-dev1.net"
self.prod_server = f"{self.client_tag.replace('_', '-')}-prod1.net"
@property
def read_only_keys(self):
return self.get_keys(self, KeyType.READ_ONLY)
- The
read_only_keys
property will not run only once. Each time you access theread_only_keys
property, theget_keys
method will be called withKeyType.READ_ONLY
as an argument and a new key will be generated. - To generate a single key and store it, you can modify the code to store the result of the first call to
get_keys
in an instance variable and return that value for subsequent accesses to theread_only_keys
property. - Here is an example of how you can modify the
ClientIglu
class to store the key in an instance variable (note the backed property_read_only_key
!):
class Client:
def __init__(self, client_tag: str, ticket_id: str):
self.client_tag = client_tag
self.org_id = handlers.get_org_id(client_tag)
self.ticket_id = ticket_id
self.dev_server = f"{self.client_tag.replace('_', '-')}-dev1.net"
self.prod_server = f"{self.client_tag.replace('_', '-')}-prod1.net"
self._read_only_key = None
@property
def read_only_keys(self):
if not self._read_only_key:
self._read_only_key = self.get_keys(self, KeyType.READ_ONLY)
return self._read_only_key
- In this modified version of the class, the first time you access the
read_only_keys
property, theget_keys
method will be called withKeyType.READ_ONLY
as an argument and the result will be stored in the_read_only_key
instance variable. - Subsequent accesses to the
read_only_keys
property will return the value stored in the_read_only_key
instance variable without calling theget_keys
method again.