Generates a property
You generate a property by calling the
property() built-in function, passing in three methods (getter, setter and deleter) as well as the docstring for the property.
If any argument is passed as None or omitted, that operation is not supported.
attrib = property(fget,fset,fdel,doc)
|fget||Optional||A function for getting an attribute value|
|fset||Optional||A function for setting an attribute value|
|fdel||Optional||A function for deleting an attribute|
|doc||Optional||The docstring of an attribute|
attrib = property(fget, fset, fdel, doc) line creates a new class attribute called attrib and defines the three methods as properties.
Now, when you reference
x.attrib, Python calls the fget method.
When you assign
x.attrib = value, Python calls the fset method and passes value as an argument.
When you execute
del x.attrib, Python calls the fdel method.
Python uses the argument you passed as doc as the docstring of the attribute.
When you do not specify the doc parameter, the getter method’s docstring becomes the docstring of the property.
Here is a simple example in which the Person class is defined.
This class has a single attribute named
hidden_name which we do not want people to access directly. Hence, three methods are defined in the class – a getter called
get_name(), a setter called
set_name() and a deleter called
class Person(): def __init__(self, value): self.hidden_name = value # getter function def get_name(self): print('Getting name:') return self.hidden_name # setter function def set_name(self, value): print('Setting name to', value) self.hidden_name = value # deleter function def del_name(self): print('Deleting name') del self.hidden_name # make a property name = property(get_name, set_name, del_name, doc='name of the person')
del_name() methods act like normal getter, setter and deleter until this line.
name = property(get_name, set_name, del_name, doc='name of the person')
It creates a new class attribute called ‘name’ and defines the three methods as its properties.
Now when you refer to the name attribute of any Person object, Python actually calls the
p = Person('Bob') print(p.name) # Prints Getting name: Bob
When you assign a value to the name attribute, the
set_name() method is called.
p.name = "Sam" # Prints Setting name to Sam print(p.name) # Prints Getting name: Sam
And when you try to delete the name attribute, the
del_name() method is called.
del p.name # Prints Deleting name
You can access the docstring through the
print(Person.name.__doc__) # Prints name of the person
Properties can also be a way to define computed attributes – attributes that are not actually stored, but are calculated dynamically on demand.
Let’s define a Rectangle class that has two normal attributes (width and height) and one computed attribute (area)
class Rectangle(object): def __init__(self, width, height): self.width = width self.height = height # computed attribute def area(self): return self.width * self.height # make a property area = property(area)
Let’s create a Rectangle object with an initial value for its width and height.
r = Rectangle(2, 5)
Now you can call the area as if it were an attribute:
print(r.area) # Prints 10
Here’s the fun part: you can change the width and height of the rectangle at any time, and the area property will be computed accordingly:
r.width = 3 r.height = 6 print(r.area) # Prints 18
As you can see we have not specified setter property for an attribute, we can’t set it from the outside. This is handy for read-only attributes:
r.area = 18 # Triggers AttributeError: can't set attribute
Equivalent Method – @property
A more elegant syntax to define properties in a class is to use property as a decorator
In the next example, we’ll define three different methods, each called
name() but preceded by different decorators:
- @property decorator goes before the getter method
- @name.setter decorator goes before the setter method
- @name.deleter decorator goes before the deleter method
Here’s how they actually look in the code:
class Person(): def __init__(self, value): self.hidden_name = value @property def name(self): print('Getting name:') return self.hidden_name @name.setter def name(self, value): print('Setting name to', value) self.hidden_name = value @name.deleter def name(self): print('Deleting name') del self.hidden_name
Here the first method is a getter, and establishes name as being a property. The other two methods attach setter and deleter to the name property.
You can still access name as if it were an attribute:
p = Person('Bob') # calls the getter print(p.name) # Prints Getting name: Bob # calls the setter p.name = 'Sam' # Prints Setting name to Sam # calls the deleter del p.name # Prints Deleting name
Please note that you cannot define @name.setter and @name.deleter decorators unless you already establish name as a property using @property decorator.