8: Inside Class¶
Summary¶
__new__ is automatically called to create the instance
__init__ is called after __new__ to add instance attributes to the new instance
__new__ generally works by calling the superclass __new__ to create an instance, but you can use any mechanism that creates an object
As long as the object is of the same type as the class or is subtype the __init__ method is called
One standard example of how __new__ can be used is to create a singleton, a class that can only be initialized once
Another use of __new__ is to customize immutable data objects
- Adding a custom __call__ method can be used to convert any object into a callable
This is how class is converted to a callable
Program¶
"""
Demonstration of:
- __new__
- __init__
- Singleton pattern using __new__
- Custom immutable object using __new__
- Custom __call__ method
"""
# --------------------------------------------------
# 1. Basic Example Showing __new__ and __init__
# --------------------------------------------------
class BasicExample:
def __new__(cls, *args, **kwargs):
print("__new__ called: Creating instance")
instance = super().__new__(cls) # Call superclass __new__
return instance
def __init__(self, name):
print("__init__ called: Initializing instance")
self.name = name
print("---- Basic Example ----")
obj1 = BasicExample("Brayden")
print("Name:", obj1.name)
print()
# --------------------------------------------------
# 2. Singleton Pattern Using __new__
# --------------------------------------------------
class Singleton:
_instance = None
def __new__(cls, value):
if cls._instance is None:
print("Creating new Singleton instance")
cls._instance = super().__new__(cls)
else:
print("Using existing Singleton instance")
return cls._instance
def __init__(self, value):
# This still runs every time unless guarded
self.value = value
print("---- Singleton Example ----")
s1 = Singleton(10)
print("Value:", s1.value)
s2 = Singleton(20)
print("Value:", s2.value)
print("Are s1 and s2 the same object?", s1 is s2)
print()
# --------------------------------------------------
# 3. Custom Immutable Object Using __new__
# --------------------------------------------------
class PositiveInt(int):
def __new__(cls, value):
print("Customizing immutable int")
if value < 0:
value = 0 # Force non-negative
return super().__new__(cls, value)
print("---- Immutable Customization Example ----")
num1 = PositiveInt(5)
num2 = PositiveInt(-10)
print("num1:", num1)
print("num2 (negative converted to 0):", num2)
print()
# --------------------------------------------------
# 4. Making an Object Callable with __call__
# --------------------------------------------------
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, value):
return value * self.factor
print("---- __call__ Example ----")
double = Multiplier(2)
print("Calling object like a function:")
print("double(5) =", double(5))
print("double(10) =", double(10))
Program Output¶
(docs-env) root@BMitchellLTOP:~/git/sphinx_students/source/programming_lang/book/programs# python3 chapterEight.py
---- Basic Example ----
__new__ called: Creating instance
__init__ called: Initializing instance
Name: Brayden
---- Singleton Example ----
Creating new Singleton instance
Value: 10
Using existing Singleton instance
Value: 20
Are s1 and s2 the same object? True
---- Immutable Customization Example ----
Customizing immutable int
Customizing immutable int
num1: 5
num2 (negative converted to 0): 0
---- __call__ Example ----
Calling object like a function:
double(5) = 10
double(10) = 20