Private Attributes and Properties

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

Objectives:

  • Learn how to encapsulate object internals using private attributes, properties, and slots

Files Modified: stock.py


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/numeric_types("`Numeric Types`") python/BasicConceptsGroup -.-> python/strings("`Strings`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/ControlFlowGroup -.-> python/list_comprehensions("`List Comprehensions`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/DataStructuresGroup -.-> python/sets("`Sets`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/using_packages("`Using Packages`") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("`Classes and Objects`") python/ObjectOrientedProgrammingGroup -.-> python/constructor("`Constructor`") python/ObjectOrientedProgrammingGroup -.-> python/polymorphism("`Polymorphism`") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("`Encapsulation`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132494{{"`Private Attributes and Properties`"}} python/variables_data_types -.-> lab-132494{{"`Private Attributes and Properties`"}} python/numeric_types -.-> lab-132494{{"`Private Attributes and Properties`"}} python/strings -.-> lab-132494{{"`Private Attributes and Properties`"}} python/conditional_statements -.-> lab-132494{{"`Private Attributes and Properties`"}} python/for_loops -.-> lab-132494{{"`Private Attributes and Properties`"}} python/list_comprehensions -.-> lab-132494{{"`Private Attributes and Properties`"}} python/lists -.-> lab-132494{{"`Private Attributes and Properties`"}} python/tuples -.-> lab-132494{{"`Private Attributes and Properties`"}} python/sets -.-> lab-132494{{"`Private Attributes and Properties`"}} python/function_definition -.-> lab-132494{{"`Private Attributes and Properties`"}} python/importing_modules -.-> lab-132494{{"`Private Attributes and Properties`"}} python/using_packages -.-> lab-132494{{"`Private Attributes and Properties`"}} python/classes_objects -.-> lab-132494{{"`Private Attributes and Properties`"}} python/constructor -.-> lab-132494{{"`Private Attributes and Properties`"}} python/polymorphism -.-> lab-132494{{"`Private Attributes and Properties`"}} python/encapsulation -.-> lab-132494{{"`Private Attributes and Properties`"}} python/raising_exceptions -.-> lab-132494{{"`Private Attributes and Properties`"}} python/python_shell -.-> lab-132494{{"`Private Attributes and Properties`"}} python/build_in_functions -.-> lab-132494{{"`Private Attributes and Properties`"}} end

Private attributes

As a general rule, attributes that are internal to a class should have a leading underscore. In the previous exercise, the Stock class had a types class variable that was used for converting rows of data. Change the code so that this variable has a leading underscore on it.

Properties for computed attributes

Earlier, you defined a class Stock. For example:

>>> s = Stock('GOOG',100,490.10)
>>> s.name
'GOOG'
>>> s.shares
100
>>> s.price
490.1
>>> s.cost()
49010.0
>>>

Using a property, turn cost() into an attribute that no longer requires the parentheses. For example:

>>> s = Stock('GOOG', 100, 490.1)
>>> s.cost               ## Property. Computes the cost
49010.0
>>>

Enforcing Validation Rules

Using properties and private attributes, modify the shares attribute of the Stock class so that it can only be assigned a non-negative integer value. In addition, modify the price attribute so that it can only be assigned a non-negative floating point value.

The new object should work almost exactly the same as the old one except for extra type and value checking.

>>> s = Stock('GOOG', 100, 490.10)
>>> s.shares = 50          ## OK
>>> s.shares = '50'
Traceback (most recent call last):
...
TypeError: Expected integer
>>> s.shares = -10
Traceback (most recent call last):
...
ValueError: shares must be >= 0

>>> s.price = 123.45       ## OK
>>> s.price = '123.45'
Traceback (most recent call last):
...
TypeError: Expected float
>>> s.price = -10.0
Traceback (most recent call last):
...
ValueError: price must be >= 0
>>>

Adding __slots__

Modify your new Stock class to use __slots__. You will find that you have to use a different set of attribute names than before--specifically, you will have to list the private attribute names (e.g., if a property is storing a value in an attribute _shares, that is the name you list in __slots__). Verify that the class still works and that you can no longer add new attributes.

>>> s = Stock('GOOG', 100, 490.10)
>>> s.spam = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Stock' object has no attribute 'spam'
>>>

Reconciling Types

In the current Stock class, there is a _types class variable that gives conversions when reading from a file, but there are also properties that are enforcing types. Who is in charge of this show? Fix the property definitions so that they use the types specified in the _types class variable. Make sure the properties work when types are changed via subclassing. For example:

>>> from decimal import Decimal
>>> class DStock(Stock):
        _types = (str, int, Decimal)

>>> s = DStock('AA', 50, Decimal('91.1'))
>>> s.price = 92.3
Traceback (most recent call last):
...
TypeError: Expected a Decimal
>>>

Discussion

The resulting Stock class at the end of this lab is a muddled mess of properties, type checking, constructors, and other details. Imagine how unpleasant it would be to maintain code that featured dozens or hundreds of such class definitions.

We're going to figure out how to simplify things considerably, but it's going to take some time and some more advanced techniques. Stay tuned.

Summary

Congratulations! You have completed the Private Attributes and Properties lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like