From 30d8872b90dc876935fdd1aa55fc9e1ad0ef6fcd Mon Sep 17 00:00:00 2001 From: baniasbaabe Date: Sat, 30 Dec 2023 13:04:27 +0000 Subject: [PATCH] deploy: 7e37de7efba2f534a60c581a48469f5d24eca2ba --- _sources/book/pythontricks/Chapter.ipynb | 50 ++++++++++++++++++ _sources/book/testing/Chapter.ipynb | 64 ++++++++++++++++++++++++ book/pythontricks/Chapter.html | 36 +++++++++++++ book/testing/Chapter.html | 30 +++++++++++ searchindex.js | 2 +- 5 files changed, 181 insertions(+), 1 deletion(-) diff --git a/_sources/book/pythontricks/Chapter.ipynb b/_sources/book/pythontricks/Chapter.ipynb index 4e91ec7..2c6ecd2 100644 --- a/_sources/book/pythontricks/Chapter.ipynb +++ b/_sources/book/pythontricks/Chapter.ipynb @@ -829,6 +829,56 @@ "p = Point(x=3.2, y=1.0)\n", "print(p.x, p.y)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mutable Default Values for Function Arguments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One big mistake in Python:\n", + "\n", + "Using mutable default values for function arguments.\n", + "\n", + "When using mutable objects like a list as a default value, you have to be careful.\n", + "\n", + "See the example below, where the default list is shared among all calls to the function.\n", + "\n", + "To fix this, set the default value to None and create a new list inside the function if no list is provided." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Dangerous behaviour:\n", + "def increment_numbers(numbers=[]):\n", + " for i in range(3):\n", + " numbers.append(i)\n", + " print(f\"Numbers: {numbers}\")\n", + "\n", + "increment_numbers() # Output: Numbers: [0, 1, 2]\n", + "increment_numbers() # Output: Numbers: [0, 1, 2, 0, 1, 2]\n", + "\n", + "\n", + "# Better:\n", + "def increment_numbers(numbers=None):\n", + " if numbers is None:\n", + " numbers = []\n", + " for i in range(3):\n", + " numbers.append(i)\n", + " print(f\"Numbers: {numbers}\")\n", + "\n", + "increment_numbers() # Output: Numbers: [0, 1, 2]\n", + "increment_numbers() # Output: Numbers: [0, 1, 2]" + ] } ], "metadata": { diff --git a/_sources/book/testing/Chapter.ipynb b/_sources/book/testing/Chapter.ipynb index 4674171..7c6470a 100644 --- a/_sources/book/testing/Chapter.ipynb +++ b/_sources/book/testing/Chapter.ipynb @@ -358,6 +358,70 @@ "def test_addition_commutative(a, b):\n", " assert a + b == b + a" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mocking Dependencies with `pytest-mock`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Testing is an essential part of Software projects.\n", + "\n", + "\n", + "\n", + "Especially unit testing, where you test the smallest piece of code that can be isolated.\n", + "\n", + "\n", + "\n", + "They should be independent, and fast & cheap to execute.\n", + "\n", + "\n", + "\n", + "But, what if you have some dependencies like API calls or interactions with databases and systems?\n", + "\n", + "\n", + "\n", + "Here's where mocking comes into play.\n", + "\n", + "\n", + "\n", + "Mocking allows you to replace dependencies and real objects with fake ones which mimic the real behavior.\n", + "\n", + "\n", + "\n", + "So, you don't have to rely on the availability of your API, or ask for permission to interact with a database, but you can test your functions isolated and independently.\n", + "\n", + "\n", + "\n", + "In Python, you can perform mocking with `pytest-mock`, a wrapper around the built-in mock functionality of Python.\n", + "\n", + "\n", + "\n", + "See the example below, where we mock the file removal functionality. We can test it without deleting a file from the disk." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "\n", + "class UnixFS:\n", + " @staticmethod\n", + " def rm(filename):\n", + " os.remove(filename)\n", + "def test_unix_fs(mocker):\n", + " mocker.patch('os.remove')\n", + " UnixFS.rm('file')\n", + " os.remove.assert_called_once_with('file')\n" + ] } ], "metadata": { diff --git a/book/pythontricks/Chapter.html b/book/pythontricks/Chapter.html index 1c70b5e..bb7543b 100644 --- a/book/pythontricks/Chapter.html +++ b/book/pythontricks/Chapter.html @@ -435,6 +435,7 @@

Contents

  • 10.1.18. Count Occurrences in an Iterable with Counter
  • 10.1.19. Set Default Values for Dictionaries with defaultdict
  • 10.1.20. More Structured Tuples with namedtuple
  • +
  • 10.1.21. Mutable Default Values for Function Arguments
  • @@ -934,6 +935,40 @@

    10.1.20. More Structured Tuples with +
    +

    10.1.21. Mutable Default Values for Function Arguments#

    +

    One big mistake in Python:

    +

    Using mutable default values for function arguments.

    +

    When using mutable objects like a list as a default value, you have to be careful.

    +

    See the example below, where the default list is shared among all calls to the function.

    +

    To fix this, set the default value to None and create a new list inside the function if no list is provided.

    +
    +
    +
    # Dangerous behaviour:
    +def increment_numbers(numbers=[]):
    +    for i in range(3):
    +        numbers.append(i)
    +    print(f"Numbers: {numbers}")
    +
    +increment_numbers()  # Output: Numbers: [0, 1, 2]
    +increment_numbers()  # Output: Numbers: [0, 1, 2, 0, 1, 2]
    +
    +
    +# Better:
    +def increment_numbers(numbers=None):
    +    if numbers is None:
    +        numbers = []
    +    for i in range(3):
    +        numbers.append(i)
    +    print(f"Numbers: {numbers}")
    +
    +increment_numbers()  # Output: Numbers: [0, 1, 2]
    +increment_numbers()  # Output: Numbers: [0, 1, 2]
    +
    +
    +
    +
    +