The Daily Shaarli
Today - 06/15/26
In Python, a closure is a function that captures local variables from its lexical environment, even when the function is called outside of that environment.
The closure retains a reference to the parent function's variables, not their values at any given time
If the parent variable is modified after the closure is created, the closure will see the new value
def add_n(n):
def f(x):
return x + n
return f
add_10 = add_n(10)
print(add_10(100))
>>> 110
print('closure len', add_10.__closure__.__len__())
>>> 1
print('closure content', add_10.__closure__[0].cell_contents)
>>> 10
usefulness
Decorators: Closures are often used to create decorators
Function factories: Create functions with predefined behaviors
Context memory: Preserve state between function calls
modified closure value
nonlocal
def counter_call(f):
counter = 0
def wrapper(*args, **kargs):
nonlocal counter # set counter is non local attribute but free variable
counter += 1
r = f(*args, **kargs)
print(f"{f.__name__} called {counter} times")
return r
return wrapper
@counter_call
def poly(n):
return sum(i**5 for i in range(n))
function attribute
def counter_call(f):
def wrapper(*args, **kargs):
wrapper.counter += 1
r = f(*args, **kargs)
print(f"{f.__name__}() called {wrapper.counter} times")
return r
wrapper.counter = 0 # evaluate before wrapper calling
return wrapper
@counter_call
def poly(n):
return sum(i**5 for i in range(n))
r = poly(10)
>>> poly() called 1 times
print(poly(10))
>>> poly() called 2 times
print(poly.counter) # poly = wrapper(poly) et wrapper has attribute counter
>>> 2getattribute and setattr
'getattribute' and 'setattr' are two special methods (dunder methods) used to control access to a class's attributes. Here's a clear explanation of their role and differences:
getattribute(self, name)
Role: Called every time an attribute is accessed (read), even if the attribute doesn't exist
Default behavior: Searches for the attribute in the instance, then in the class, and then in the parent classes
usefulness
- Intercept access to an attribute to add logic (e.g., logging, validation)
- Raise an exception if the attribute is not allowed
Caution
If you override 'getattribute', you must call 'super().getattribute(name)' to avoid blocking access to internal attributes
setattr(self, name, value)
Role: Called whenever an attribute is modified (written)
Default behavior: Stores the value in the instance
usefulness
- Validate or modify the value before assigning it
- Prevent modification of certain attributes
- Synchronize attributes with other data
Caution
If you override 'setattr', you must use 'self.dict[name] = value' to avoid infinite recursion
example
class Temperature:
def __get__(self, obj, objtype):
print("desc __get__")
return obj._temperature
def __set__(self, obj, value):
print(f"desc __set__ {value}")
obj._temperature = value
class Maison:
def __init__(self, temperature):
self.temperature = temperature
def __getattribute__(self, attr):
print(f"__getattribute__ : {attr}")
return object.__getattribute__(self, attr)
def __setattr__(self, attr, value):
print(f"__setatt__ : {attr} - {value}")
return object.__setattr__(self, attr, value)
temperature = Temperature()
m = Maison(18)
__setatt__ : temperature - 18
desc __set__ 18
__setatt__ : _temperature - 18
m.temperature
__getattribute__ : temperature
desc __get__
__getattribute__ : _temperature
m.temperature = 22
__setatt__ : temperature - 22
desc __set__ 22
__setatt__ : _temperature - 22
m.x = 10
__setatt__ : x - 10
m.x
__getattribute__ : x
m.y
__getattribute__ : y
__getattribute__ : __dir__
__getattribute__ : __dict__
__getattribute__ : __class__
AttributeError: 'Maison' object has no attribute 'y'
getattr(self, name)
'getattr' is a special method (or dunder method) that is called when an attribute is not found in a class instance using the standard methods (getattribute, hasattr, etc.)
It allows you to dynamically manage access to missing attributes
usefulness
-
Creating attributes on the fly
-
Implementing default behaviors for undefined attributes
-
Proxies or wrappers: To redirect attribute calls to another object
-
Dynamic attributes: To generate values based on the attribute name
-
Compatibility with external APIs: To manage attributes that do not yet exist in a class
example
class Redirector:
def __init__(self, id):
self.id = id
def __repr__(self):
return f"Redirector2({self.id})"
def __getattr__(self, name):
def forwarder(arg):
return f"{self.id} -> {name}({arg})"
return forwarder
R = Redirector(2)
print(R)
print(R.bar(20))metaclass
A metaclass is a class that defines the behavior of other classes. By default, in Python, classes are instances of 'type', which is the default metaclass. However, you can create your own metaclasses to control class creation, add attributes, or modify class behavior.
usefulness
- Control class creation: Add attributes, validate properties, or modify the class before it is finalized
- Singleton: Ensure that a class has only one instance
- Automatic validation: Verify that certain methods or attributes exist in the classes that use them
- Automatic registration: Register classes in a registry (for example, for a plugin system)
new
- Role: 'new' is responsible for memory allocation and instance creation
- Signature: new(cls, name, bases, namespace)
- Return: It must return a new instance of the class (or another class if necessary)
usefulness
- To create singletons (a single instance of a class)
- To modify the type of the returned instance (for example, to return a subclass)
- To control object creation (e.g., validation before creation)
class LowerAttrType(type):
def __new__(cls, name, bases, namespace, **kwds):
namespace = {(n.lower() if not n.startswith('__') else n): obj for n, obj in namespace.items()}
bases = (BaseOfAll,)
return type.__new__(cls, name, bases, namespace, **kwds)
class BaseOfAll:
def common_func(self):
return f"in common_func"
class C(metaclass=LowerAttrType):
def fUnc_bAd_CAP(selfself):
return f"in fUnc_bAd_CAP"
c = C()
print('func_bad_cap', 'func_bad_cap' in c.__dir__())
>>> True
print('fUnc_bAd_CAP', 'fUnc_bAd_CAP' in c.__dir__())
>>> False
print('c.common_func', c.common_func())
>>> c.common_func in common_func
print('c.func_bad_cap', c.func_bad_cap())
>>> c.func_bad_cap in fUnc_bAd_CAPproperty
'@property' decorator allows you to define a method as a property of a class. This means you can access that method as if it were an attribute, without using parentheses to call a method.
- No parentheses: object.property (not object.property()).
- Convention: Use _<name> for the "private" attribute (e.g., _name).
- Validation: The setter can include checks.
- Compatibility: Works with inheritance and abstract classes.
example
class Circle:
def __init__(self, radius):
self._radius = radius
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value <= 0:
raise ValueError("The radius must be positive")
self._radius = value
@radius.deleter
def radius(self):
print("Deleting the radius")
del self._radius
c = Circle(5)
print(c.radius)
>>> 5
c.radius = 10
del c.radius
descriptor
A Python descriptor is an object that implements at least one of the following special methods:
__get__(self, obj, objtype=None)
__set__(self, obj, value)
__delete__(self, obj)
These methods allow you to customize access to, modification of, or deletion of an attribute of a class instance
usefulness
- Data validation: Check or transform values before assigning them
- Dynamic calculations: Generate a value on the fly (e.g., @property properties)
- Data sharing: Manage common attributes between multiple instances
examples
class DescriptorTemperature:
TEMPMAX = 20
def __get__(self, obj, objtype=None):
print('__get__')
return obj._temperature
def __set__(self, obj, value):
print('__set__')
if value > __class__.TEMPMAX:
raise ValueError(f"Temperature is too hot:{value}, max:{__class__.TEMPMAX} ")
obj._temperature = value
class Home:
def __init__(self, temperature):
self.temperature = temperature
temperature = DescriptorTemperature()
h = Home(18)
print(h.temperature)
h.temperature = 20
h.temperature = 30