{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fdcf04c6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Class Design, Hiding Attributes\n",
    "\n",
    "#### CS 65: Introduction to Computer Science I"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1a35e9e",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Reviewing Objects and Classes\n",
    "\n",
    "Let's continue with the Rectangle class we worked with last time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "95d78342",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Rectangle 1's area: 35\n",
      "Rectangle 2's area: 5700\n"
     ]
    }
   ],
   "source": [
    "class Rectangle:\n",
    "    \"\"\"\n",
    "    Used for representing rectangles\n",
    "    \n",
    "    attributes: length, width\n",
    "    \"\"\"\n",
    "    def __init__(self, starting_length, starting_width):\n",
    "        self.length = starting_length\n",
    "        self.width = starting_width\n",
    "        \n",
    "    def area(self):\n",
    "        return self.length*self.width\n",
    "    \n",
    "    def perimeter(self):\n",
    "        return 2*self.length + 2*self.width\n",
    "    \n",
    "\n",
    "rec1 = Rectangle(7,5) \n",
    "rec2 = Rectangle(60,95)\n",
    "print(\"Rectangle 1's area:\",rec1.area() )\n",
    "print(\"Rectangle 2's area:\",rec2.area() )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8ddeb9cf",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## Group Discussion\n",
    "\n",
    "In the example above, which things are\n",
    "\n",
    "1. Classes\n",
    "2. Objects\n",
    "3. Attributes\n",
    "4. Methods\n",
    "\n",
    "Also discuss the following:\n",
    "\n",
    "5. Each of the methods has a parameter `self`, but we don't put something inside the parentheses when we call it. What is `self` and where does it get its value from?\n",
    "6. When does the `__init__` method get called? Where do its parameters `starting_length` and `starting_width` come from?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a00e6144",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Group Class Design Exercise\n",
    "\n",
    "Suppose you need to create a `Car` class so you can have a data type for representing cars in your program. Discuss the following:\n",
    "\n",
    "1. What are some attributes a car might have? (i.e., what are some data/variables that are important for cars?)\n",
    "2. What are some methods a car might have? (i.e., what kinds of actions/operations might a program need to do with a car?)\n",
    "\n",
    "Then, create a `Car` class that includes at least 3 attributes and one method.\n",
    "\n",
    "Create at least two objects that are instances of the `Car` class (i.e., their type is `Car`) - maybe represent each of your cars."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7fc96339",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "88e45d9b",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Another Example\n",
    "\n",
    "Consider the following `BankAccount` class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "47039f6f",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Account Holder: Eric\n",
      "Balance: 505.0\n",
      "Interest Rate: 0.01\n"
     ]
    }
   ],
   "source": [
    "class BankAccount:\n",
    "    \"\"\"\n",
    "    A class for creating objects representing bank accounts\n",
    "    \n",
    "    attributes:\n",
    "        balance - amount of money in the account\n",
    "        customer_name - the customer's name\n",
    "        interest_rate - interest rate for the account\n",
    "    \"\"\"\n",
    "    def __init__(self, starting_customer_name):\n",
    "        self.customer_name = starting_customer_name\n",
    "        self.balance = 0\n",
    "        self.interest_rate = 0.0\n",
    "        \n",
    "    def deposit(self, amount):\n",
    "        self.balance += amount\n",
    "    \n",
    "    def apply_interest(self):\n",
    "        self.balance = self.balance * (1+self.interest_rate)\n",
    "        \n",
    "    def display_info(self):\n",
    "        print(\"Account Holder:\",self.customer_name)\n",
    "        print(\"Balance:\",self.balance)\n",
    "        print(\"Interest Rate:\",self.interest_rate)\n",
    "        \n",
    "erics_checking = BankAccount(\"Eric\")\n",
    "erics_checking.deposit(500) #this gets passed to the amount parameter\n",
    "erics_checking.interest_rate = 0.01\n",
    "erics_checking.apply_interest()\n",
    "erics_checking.display_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d48e6dff",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Classes can control how attributes are used\n",
    "\n",
    "What if a programmer tries to use an attribute in a way that doesn't make sense?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "14ec0f8d",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Account Holder: Al Yankovic\n",
      "Balance: -100\n",
      "Interest Rate: 0.0\n"
     ]
    }
   ],
   "source": [
    "als_savings = BankAccount(\"Al Yankovic\")\n",
    "als_savings.deposit(-100)\n",
    "als_savings.display_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8eabda3a",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Let's fix this up"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90dde14e",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Hiding attributes\n",
    "\n",
    "It's bad manners to directly mess with an object's attributes. But...\n",
    "\n",
    "A programmer could bypass your updated deposit method and do something weird like"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "1508661c",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Account Holder: Al Yankovic\n",
      "Balance: SPAM!\n",
      "Interest Rate: 0.0\n"
     ]
    }
   ],
   "source": [
    "als_savings.balance = \"SPAM!\"\n",
    "als_savings.display_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbbb6cfe",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Fortunately, you can protect against this.\n",
    "\n",
    "Attribute names that start with two underscore are hidden and can't be changed in this way _outside the class's code_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4bb5f21a",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Account Holder: Al Yankovic\n",
      "Balance: 0\n",
      "Interest Rate: 0.0\n"
     ]
    }
   ],
   "source": [
    "class BankAccount:\n",
    "    \"\"\"\n",
    "    A class for creating objects representing bank accounts\n",
    "    \n",
    "    attributes:\n",
    "        balance - amount of money in the account\n",
    "        customer_name - the customer's name\n",
    "        interest_rate - interest rate for the account\n",
    "    \"\"\"\n",
    "    def __init__(self, starting_customer_name):\n",
    "        self.customer_name = starting_customer_name\n",
    "        self.__balance = 0\n",
    "        self.interest_rate = 0.0\n",
    "        \n",
    "    def deposit(self, amount):\n",
    "        if amount > 0:\n",
    "            self.__balance += amount\n",
    "        else:\n",
    "            print(\"Error: the deposit amount must be positive.\")\n",
    "    \n",
    "    def apply_interest(self):\n",
    "        self._balance = self.__balance * (1+self.interest_rate)\n",
    "        \n",
    "    def display_info(self):\n",
    "        print(\"Account Holder:\",self.customer_name)\n",
    "        print(\"Balance:\",self.__balance)\n",
    "        print(\"Interest Rate:\",self.interest_rate)\n",
    "        \n",
    "als_savings = BankAccount(\"Al Yankovic\")\n",
    "als_savings.__balance = \"SPAM!\" #doesn't actually change self.__balance\n",
    "als_savings.display_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f48ed08f",
   "metadata": {
    "hideCode": false,
    "hidePrompt": false,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "### Why is this useful? \n",
    "\n",
    "You, as the designer of the class, get to have complete control over how the data is used and manipulated.\n",
    "* Fewer errors from misuse\n",
    "* Easier to maintain"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "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.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
