0

In python, I have a class, one of it's attributes is some other class that holds data, like this:

class A: class Params: def __init__(self): self.param1 = None self.param2 = None ... def __init__(self): self.parameters = self.Params() 

This way, if anyone wants to change one of the parameters in self.parameters he can write self.parameters.param1 = ....

I want to change the class Params that holds the parameters into a dictionary

class A: def __init__(self): self.parameters = { 'self.param1': None 'self.param2': None } 

This way if anyone wants to change anything he can write self.parameters['param1'] = ... instead. I've implemented this change (in a development branch) and it works. The problem is that other people use that code and if I make this change it will break their code (it's and simple fix, but still).

I wanted to know if there is any way I can make it so that if anyone writes in the old way (i.e. parameters.param1 = ...) it would still work but print a DEPRECATED message when he does, this way I can make a transition window where people go from the old way to the new way. The only way I came up with is if I change the name of the attribute I can keep the old code and add a log.warning, but I don't want to change the name of the attribute so if anyone can think of a way to do this it would be great :)

1 Answer 1

1

You can define make Params implement the mapping protocol, then produce deprecation warnings on attribute access.

class Params: def __init__(self): ... # As before def __setitem__(self, key, value): # This check is optional, to prevent creating new attributes if not hasattr(self, key): raise ValueError(f"No such parameter {key}") setattr(self, key, value) __getitem__ = getattr def __setattr__(self, attr, value): warnings.warn("Don't set attributes directly", DeprecationWarning) super().__setattr__(attr, value) def __getattribute__(self, attr): warnings.warn("Don't access attributes directly", DeprecationWarning) super().__getattribute__(self, attr) 

Now your next version will support both techniques, but attribute access will raise the warning. In the version after that, you can get rid of the Params class.

class A: def __init__(self): self.parameters = { 'param1': None 'param2': None } 
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the quick response!! I actually just realized a similar way to do it (yours is better though 😀) where the class inherits from dict and I use super to initialize the class.
I have another question (that I can get around but with a non-elegant way). hasstr calls __getattribute__ (I learned that the hard way :) so whenever I set attributes correctly (i.e. param['key'] = value) I get the warning to not access attributes directly. Because I know all the attributes, I can do without the hasstr but I wonder if you have a solution for that. Thanks! again!
I didn't revise this question as I should have. I used hasattr before I anticipated overriding __getattribute__ to provide a deprecation, and in hindsight I shouldn't be overriding __getattribute__. The only thing that comes to mind would be to define a descriptor whose __get__ and __set__ methods provide the deprecation warnings, instead of overriding __setattr__ and __getattribute__.
I managed to get it to work, I did need to modify your code in several places. I'm still really glad since I got to learn about these magic functions :)
Feel free to update this answer (or even post a separate one of your own); I made a small fix regarding the source of DeprecationWarning, but this was entirely untested.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.