EDIT This code contains several bugs, see jsbueno's answer below for a correct version
I would like to create read-only attributes that dynamically retrieve values from an internal dictionary. I have tried to implement these as descriptors:
from typing import Any
class AttDesc:
def __init__(self, name):
self.name = name
def __get__(self, obj, objtype=None):
return obj._d[self.name]
def __set__(self, obj, value):
raise AtrributeError("Read only!")
class A:
def __init__(self, klist: list[str], vlist: list[Any]) -> None:
self._d = dict(zip(klist, vlist))
for k in self._d:
setattr(type(self), k, AttDesc(k))
@property
def d(self):
return self._d
The problem with this approach is that the descriptor instances are class attributes. This means that, in an interactive session:
a1 = A(['x', 'y'], [1, 2])
a2 = A(['z', ], [3])
if I press TAB for autocomplete on a1. I will be given the option to choose the attribute z, which "belongs" to instance a2. I have also tried to implement via the instance's __getattr__ method:
class B:
def __init__(self, klist: list[str], vlist: list[Any]):
object.__setattr__(self, '_d', dict(zip(klist, vlist)))
@property
def d(self):
return self._d
def __getattr__(self, name):
if name in self.d:
return self.d[name]
else:
object.__getattr__(name)
def __setattr__(self, k, v):
if k in self.d:
raise AttributeError("Read only!")
object.__setattr__(self, k, v)
If I try b = B(['w'], [3]) in an interactive session, pressing TAB on b. won't show w as an option, because it's not an instance attribute.
Pandas does something similar to what I want: it allows accessing the columns of a DataFrame with the dot operator, and only the appropriate columns for a given instance show up upon pressing TAB in an interactive session. I have tried to look into the Pandas code but it is a bit abstruse to me. I think they use something similar to my second __getattr__ option, but I don't understand how they make it work.
How could I implement this behaviour?
aandbinstances of the same class if they have different interfaces? This seems like a design problem that's independent of any IDE-specific features.Aa1anda2, andbis the instance ofB.a1anda2are instances of the same class if they have different interfaces.DataFrame. DifferentDataFrameinstances can have differently named columns. But they are instances of the same class. Maybe I could dynamically create a factory of classes derived from a common base and have attributes set according to the internal dictionary, but why would I do that if I have a much simpler option available?