Specialized @property decorators in python

I have several classes, each of which has several attributes. What all attributes have in common is that they must be numeric properties. This seems to be the perfect place to use python decorators, but I can't imagine what the correct implementation is. Here is a simple example:

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")

It seems to me that it should be possible to write a decorator that does everything so that the above can be converted to:

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

Thus, the new decorator numeric_propertycan be used in many classes.

+4
source share
3 answers

A @property Python , , , . :

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

:

>>> j = Junk('hi')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  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'
>>> j = Junk(1)
>>> del j.var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jonrsharpe/test.py", line 21, in __delete__
    raise RuntimeError("You can't delete {}".format(self.attr))
RuntimeError: You can't delete _var
>>> j.var = 'hello'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  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'
>>> j.var = 2
>>> j.var
2
+2

1: property

property . . Descriptor HowTo python.org.

, property .

, int setter:

class numeric_property(property):
    def __set__(self, obj, value):
        assert isinstance(value, int), "numeric_property requires an int"
        super(numeric_property, self).__set__(obj, value)

class A(object):
    @numeric_property
    def x(self):
        return self._x

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

:

>>> a = A()
>>> a.x = 'aaa'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __set__
AssertionError: numeric_property requires an int

2.

, , , .

:

class A(object):
    x = numeric_property('_x')

, :

class numeric_property(object):
    def __init__(self, private_attribute_name, default=0):
        self.private_attribute_name = private_attribute_name
        self.default = default

    def __get__(self, obj, typ):
        if not obj: return self
        return getattr(obj, self.private_attribute_name, self.default)

    def __set__(self, obj, value):
        assert isinstance(value, int), "numeric_property requires an int"
        setattr(obj, self.private_attribute_name, value)

:)

Pyhon, Python .

+1

You can simply create a function that does this for you. Not as simple as possible, there is no need to create your own descriptor:

def numprop(name, privname):
    @property
    def _numprop(self):
        return getattr(self, privname)

    @_numprop.setter
    def _numprop(self, value):
        if not isinstance(value, int):
            raise ValueError("{name} must be an integer, {name} = {}".format(value, name=name))
        setattr(self, privname, value)

    @_numprop.deleter
    def _numprop(self):
        raise RuntimeError("You can't delete var")
    return _numprop


class Junk(object):
    def __init__(self, var):
        self._var = var
    var = numprop("var", "_var")
0
source

Source: https://habr.com/ru/post/1654857/


All Articles