How To Call Deleter With Decorator

Hi,

I am working out a textbook problem. I was able to implement the
solution. When testing the solution however, I am unable to determine how to call the ‘deleter’ method being that it has the same form as the the original method with the same name and with the ‘property’ decorator.

Please refer to the 2nd to last line of code below.

class Numbers:

    MULTIPLIER = 5            # Class variable

    def __init__(self, x, y): # Constructor/initializer
        self.x = x
        self.y = y     

    def add(self):
        return self.x + self.y

    @classmethod
    def multiply(cls, a):
        return a * cls.MULTIPLIER
        
    @staticmethod
    def substract(b, c):
        return b - c

    @property
    def value(self):
        return (self.x, self.y)

    @value.setter
    def value(self, xy_tuple):
        self.x, self.y = xy_tuple

    @value.deleter
    def value(self):
        del self.x
        del self.y

    
solveNumbers = Numbers(45, 56)   # Instantiation (create object)
print(solveNumbers.multiply(10))
print(solveNumbers.substract(85, 56))
print(solveNumbers.value)        # Print x, y

solveNumbers.value = (36,12)     # Update x, y values
print(solveNumbers.value)        # Print new x, y values

solveNumbers.value               # How to call method deleter correctly?
print(solveNumbers.value)        # Print to confirm deletion

The deleter is called when you have a Numbers instance numb and then call:

del numb.value

or

delattr(numb, "value")

or

numb.__delattr__("value")

After calling this you can verify that the attributes where indeed removed by trying to access numb.value (which should then raise an AttributeError).

Hello,

thank you for your response. I tested all three suggestions. After executing each line, I received no error(s). I only received an error once I attempted to re-print the values (accessing the values x, y).
I was a little confused. I thought that I was merely deleting the values that were initially assigned to self.x and self.y. From the errors that I received when I attempted to re-print the values, I have come to the conclusion that this method is actually deleting the variables and not just the contents.

Is there a practical application to this or is this more of an academic exercise?

You are quite right - This call deletes the internal x and y attributes. And after doing that, trying to access either one of those (or trying to access numb.value) will all raise an exception, untill a new value is again set. The code as-is (basically identical to the one in the Python docs) is not very practical and only illustrates the way those properties can be defined. In “real” code you usually do not need a “deleter”, and if you provide one, then it would be better to also everywhere add checks to verify that x and y are still valid attributes.

One possible reason for adding a special deleter might be if you have some kind of huge internal object and you want to try to release its memory, but even then I don’t think it’s worth the extra trouble of adding it (also the del may still not be able to actually release the underlying memory, since it’s outside of Python’s control exactly when that happens).

Yet another reason is if you want to do some special actions - if you need extra cleanup steps (close files, close a database connection, release some other resource handle - but this assumes some tight link between that “value” and those resources.

2 Likes

Ok, got it. Thank you for the detailed explanation. This really helps in understanding the subtle nuances when developing code (and not just adhering to proper syntax).

Much appreciated!

1 Like

This thread is a little disturbing for me to read, because it implies some unfortunate things about the tutorials that are out there. Every possible way that you should have been able to find out that there is such a thing as a .deleter for a property, in the first place, should have explained this to you clearly at the same time.

Hi,

this was the only line of text describing the ‘deleter’ method in the textbook:

“a deleter will delete the attribute from our object”

Afterwards, the textbook did not elaborate further other than give the example
that I provided above. I might have misinterpreted the above statement meaning
that it would basically clear the results from the class attributes as you would
‘normally’ clear or reset a variable. I have never come across a situation where
you actually delete a variable (I am coming from an embedded C procedural background).
This is why it would have seemed a bit odd to me to delete a variable
from the code.

But you are right that textbooks can be improved. One of the books that I was
reading went on about the topic of inheritance. They provided a few examples on
the subject matter. They then followed it up by providing a relatively involved example
(by way of a game of blackjack) not using inheritance but rather composition! No theory was
ever discussed on the subject of composition - in the entire book.

Well, the ‘del’ operation is (not the same of course, but) kind of similar to calling a C++ destructor on a class instance. Python’s extremely dynamic nature makes it possible to “delete” attributes of a class instance:

class X:
     def __init__(self, x):
         self._x = x
>>> x = X(3)
>>> del x._x  # poof
>>> x._x # will now raise AttributeError

I always kind of disliked this particular feature in regular Python classes. It’s very unlike classes in Java or C++ and from a developer’s point of view (using a class) it make the class less “reliable” since you don’t actually know which attributes x will actually have at any point in time.

But this also suggests one nice other usage of a deleter property, namely to prevent attributes from being deleted:

# in the class X definition:
    @value.deleter
    def value(self):
          raise AttributeError("Attribute 'value' can not be deleted")

After adding this, any call to del x.value will raise that exception, so you can now (in principle) be a bit more sure that the underlying attributes are always there.
The setter property is sometimes defined in a similar way to make attributes read-only.

1 Like

Ok, here I am a little confused. I thought that a setter was meant for just that, to set, or change an attribute value. The ‘getter’ is to read an attribute. To me, ‘read-only’ implies no change.

Nevermind, I think that I understand your statement. You can write the code such that it
would raise an exception should there be a change to the value of the attribute.

You’re right that normally a setter is used to modify an attribute, but it can also be used to prevent any modification:

@value.setter
def value(self, new_value):
     self._x = new_value

The above makes it possible to reset the “value” property to new_value. But

@value.setter
def value(self, new_value):
     raise AttributeError("Sorry, 'value' is read-only")

makes it impossible to call x.value = 3 (etc) without raising an exception. So, the value is read-only only in this case. This is a pretty important use case.

1 Like

That is indeed very concerning. Could I know the title of this textbook, so that I can warn others against using it in the future?

It’s not necessary to do that. Given

class Example:
    def __init__(self):
        self._implementation = 'example value'
    @property
    def value(self):
        return self._implementation

without a setter, the property logic will already prevent setting:

>>> e = Example()
>>> e.value = 'something else'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Writing the setter does allow for customizing the exception that is raised, of course.

1 Like

The name of the book is actually from python:
python-textbok-readthedocs-io-en-1.0.pdf
(published 2017)

However, as a rookie to python, I think that it is a good book overall since it covers a lot of general
topics - rather comprehensive but general. Good as a reference. So, I don’t want to knock them
for a small item that they did not elaborate on especially for something that as commented above
‘is not very practical’.

fyi, I also attempted to run the example on page 151, but I kept getting a run time exception.
I tried debugging it but with my limited experience and understanding, I could not figure out the
issue.

Is it this?

https://python-textbok.readthedocs.io/en/1.0/

That’s not official documentation or anything, anyone can publish whatever they like.

The trouble is, if the book is inaccurate on the one point that you’ve checked with someone, how do you know whether its other advice is valuable?

I flipped through a few other pages, and at best, the advice is quite old. Sometimes that’s not a major problem (for example, the super() calls are all shown with two arguments, but the zero-argument super() is by far the best way to do things in nearly all cases), but other times, it means that examples straight up won’t work (like print being used as a statement instead of a function).

Yes, this is it.

Regarding the super() call, yes, I have seen it without arguments/parameters passed in in other tutorials/books. The good thing is that it forced me to do a little research on the subject and what I learned is that if you include arguments/parameters, they will override the parameters in the base class - assuming that is in fact what you want to do.

I do understand however, that the init method in the derived class overrides the base class
init method. So, as you stated the parameters in the super() call might not be explicitly necessary since the init method takes care of this.