You created an attribute that can't be deleted:
>>> f = Foo2('bar')
>>> f.prop
'bar'
>>> f.prop = 'spam'
>>> f.prop
'spam'
>>> del f.prop
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
Compare this to the Foo().prop
attribute, which can be deleted:
>>> f = Foo('bar')
>>> f.prop
'bar'
>>> del f.prop
>>> f.prop
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'prop'
Apart from that, or if you added a @prop.deleter
handler, there is not really any advantage to creating such a property. Delegating to a function what normal attribute access can do is not that useful.
Property setters or getters are much more useful when they do something in addition to just setting the attribute, like validation or transformation.
For example, the setter could enforce that values are always integers, including setting a default value if conversion to an integer fails:
@prop.setter
def prop(self, value):
try:
self._prop = int(prop)
except ValueError:
self._prop = 0
In other programming languages such as Java, you can't easily convert attributes to getters and setters (there is no equivalent concept to Python properties in Java) without having to re-write all access to those attributes everywhere in your code-base, so in those languages you often see advice to start out with getters and setters. This doesn't apply to Python, you can trivially turn existing attributes into properties without having to change any code using those attributes.