1

I am trying to achieve to dynamically update some parameters in object. Let's have a property that I use in some dict ->

class Test(): def __init__(self): self._id = None self.some_dict = {"id": self.id} @property def id(self): return self._id @id.setter def id(self, new_id: int): self._id = new_id tst = Test() tst.id = 112 print(tst.id) # 112 print(tst.some_dict) # {'id': None} 

The desired result is to have:

print(tst.some_dict) # {'id': 112} 

How to update the dictionary after some parameter (id) as a property will be updated?

Edit: Sure thing I can do:

@id.setter def id(self, new_id: int): self._id = new_id self._some_dict.update({"id": self._id}) 

But for a lot of parameters it is kind of exhausting to write, is there any automatic way?

4
  • 2
    The dict won't implicitly update because some other variable updates. You either need to explicitly update the dict in the setter, or you make some_dict a @property as well and always create it on-the-fly. Commented Sep 1, 2021 at 7:39
  • 1
    "is there any automatic way?" Well, you can create the dict automatically when the user asks for it. You already know the tool to use for this - it's the same one you're using now to create the id value automatically when the user asks for it. Commented Sep 1, 2021 at 7:54
  • 1
    there is no self._some_dict in your code. (typo?) You are using getters and setters without doing anything special in them, simply use self.id instead - unless you really need a getter/setter for logic . python works just fine without it - and it is simpler/easier to understand Commented Sep 1, 2021 at 8:16
  • 1
    you could always forgoe the self._id = None in your class and instead pull/put all your values (via getter/setter) from the dict that you use as "generic" backingfield - but that makes not much sense as the class already has a dict that stores all its members ... Commented Sep 1, 2021 at 8:17

6 Answers 6

1

You could access the __dict__ attribute of your instance and return a sanitized version of this dict.

Example: (should work as is)

class A: _prohibited_keys = ["instance", "variables", "you", "dont", "want", "to", "show"] def __init__(self): self._id = None @property def id(self): return self._id @id.setter def id(self, value: int): self._id = value @property def dict(self): return {self._private_to_pub_key(k): v for k, v in self.__dict__.items() if k not in self._prohibited_keys and not k.startswith('__')} def _private_to_pub_key(self, k): return k[1:] if k.startswith('_') else k a = A() print(a.dict) >>> {'id': None} 
Sign up to request clarification or add additional context in comments.

2 Comments

no need for getters/setters at all if there is no logic inside the functions but beside the point - minor nitpick: if you happen to create own dunder methods in your class, your private_to_pub_key will remove one of the leading 2 underscores.
I actually use this method in my code: return sanitize(k): v for k, v in self.__dict__.items() if k not in self._prohibited_keys and not k.startswith('__')
1

You have to add line self.some_dict['id'] = new_id to the def id.

Comments

1
class Test(): def __init__(self): self._id = None self.some_dict = {"id": self.id} @property def id(self): return self._id @id.setter def id(self, new_id: int): self._id = new_id self.some_dict['id'] = new_id tst = Test() tst.id = 112 print(tst.id) # 112 print(tst.some_dict) # {'id': 112} 

2 Comments

Please provide additional details in your answer. As it's currently written, it's hard to understand your solution.
While this code may answer the question, it would be better to include some context, explaining how it works and when to use it. Code-only answers are not useful in the long run.
1

You can update it inside id.setter function:

 @id.setter def id(self, new_id: int): self._id = new_id self.some_dict.update({'id': new_id}) 

Comments

1

It's possible to automate things to some degree, as in the sense of eliminating a lot of repetitive coding. In the code below this is being done by utilizing a combination of a utility function that creates the properties along with a metaclass that post-processes the class definition and to initialize the dictionary you also want it to also have.

The latter involves searching for those that were defined and initializing both the instance storage attribute associated with their property name as well as the corresponding item in this additional dictionary. This requirement is what is making the creation of a class instance as complicated as it is, especially the need to have a metaclass — so you might want to reconsider whether that's really necessary or not.

def dict_property(name): """ Define property with given name that updates a private attribute and the instance's dictionary named some_dict. """ storage_name = '_' + name @property def prop(self): return getattr(self, storage_name) @prop.setter def prop(self, value): setattr(self, storage_name, value) self.some_dict.update({name: value}) return prop class MyMetaClass(type): def __new__(cls, name, bases, classdict, **kwargs): """ Initialize dict_property values in class and some_dict to None. """ names = [] for name, value in classdict.items(): if isinstance(value, property): #print(f'found property named {name!r}') names.append(name) some_dict = {} for name in names: storage_name = '_' + name classdict[storage_name] = None some_dict[name] = None classdict['some_dict'] = some_dict # Add dict to class. # Create the class. return type.__new__(cls, name, bases, classdict, **kwargs) class Test(metaclass=MyMetaClass): id = dict_property('id') age = dict_property('age') tst = Test() print(f'{tst.id=}') # -> tst.id=None print(f'{tst.age=}') # -> tst.age=None print(f'{tst.some_dict=}') # -> tst.some_dict={'id': None, 'age': None} print() tst.id = 112 print(f'{tst.id=}') # -> tst.id=112 print(f'{tst.age=}') # -> tst.age=None print(f'{tst.some_dict=}') # -> tst.some_dict={'id': 112, 'age': None} print() tst.age = 42 print(f'{tst.id=}') # -> tst.id=112 print(f'{tst.age=}') # -> tst.age=42 print(f'{tst.some_dict=}') # -> tst.some_dict={'id': 112, 'age': 42} 

Comments

0

Try @property decorator with some_dict:

class Test(): def __init__(self): self._id = None @property def id(self): return self._id @id.setter def id(self, new_id: int): self._id = new_id @property def some_dict(self): return {'id': self.id} tst = Test() tst.id = 112 print(tst.id) # 112 print(tst.some_dict) # {'id': 112} 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.