diff --git a/_includes/posts/2023-06-22-encapsulation_in_python.html b/_includes/posts/2023-06-22-encapsulation_in_python.html new file mode 100644 index 0000000..645780d --- /dev/null +++ b/_includes/posts/2023-06-22-encapsulation_in_python.html @@ -0,0 +1,14550 @@ + + + + + +2023-06-22-encapsulation_in_python + + + + + + + + + + + + + + + + + + + + + + + +
+
+

In OOP, setter and getter (mutator and accessor) methods is a common way to implement encapsulation which protects private variables. In Python, as we know, object properties are public, when we need to achieve property protection, the pythonic way is the property decorator.

+ +
+
+
+
+

General Class

A general class looks like

+ +
+
+
+ +
+ +
+
+ +
+ +
+ + + + +
+ +
+
+
+

Clearly, we can have some unexpected names...

+ +
+
+
+ +
+ +
+ + + + +
+ +
+
+
+

Class with Property Protection

Depends on the purpose, if certain level of encapsulation is need, we can leverage the property decorator on getter and overloading setter method.

+ +
+
+
+ +
+ +
+
+ +
+ +
+ + + + +
+ +
+
+
+

Since it's an assignment operation with one argument in it, the overloading name setter method will be used.

+

Let's try to instantiate an object with unexpected name...

+ +
+
+
+ +
+ +
+ + + + +
+ +
+
+
+

From the traceback, we can see the self.name = name in the 4th line is infact the initial setter and the real value stores in the self._name property.

+ +
+
+
+
+

Reference

    +
  1. https://en.wikipedia.org/wiki/Mutator_method
  2. +
  3. https://docs.python.org/3.6/library/functions.html?highlight=setter#property
  4. +
+ +
+
+ + + + + + + + + diff --git a/_posts/2023-06-22-encapsulation_in_python.md b/_posts/2023-06-22-encapsulation_in_python.md new file mode 100644 index 0000000..9262be4 --- /dev/null +++ b/_posts/2023-06-22-encapsulation_in_python.md @@ -0,0 +1,9 @@ +--- +layout: post +title: "Encapsulation in Python" +date: 2023-06-23 01:03:00 +0800 +tags: python +--- + + +{% include posts/2023-06-22-encapsulation_in_python.html %} diff --git a/assets/posts/2023-06-22-encapsulation_in_python/2023-06-22-encapsulation_in_python.ipynb b/assets/posts/2023-06-22-encapsulation_in_python/2023-06-22-encapsulation_in_python.ipynb new file mode 100644 index 0000000..4452fdf --- /dev/null +++ b/assets/posts/2023-06-22-encapsulation_in_python/2023-06-22-encapsulation_in_python.ipynb @@ -0,0 +1,223 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In OOP, setter and getter (mutator and accessor) methods is a common way to implement encapsulation which protects private variables. In Python, as we know, object properties are public, when we need to achieve property protection, the pythonic way is the `property` decorator." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## General Class\n", + "A general class looks like" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "class Person:\n", + " def __init__(self, name: str) -> None:\n", + " self.name = name" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'John'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "p = Person(\"John\")\n", + "p.name" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Clearly, we can have some unexpected names..." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "123\n", + "[]\n" + ] + } + ], + "source": [ + "print(Person(123).name)\n", + "print(Person([]).name)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Class with Property Protection\n", + "Depends on the purpose, if certain level of encapsulation is need, we can leverage the `property` decorator on getter and overloading setter method." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "class StrictPerson:\n", + " def __init__(self, name: str) -> None:\n", + " self._name = None # where real value is\n", + " self.name = name # interface for getter and setter\n", + " \n", + " @property\n", + " def name(self) -> str:\n", + " return self._name\n", + " \n", + " @name.setter\n", + " def name(self, name: str) -> None:\n", + " if isinstance(name, str):\n", + " self._name = name\n", + " else:\n", + " raise ValueError(\"Name should be a string.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Doe\n", + "John Wick\n" + ] + }, + { + "ename": "ValueError", + "evalue": "Name should be a string.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0msp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"John Wick\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0msp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m123\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mname\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Name should be a string.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: Name should be a string." + ] + } + ], + "source": [ + "sp = StrictPerson(\"Doe\")\n", + "print(sp.name)\n", + "sp.name = \"John Wick\"\n", + "print(sp.name)\n", + "sp.name = 123" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since it's an assignment operation with one argument in it, the overloading name setter method will be used.\n", + "\n", + "Let's try to instantiate an object with unexpected name..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Name should be a string.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mStrictPerson\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m123\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mstr\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;31m# where real value is\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mname\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mname\u001b[0m \u001b[0;31m# interface for getter and setter\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0mproperty\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mname\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_name\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Name should be a string.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mValueError\u001b[0m: Name should be a string." + ] + } + ], + "source": [ + "StrictPerson(123)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "From the traceback, we can see the `self.name = name` in the 4th line is infact the initial setter and the real value stores in the `self._name` property." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference\n", + "1. https://en.wikipedia.org/wiki/Mutator_method\n", + "2. https://docs.python.org/3.6/library/functions.html?highlight=setter#property" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyNMTe6QuzjH1w/PUyuUGaB0", + "collapsed_sections": [], + "name": "introduction_to_statistics.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.15" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}