[python advanced] property function

Posted by phprocker on Wed, 21 Aug 2019 13:15:29 +0200

Articles Catalogue

prototype

class property(fget=None, fset=None, fdel=None, doc=None)

It returns an attribute.

For example:

class C:
    @property
    def x(self):
        return None

    print(type(x))

You can see that the result of printing is:

<class 'property'>

That is to say, the returned x is actually a class object, remember, it is not an instance of a class, it is itself a class, called property. This class has setter and deleter methods, which will be used below.

That is to say, property is actually a class. When initializing, the four parameters above can be given. The first three parameters are all parameters of the receiving function type. They are used to get the value of the attribute, set the value of the attribute, and delete the attribute respectively. The last doc is used to set the docstring of the attribute.

Look at the difference between it and class attributes:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")
    y = 1

c = C()
print(C.x)
print(C.y)
print(c.x)
print(c.y)

The result of printing is:

<property object at 0x000002789820F458>
1
None
1

That is to say, x is also a class attribute, but if you access it through instance C of C, you will access the corresponding getx setx delx and other methods.

Let's look at an example:

class C:
    def __init__(self):
        self._x = None

    def getx(self):
        print('getter')
        return self._x

    def setx(self, value):
        print('setter')
        self._x = value

    def delx(self):
        print('deleter')
        del self._x

    x = property(getx, setx, delx, "I'm the 'x' property.")


c = C()
print(c.x)
c.x = 1
print(c.x)
del c.x
# print(c.x) #Because del is used above, errors will be reported here

The final print result is:

getter
None
setter
getter
1
deleter

It's very similar to MyProperty below (if you're familiar with it). Descriptor The concept is well understood below.

class MyProperty:
    def __init__(self, getter, setter, deleter, docstr):
        self.getter = getter
        self.setter = setter
        self.deleter = deleter
        self.docstr = docstr

    def __get__(self, instance, owner):
        return self.getter(instance)

    def __set__(self, instance, value):
        self.setter(instance, value)


class C:
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x

    def setx(self, value):
        self._x = value

    def delx(self):
        del self._x

    x = MyProperty(getx, setx, delx, "I'm the 'x' property.")


c = C()
print(c.x)
c.x = 1
print(c.x)

Read-only property

If we want an attribute to be readable and not modified, we can use the following ways:

class C:
    def __init__(self):
        self._x = 1

    @property
    def x(self):
        return self._x


c = C()
print(c.x)
# c.x = 1 # Because it's set to read-only, modifications here will cause errors

The final result is:

1

Similarly, we can write a replacement (of course, many of the substitutions in this article, because they are examples, do not take into account all situations, only code the current situation used):

class MyProperty:
    def __init__(self, getter=None, setter=None, deleter=None, docstr=None):
        self.getter = getter
        self.setter = setter
        self.deleter = deleter
        self.docstr = docstr

    def __get__(self, instance, owner):
        return self.getter(instance)

    def __set__(self, instance, value):
        if not self.setter:
            raise AttributeError("can't set attribute")


class C:
    def __init__(self):
        self._x = 1

    @MyProperty
    def x(self):
        return self._x


c = C()
print(c.x)
c.x = 1

Result:

1

Finally, an AttributeError: can't set attribute exception is thrown.

More

After using @property as above, the attributes become read-only. If you want to become writable and deletable, you can use the following way:

class C:
    def __init__(self):
        self._x = 1

    @property
    def x(self):
        return self._x

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

    @x.deleter
    def x(self):
        del self._x


c = C()
print(c.x)
c.x = 2
print(c.x)
del c.x
# print(c.x) # Throw an exception here

Result:

1
2

Topics: Attribute