{"id":164,"date":"2024-01-25T14:10:22","date_gmt":"2024-01-25T14:10:22","guid":{"rendered":"https:\/\/learnpython.elegantwallp.com\/?p=164"},"modified":"2024-01-25T14:10:23","modified_gmt":"2024-01-25T14:10:23","slug":"python-property","status":"publish","type":"post","link":"https:\/\/learnpython.elegantwallp.com\/2024\/01\/25\/python-property\/","title":{"rendered":"Python Property"},"content":{"rendered":"\n<p><strong>Summary<\/strong>: in this tutorial, you\u2019ll learn about the Python&nbsp;<code>property<\/code>&nbsp;class and how to use it to define properties for a class.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction to class properties<\/h2>\n\n\n\n<p>The following defines a\u00a0<code>Person<\/code>\u00a0class\u00a0that has two\u00a0attributes\u00a0<code>name<\/code>\u00a0and\u00a0<code>age<\/code>, and create a new instance of the\u00a0<code>Person<\/code>\u00a0class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>class Person: def __init__(self, name, age): self.name = name self.age = age john = Person('John', 18)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Since\u00a0<code>age<\/code>\u00a0is the\u00a0instance attribute\u00a0of the\u00a0<code>Person<\/code>\u00a0class, you can assign it a new value like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john.age = 19<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The following assignment is also technically valid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john.age = -1<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>However, the age is semantically incorrect.<\/p>\n\n\n\n<p>To ensure that the age is not zero or negative, you use the\u00a0<code><a href=\"https:\/\/www.pythontutorial.net\/python-basics\/python-if\/\">if<\/a><\/code>\u00a0statement to add a check as follows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>age = -1 if age &lt;= 0: raise ValueError('The age must be positive') else: john.age = age<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>And you need to do this every time you want to assign a value to the&nbsp;<code>age<\/code>&nbsp;attribute. This is repetitive and difficult to maintain.<\/p>\n\n\n\n<p>To avoid this repetition, you can define a pair of methods called getter and setter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getter and setter<\/h2>\n\n\n\n<p>The getter and setter methods provide an interface for accessing an instance attribute:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The getter returns the value of an attribute<\/li>\n\n\n\n<li>The setter sets a new value for an attribute<\/li>\n<\/ul>\n\n\n\n<p>In our example, you can make the\u00a0<code>age<\/code>\u00a0attribute\u00a0private\u00a0(by convention) and define a getter and a setter to manipulate the\u00a0<code>age<\/code>\u00a0attribute.<\/p>\n\n\n\n<p>The following shows the new\u00a0<code>Person<\/code>\u00a0class with a getter and setter for the\u00a0<code>age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>class Person: def __init__(self, name, age): self.name = name self.set_age(age) def set_age(self, age): if age &lt;= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>How it works.<\/p>\n\n\n\n<p>In the&nbsp;<code>Person<\/code>&nbsp;class, the&nbsp;<code>set_age()<\/code>&nbsp;is the setter and the&nbsp;<code>get_age()<\/code>&nbsp;is the getter. By convention the getter and setter have the following name:&nbsp;<code>get_&lt;attribute&gt;()<\/code>&nbsp;and&nbsp;<code>set_&lt;attribute&gt;()<\/code>.<\/p>\n\n\n\n<p>In the\u00a0<code>set_age()<\/code>\u00a0method, we raise a\u00a0<code>ValueError<\/code>\u00a0if the\u00a0<code>age<\/code>\u00a0is less than or equal to zero. Otherwise, we assign the\u00a0<code>age<\/code>\u00a0argument to the\u00a0<code>_age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def set_age(self, age): if age &lt;= 0: raise ValueError('The age must be positive') self._age = age<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The\u00a0<code>get_age()<\/code>\u00a0method returns the value of the\u00a0<code>_age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def get_age(self): return self._age<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>In the\u00a0<code>__init__()<\/code>\u00a0method, we call the\u00a0<code>set_age()<\/code>\u00a0setter method to initialize the\u00a0<code>_age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>def __init__(self, name, age): self.name = name self.set_age(age)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The following attempts to assign an invalid value to the\u00a0<code>age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john = Person('John', 18) john.set_age(-19)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>And Python issued a\u00a0<code>ValueError<\/code>\u00a0as expected.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>ValueError: The age must be positive<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>This code works just fine. But it has a backward compatibility issue.<\/p>\n\n\n\n<p>Suppose you released the&nbsp;<code>Person<\/code>&nbsp;class for a while and other developers have been already using it. And now you add the getter and setter, all the code that uses the Person won\u2019t work anymore.<\/p>\n\n\n\n<p>To define a getter and setter method while achieving backward compatibility, you can use the&nbsp;<code>property()<\/code>&nbsp;class.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Python property class<\/h2>\n\n\n\n<p>The property class returns a\u00a0<code>property<\/code>\u00a0object. The\u00a0<code>property()<\/code>\u00a0class has the following syntax:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>property(fget=None, fset=None, fdel=None, doc=None)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>property()<\/code>&nbsp;has the following parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>fget<\/code>&nbsp;is a function to get the value of the attribute, or the getter method.<\/li>\n\n\n\n<li><code>fset<\/code>&nbsp;is a function to set the value of the attribute, or the setter method.<\/li>\n\n\n\n<li><code>fdel<\/code>&nbsp;is a function to delete the attribute.<\/li>\n\n\n\n<li><code>doc<\/code>&nbsp;is a docstring i.e., a comment.<\/li>\n<\/ul>\n\n\n\n<p>The following uses the\u00a0<code>property()<\/code>\u00a0function to define the\u00a0<code>age<\/code>\u00a0property for the\u00a0<code>Person<\/code>\u00a0class.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>class Person: def __init__(self, name, age): self.name = name self.age = age def set_age(self, age): if age &lt;= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age age = property(fget=get_age, fset=set_age)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>In the\u00a0<code>Person<\/code>\u00a0class, we create a new property object by calling the\u00a0<code>property()<\/code>\u00a0and assign the property object to the age attribute. Note that the\u00a0<code>age<\/code>\u00a0is a\u00a0class attribute, not an\u00a0instance attribute.<\/p>\n\n\n\n<p>The following shows that the&nbsp;<code>Person.age<\/code>&nbsp;is a&nbsp;<code>property<\/code>&nbsp;object:<code>print(Person.age)<\/code><small>Code language: Python (python)<\/small><\/p>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>&lt;property object at 0x000001F5F5149180><\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The following creates a new instance of the\u00a0<code>Person<\/code>\u00a0class and access the\u00a0<code>age<\/code>\u00a0attribute:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john = Person('John', 18)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>The\u00a0<code>john.__dict__<\/code>\u00a0stores the instance attributes of the john object. The following shows the contents of the\u00a0<code>john.__dict__<\/code>\u00a0:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>print(john.__dict__)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>{'_age': 18, 'name': 'John'}<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>As you can see clearly from the output, the&nbsp;<code>john.__dict__<\/code>&nbsp;doesn\u2019t have the&nbsp;<code>age<\/code>&nbsp;attribute.<\/p>\n\n\n\n<p>The following assigns a value to the\u00a0<code>age<\/code>\u00a0attribute of the john object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john.age = 19<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>In this case, Python looks up the&nbsp;<code>age<\/code>&nbsp;attribute in the&nbsp;<code>john.__dict__<\/code>&nbsp;first. Because Python doesn\u2019t find the&nbsp;<code>age<\/code>&nbsp;attribute in the&nbsp;<code>john.__dict__<\/code>, it\u2019ll then find the&nbsp;<code>age<\/code>&nbsp;attribute in the&nbsp;<code>Person.__dict__<\/code>.<\/p>\n\n\n\n<p>The\u00a0<code>Person.__dict__<\/code>\u00a0stores the class attributes of the Person class. The following shows the contents of the\u00a0<code>Person.__dict__<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>pprint(Person.__dict__)<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>mappingproxy({'__dict__': &lt;attribute '__dict__' of 'Person' objects>, '__doc__': None, '__init__': &lt;function Person.__init__ at 0x000002242F5B2670>, '__module__': '__main__', '__weakref__': &lt;attribute '__weakref__' of 'Person' objects>, 'age': &lt;property object at 0x000002242EE39180>, 'get_age': &lt;function Person.get_age at 0x000002242F5B2790>, 'set_age': &lt;function Person.set_age at 0x000002242F5B2700>})<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Because Python finds the&nbsp;<code>age<\/code>&nbsp;attribute in the&nbsp;<code>Person.__dict__<\/code>, it\u2019ll call the&nbsp;<code>age<\/code>&nbsp;property object.<\/p>\n\n\n\n<p>When you assign a value to the\u00a0<code>age<\/code>\u00a0object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>john.age = 19<\/code><small>Code language: Python (python)<\/small><\/code><\/pre>\n\n\n\n<p>Python will call the function assigned to the&nbsp;<code>fset<\/code>&nbsp;argument, which is the&nbsp;<code>set_age()<\/code>.<\/p>\n\n\n\n<p>Similarly, when you read from the&nbsp;<code>age<\/code>&nbsp;property object, Python will execute the function assigned to the&nbsp;<code>fget<\/code>&nbsp;argument, which is the&nbsp;<code>get_age()<\/code>&nbsp;method.<\/p>\n\n\n\n<p>By using the&nbsp;<code>property()<\/code>&nbsp;class, we can add a property to a class while maintaining backward compatibility. In practice, you will define the attributes first. Later, you can add the property to the class if needed.<\/p>\n\n\n\n<p>Putting it all together.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code>from pprint import pprint class Person: def __init__(self, name, age): self.name = name self.age = age def set_age(self, age): if age &lt;= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age age = property(fget=get_age, fset=set_age) print(Person.age) john = Person('John', 18) pprint(john.__dict__) john.age = 19 pprint(Person.__dict__)<\/code><\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Summary: in this tutorial, you\u2019ll learn about the Python&nbsp;property&nbsp;class and how to use it to define properties for a class. Introduction to class properties The following defines a\u00a0Person\u00a0class\u00a0that has two\u00a0attributes\u00a0name\u00a0and\u00a0age, and create a new instance of the\u00a0Person\u00a0class: Since\u00a0age\u00a0is the\u00a0instance attribute\u00a0of the\u00a0Person\u00a0class, you can assign it a new value like this: The following assignment is also [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27],"tags":[],"class_list":["post-164","post","type-post","status-publish","format-standard","hentry","category-3-property"],"_links":{"self":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/comments?post=164"}],"version-history":[{"count":1,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/164\/revisions"}],"predecessor-version":[{"id":165,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/posts\/164\/revisions\/165"}],"wp:attachment":[{"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/media?parent=164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/categories?post=164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/learnpython.elegantwallp.com\/wp-json\/wp\/v2\/tags?post=164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}