{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Software Entwicklung \n",
    "\n",
    "## Kapitel 4: Funktionen\n",
    "\n",
    "### 4.4 Funktionsparameter \n",
    "\n",
    "Globale Variablen sind keine gute Idee, um Werte in Funktionen verfügbar zu machen, da schnell der\n",
    "Überblick verloren geht, welche Variable in welcher Funktion gelesen oder sogar geändert wird.\n",
    "Auch würde ein solches Vorgehen erfordern, dass man funktionsübergreifend Variablennamen im \n",
    "Blick behält, was ebenfalls bei komplexeren Aufgaben ein schwieriges Unterfangen ist."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Daher verwendet man besser *Funktionsparameter*, um Werte von der aufrufenden Programmstelle \n",
    "in eine Funktion zu übergeben. Die erwarteten Parameter werden bei der Funktionsdefinition\n",
    "und beim Funktionsaufruf zwischen den runden Klammern aufgeführt.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def drucke_addition(a, b):\n",
    "    print(a+b)\n",
    "\n",
    "drucke_addition(4, 7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Natürlich können auch Werte aus Variablen beim Funktionsaufruf übergeben werden. \n",
    "Dabei sind die Variablen- und Parameternamen völlig unabhängig. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def drucke_addition(a, b):\n",
    "    print(a+b)\n",
    "\n",
    "x=1\n",
    "y=2\n",
    "drucke_addition(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Um berechnete Werte aus einer Funktion an die Außenwelt unter Vermeidung von\n",
    "globalen Variablen zurückzugeben, sollten Rückgabewerte benutzt werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def tausche(a, b):\n",
    "    return (b, a)\n",
    "\n",
    "x=\"Erster\"\n",
    "y=\"Zweiter\"\n",
    "x, y = tausche(x, y) \n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### 4.5 Identität vs. Gleichheit\n",
    "\n",
    "In der Informatik wird zwischen *Identität* und *Gleichheit* unterschieden. Zwei \n",
    "Variablen sind gleich, wenn sie den gleichen Wert enthalten. Dies wird typischerweise\n",
    "durch das Doppel-Gleichheitszeichen <code>==</code> überprüft."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "x=5\n",
    "y=5\n",
    "if x == y:\n",
    "    print(\"Gleich!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Die Identität von zwei Variablen ist dann gegeben, wenn sie auf das gleiche Objekt im\n",
    "Speicher verweisen. Überprüft wird dies in Python durch den Infix-Operator <code>is</code>. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "x=5\n",
    "y=5\n",
    "if x is y:\n",
    "    print(\"Identisch!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Außerdem kann mit der Built-In Funktion <code>id</code> eine Art *Speicheradresse* eines Werts \n",
    "ermittelt werden, der bei identischen Werten gleich ist. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "x=\"Hello\"\n",
    "y=\"Hello\"\n",
    "\n",
    "print(id(x))\n",
    "print(id(y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Bei Datentypen, deren Werte unveränderlich sind, können Identität und Gleichheit synonym verwendet werden. \n",
    "In Python sind dies\n",
    "\n",
    "* Zahlen\n",
    "* Strings\n",
    "* <code>True</code> und <code>False</code>\n",
    "* Tupel"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Variablen, die diese Art von Datentypen enthalten, ändern somit automatisch ihre Identität, wenn\n",
    "sich ihr Wert ändert."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "x=17\n",
    "print(id(x))\n",
    "x = x + 1\n",
    "print(id(x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Bei veränderlichen Datentypen wie Listen ist es aber möglich, dass \n",
    "sich ihr Wert ändert, ohne dass sie ihre Identität verlieren. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "liste = [1, 2, 3]\n",
    "print(id(liste))\n",
    "liste.append(4)\n",
    "print(id(liste))\n",
    "print(liste)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Werden Variablen an eine Funktion übergeben, so behalten sie im aufrufenden Kontext \n",
    "in jedem Fall ihre Identität, auch wenn in der Funktion der Wert des zugeordneten Parameters\n",
    "geändert wird.\n",
    "\n",
    "Bei unveränderlichen Datentypen, bei denen eine Wertänderung unweigerlich auch zu einer\n",
    "neuen Identität führt, sind diese Parameter somit wie lokale Variable zu betrachten,\n",
    "die vollständig vom aufrufenden Code entkoppelt sind."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    },
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "def tue_was(a):\n",
    "    print(f\"ID von a vor Änderung: {id(a)}\")\n",
    "    a = a + 1\n",
    "    print(a)\n",
    "    print(f\"ID von a nach Änderung: {id(a)}\")\n",
    "\n",
    "x=1\n",
    "print(f\"ID von x vor Aufruf: {id(x)}\")\n",
    "tue_was(x)\n",
    "print(x)\n",
    "print(f\"ID von x nach Aufruf: {id(x)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Bei veränderlichen Datentypen wie Listen können \n",
    "Wertänderungen nach außen durchschlagen, weil \n",
    "die Identität durch die Wertänderung nicht verändert wird."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def tue_was_mit_liste(a):\n",
    "    print(f\"ID von a vor Änderung: {id(a)}\")\n",
    "    a.append(3)\n",
    "    print(a)\n",
    "    print(f\"ID von a nach Änderung: {id(a)}\")\n",
    "\n",
    "x=[1, 2]\n",
    "print(f\"ID von x vor Aufruf: {id(x)}\")\n",
    "tue_was_mit_liste(x)\n",
    "print(x)\n",
    "print(f\"ID von x nach Aufruf: {id(x)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Daher liefern die Operatoren <code>==</code> und <code>is</code> bei Listen \n",
    "ggf. auch abweichende Ergebnisse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "liste1 = [1, 2, 3]\n",
    "liste2 = [1, 2, 3]\n",
    "\n",
    "if liste1 == liste2:\n",
    "    print(\"Gleich!\")\n",
    "    \n",
    "if liste1 is liste2:\n",
    "    print(\"Identisch!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "**Aber:** Zuweisungen und Parameterübergaben erhalten bei allen Datentypen (auch bei Listen) die Identität."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "liste1 = ['A', 'B', 'C']\n",
    "liste2 = liste1\n",
    "\n",
    "print(id(liste1))\n",
    "print(id(liste2))\n",
    "\n",
    "if liste1 == liste2:\n",
    "    print(\"Gleich!\")\n",
    "    \n",
    "if liste1 is liste2:\n",
    "    print(\"Identisch!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Soll bei einer Zuweisung oder Parameterübergabe einer Liste die empfangende Seite eine eigene \n",
    "Identität erhalten, so muss eine Kopie der Liste verwendet werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "liste1 = ['A', 'B', 'C']\n",
    "liste2 = liste1.copy()\n",
    "\n",
    "print(id(liste1))\n",
    "print(id(liste2))\n",
    "\n",
    "if liste1 == liste2:\n",
    "    print(\"Gleich!\")\n",
    "\n",
    "if liste1 is liste2:\n",
    "    print(\"Identisch!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "pycharm": {
     "name": "#%% md\n"
    },
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### 4.6 Benannte Parameter\n",
    "\n",
    "Normalerweise erfolgt das Mapping der Parameter beim Aufruf einer Funktion \n",
    "aufgrund der Reihenfolge der Parameter. Die Variablennamen bzw. Parameternamen \n",
    "haben keinen Einfluss."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "a = \"1\"\n",
    "b = \"2\"\n",
    "\n",
    "def ausgabe(a, b):\n",
    "    print(a)\n",
    "    print(b)\n",
    "    \n",
    "ausgabe(b, a) # Reihenfolge vertauscht"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Möchte man beim Aufruf die Reihenfolge der Parameter ändern, so können auch die\n",
    "Parameternamen explizit aufgeführt werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "a = \"1\"\n",
    "b = \"2\"\n",
    "\n",
    "def ausgabe(parm1, parm2):\n",
    "    print(parm1)\n",
    "    print(parm2)\n",
    "    \n",
    "ausgabe(parm2=b, parm1=a) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Bei einem Funktionsaufruf können benannte und unbenannte Parameter gemischt werden,\n",
    "jedoch dürfen nach einem benannten Parameter keine unbenannten mehr folgen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def viele_parameter(a, b, c, d, e, f):\n",
    "    print(a, b, c, d, e, f)\n",
    "\n",
    "viele_parameter(1, 2, 3, f=7, e=3, d=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### 4.7 Funktionen mit variabler Parameterzahl\n",
    "\n",
    "Normalerweise müssen bei einem Funktionsaufruf alle Parameter angegeben werden. \n",
    "Werden bei der Funktionsdefinition Parameter jedoch mit *Default-Werten* versehen,\n",
    "so können diese beim Aufruf ausgelassen werden. In der Funktion steht dann \n",
    "der Default-Wert zur Verfügung."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def default_parameter(a, b=1, c=True):\n",
    "    return a+b if c else a*b\n",
    "\n",
    "print(default_parameter(5))\n",
    "print(default_parameter(5, c=False))\n",
    "print(default_parameter(5, 2))\n",
    "print(default_parameter(4, 4, False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Noch flexibler ist die Verwendung eines <code>*</code> vor den Parameternamen. Dadurch werden \n",
    "beliebig viele Parameter als Tupel an die Funktion übergeben. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "is_executing": false,
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def ausgabe_tupel(*args):\n",
    "    for parm in args:\n",
    "        print(parm)\n",
    "    \n",
    "ausgabe_tupel(1, 'A', 3.14) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Dieser besondere Parameter kann mit weiteren Parametern kombiniert werden, muss aber als \n",
    "letzter (unbenannter) Parameter in der Liste stehen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "pycharm": {
     "name": "#%%\n"
    }
   },
   "outputs": [],
   "source": [
    "def ausgabe_parms_und_tupel(a, b, *args):\n",
    "    if a > b:\n",
    "        print(\"Größer!\")\n",
    "    for parm in args:\n",
    "        print(parm)\n",
    "    \n",
    "ausgabe_parms_und_tupel(4, 3, 1, 'A', 3.14) "
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.9.9"
  },
  "pycharm": {
   "stem_cell": {
    "cell_type": "raw",
    "metadata": {
     "collapsed": false
    },
    "source": []
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
