Using Single and Double Underscores in Python for Internal Methods, Attributes, and Special Dunder Methods
The article explains how to use single and double underscores in Python to define internal methods, attributes, and special dunder methods such as __init__, __str__, __bool__, __new__, __format__, __eq__, __del__, __copy__, and __deepcopy__, providing usage scenarios and code examples for each.
This guide demonstrates best practices for employing single and double underscores in Python class design, covering internal methods, internal attributes, and a range of special (dunder) methods.
Single underscore for internal methods : An internal method _handle_response processes HTTP responses within a client class, ensuring it is only called from inside the class.
import requests
class HttpClient:
def __init__(self, base_url):
self.base_url = base_url
def _handle_response(self, response):
if response.status_code != 200:
raise Exception(f"Request failed with status {response.status_code}")
return response.json()
def get(self, endpoint):
url = f"{self.base_url}/{endpoint}"
response = requests.get(url)
return self._handle_response(response)
client = HttpClient("https://api.example.com")
data = client.get("/resources")
print(data)Double underscore __init__ for initialization : The __init__ method sets up essential configuration such as base URLs and authentication tokens.
import requests
class TestAPI:
def __init__(self, base_url, auth_token):
self.base_url = base_url
self.auth_token = auth_token
def test_get_resources(self):
headers = {'Authorization': f'Bearer {self.auth_token}'}
response = requests.get(f"{self.base_url}/resources", headers=headers)
assert response.status_code == 200
test_api = TestAPI("https://api.example.com", "my-token")
test_api.test_get_resources()Double underscore __str__ for custom string representation : Provides a readable output for test result objects.
class TestResult:
def __init__(self, passed, message):
self.passed = passed
self.message = message
def __str__(self):
status = "Passed" if self.passed else "Failed"
return f"{status}: {self.message}"
result = TestResult(True, "All tests passed")
print(result)Double underscore __bool__ for boolean conversion : Allows instances to be used directly in boolean contexts.
class TestResult:
def __init__(self, passed, message):
self.passed = passed
self.message = message
def __bool__(self):
return self.passed
result = TestResult(True, "All tests passed")
assert bool(result), "Test should pass."Double underscore __new__ for controlling instantiation (singleton pattern) :
class SingletonTestFramework:
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(SingletonTestFramework, cls).__new__(cls)
return cls._instance
framework = SingletonTestFramework()
another_framework = SingletonTestFramework()
assert framework is another_frameworkDouble underscore __format__ for custom formatting :
class TestReport:
def __init__(self, name, result):
self.name = name
self.result = result
def __format__(self, format_spec):
if format_spec == "short":
return f"{self.name}: {self.result}"
return f"{self.name}: {self.result} (full report)"
report = TestReport("Test Name", "Passed")
print(f"{report:short}")Double underscore __eq__ for equality comparison :
class TestResult:
def __init__(self, passed, message):
self.passed = passed
self.message = message
def __eq__(self, other):
if isinstance(other, TestResult):
return self.passed == other.passed and self.message == other.message
return False
result1 = TestResult(True, "All tests passed")
result2 = TestResult(True, "All tests passed")
assert result1 == result2Double underscore __del__ for resource cleanup :
import requests
class TestFramework:
def __init__(self, session):
self.session = session
def __del__(self):
self.session.close()
session = requests.Session()
framework = TestFramework(session)
del frameworkDouble underscore __copy__ and __deepcopy__ for custom copy behavior :
import copy
class TestConfig:
def __init__(self, name, settings):
self.name = name
self.settings = settings
def __copy__(self):
return TestConfig(self.name, self.settings.copy())
def __deepcopy__(self, memo):
return TestConfig(self.name, copy.deepcopy(self.settings, memo))
config = TestConfig("Test Config", {"key": "value"})
shallow_copy = copy.copy(config)
deep_copy = copy.deepcopy(config)
config.settings["key"] = "changed"
assert config.settings["key"] == "changed"
assert shallow_copy.settings["key"] == "changed"
assert deep_copy.settings["key"] == "value"Each section includes usage notes emphasizing that internal methods and attributes should not be accessed from outside the class, that special methods must return appropriate types, and that edge cases such as authentication failures or resource‑release errors should be handled carefully.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.