<w700 properties> attributes in Python

I am relatively new to Python, so I hope I haven't missed anything, but here goes ...

I am trying to write a Python module, and I would like to create a class with the "private" attribute, which can (or maybe should) be modified only through one or more functions in the module. This is done in order to make the module more reliable, since setting this attribute outside these functions can lead to undesirable behavior. For example, I could:

  • A class that stores x and y values ​​for a scatter plot, Data
  • Function to read x and y values ​​from a file and save them in a class, read()
  • Function for building them, plot()

In this case, I would prefer the user to not be able to do something like this:

 data = Data() read("file.csv", data) data.x = [0, 3, 2, 6, 1] plot(data) 

I understand that adding a single underscore to the name tells the user that the attribute should not be changed, i.e. rename to _x and add a property decorator so that the user can access the value without feeling guilty. However, if I wanted to add the setter property:

 class Data(object): _x = [] _y = [] @property def x(self): return self._x @x.setter def x(self, value): # Do something with value self._x = value 

Now I am in the same position as before - the user can no longer directly access the _x attribute, but he can still set it using:

 data.x = [0, 3, 2, 6, 1] 

Ideally, I would rename property function definitions to _x() , but this leads to confusion as to what self._x actually means (depending on the order in which they are declared, this apparently leads to that the calling setter is recursive or the setter is ignored in favor of the attribute).

A few solutions that I can think of:

  • Add a double tag to the __x attribute __x that the name becomes garbled and not confused with the setter function. As far as I understand, this should be reserved for attributes that the class does not want to share with possible subclasses, so I'm not sure if this is legal use.
  • Rename the attribute, for example. _x_stored . Although this completely solves the problem, it makes the code more difficult to read and contains problems with naming conventions - what attributes do I rename? only those that are relevant? only those that have properties? only those in this class?

Are any of the above solutions used? And if not, is there a better way to solve this problem?

Edit

Thanks for the answers so far. Some comments thrown by comments:

  • I want to preserve the additional logic that the setter property gives me - the # Do something with value section in the above example - so internal setting of the attribute via direct access self._x does not solve the problem.
  • Removing the setter property and creating a separate _set_x() function really solves the problem, but it is not a very neat solution, because it allows you to set _x two different ways - either by calling this function or by directly accessing self._x . Then I will need to keep track of which attributes should be set using my own (non-proprietary) setter function and which should be changed using direct access. I would prefer to use one of the solutions that I proposed above, because although they mess up the naming conventions in the class, they are at least consistent in their use outside the class, i.e. They all use syntactic sugar properties. If there is no way to do this in a more neat way, then I think I just need to choose the one that causes the least violation.
+7
source share
1 answer

If you want to refuse users to change the property, but want it to be clear that they can read it, I would use @property without providing a setter similar to what you described earlier:

 class Data(object): def __init__(self): self._x = [] self._y = [] @property def x(self): return self._x @property def y(self): return self._x 

I know that you mention β€œWhat if I want to add a setter to a property?”, But I think I would come across this as follows: why add an installer if you do not want your clients to set the property? Inside you can directly access self._x .

Regarding direct client access to _x or _y , any variable with the prefix '_' is understood to be "private" in Python, so you must trust your clients to comply. If they disobey this, and ultimately ruin everything, that their fault. This type of thinking is the opposite of many other languages ​​(C ++, Java, etc.), where private data storage is considered very important, but the Python culture is simply different in that regard.

Edit

One more note, since your private properties in this particular case are lists that are mutable (unlike strings or ints that are immutable), the client may end up changing them somewhat randomly:

 >>> d = Data() >>> print dx ['1', '2'] >>> l = dx >>> print l ['1', '2'] >>> l.append("3") >>> print dx ['1', '2', '3'] # Oops! 

If you want to avoid this, you will need your property to return a copy of the list:

 @property def x(self): return list(self._x) 
+3
source

All Articles