Looking at existing database designs, they typically only allow updating entire database entries, the dict in your case, rather than only a part of the entry.
An update function would typically look like:
natural_keys = ['name', 'age']
database: Dict[tuple, dict]: ...
def update(new_entry: dict):
keys = tuple(new_entry[k] for k in natural_keys)
if keys in database: # update existing entries
database[keys] = new_entry
else: # raise error if there are no entries with these natural keys
raise ValueError(f"there are no entries with natural keys {keys}")
This way you will never really change the natural keys, only updating existing entries with the same natural keys.
Of course, you will need an insert
function to insert new entries with new natural keys.
So there is never really the problem of preventing user from changing natural keys.
Alternatively
For detailed controls, you can create a class and control property setting via property setter
For example,
class MyDict:
def __init__(self, name, age, city):
self._name = name
self._age = age
self.city = city
@property
def name(self):
return self._name
@name.setter
def name(self, value):
raise ValueError("can't set name")
@property
def age(self):
return self._age
@age.setter
def age(self, value):
raise ValueError("can't set age")
So you can define
my_dict = MyDict(name='cameron', age=29, city='boston')
You can set city
my_dict.city = 'new york'
But you will get ValueError
when you try to set name
and age
.
In more advanced usage, you can use descriptors to handle general attribute setting
import logging
class PreventSetting:
def __set_name__(self, owner, name):
self.public_name = name
self.private_name = '_' + name
def __get__(self, obj, objtype=None):
value = getattr(obj, self.private_name)
logging.info('Accessing %r giving %r', self.public_name, value)
return value
def __set__(self, obj, value):
# if defining for 1st time
if not hasattr(obj, self.private_name):
logging.info('Updating %r to %r', self.public_name, value)
setattr(obj, self.private_name, value)
else:
# raise error for setting value after 1st time
raise ValueError(f"can't set {self.public_name}")
class MyDict:
name = PreventSetting() # First descriptor instance
age = PreventSetting() # Second descriptor instance
def __init__(self, name, age, city):
self.name = name # Calls the first descriptor __set__
self.age = age # Calls the second descriptor __set__
self.city = city