Specialized @property decorators in python
<p>I have a few classes each of which has a number of attributes. What all of the attributes have in common is that they should be numeric properties. This seems to be an ideal place to use python's decorators, but I can't seem to wrap my mind around what the correct implementation would be. Here is a simple example:</p>

<pre><code>class Junk(object):
    def __init__(self, var):
        self._var = var

    @property
    def var(self):
        """A numeric variable"""
        return self._var

    @var.setter
    def size(self, value):
        # need to make sure var is an integer
        if not isinstance(value, int):
            raise ValueError("var must be an integer, var = {}".format(value))
        self._var = value

    @var.deleter
    def size(self):
        raise RuntimeError("You can't delete var")
</code></pre>

<p>It seems to me that it should be possible to write a decorator that does everything so that the above can be transformed into:</p>

<pre><code>def numeric_property(*args, **kwargs):
    ...

class Junk(object):
    def __init__(self, var):
        self._var = var

    @numeric_property
    def var(self):
        """A numeric variable"""
        return self._var
</code></pre>

<p>That way the new <code>numeric_property</code> decorator can be used in many classes.</p>

<p>A <code>@property</code> is just a special case of Python's <a href="https://docs.python.org/3/howto/descriptor.html" rel="nofollow">descriptor protocol</a>, so you can certainly build your own custom versions. For your case:</p>

<pre><code>class NumericProperty:
    """A property that must be numeric.

    Args:
      attr (str): The name of the backing attribute.

    """

    def __init__(self, attr):
        self.attr = attr

    def __get__(self, obj, type=None):
        return getattr(obj, self.attr)

    def __set__(self, obj, value):
        if not isinstance(value, int):
            raise ValueError("{} must be an integer, var = {!r}".format(self.attr, value))
        setattr(obj, self.attr, value)

    def __delete__(self, obj):
        raise RuntimeError("You can't delete {}".format(self.attr))

class Junk:

    var = NumericProperty('_var')

    def __init__(self, var):
        self.var = var
</code></pre>

<p>In use:</p>

<pre class="lang-none prettyprint-override"><code>&gt;&gt;&gt; j = Junk('hi')
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/Users/jonrsharpe/test.py", line 29, in __init__
    self.var = var
  File "/Users/jonrsharpe/test.py", line 17, in __set__
    raise ValueError("{} must be an integer, var = {!r}".format(self.attr, value))
ValueError: _var must be an integer, var = 'hi'
&gt;&gt;&gt; j = Junk(1)
&gt;&gt;&gt; del j.var
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/Users/jonrsharpe/test.py", line 21, in __delete__
    raise RuntimeError("You can't delete {}".format(self.attr))
RuntimeError: You can't delete _var
&gt;&gt;&gt; j.var = 'hello'
Traceback (most recent call last):
  File "&lt;stdin&gt;", line 1, in &lt;module&gt;
  File "/Users/jonrsharpe/test.py", line 17, in __set__
    raise ValueError("{} must be an integer, var = {!r}".format(self.attr, value))
ValueError: _var must be an integer, var = 'hello'
&gt;&gt;&gt; j.var = 2
&gt;&gt;&gt; j.var
2
</code></pre>

python python-3.x decorator python-decorators