{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [] }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "source": [], "metadata": { "id": "IJ6riesqInKV" } }, { "cell_type": "code", "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "GpSCowiqIajT", "outputId": "f296e253-bc33-4066-e305-de05522c1a93" }, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "hello world!\n" ] } ], "source": [ "# syntax\n", "# def ():\n", "# \n", "# docstring\n", "# \n", "#\n", "# function logic\n", "\n", "\n", "def helloworld():\n", " \"\"\"\n", " __docstring__ goes here\n", " accessed via .__docstring__\n", " \"\"\"\n", " print('hello world!')\n", "\n", "if '__main__' == __name__:\n", " helloworld()" ] }, { "cell_type": "code", "source": [ "# syntax\n", "# def (, , ..., =, =, ...):\n", "# ...\n", "\n", "# optional syntax (documentation, linter hints)\n", "# def (:, :=):\n", "# ...\n", "\n", "def greet_from_list(greeting: str, name_list: list=None):\n", " # ^^^^^^^^^\n", " # watchout for mutable types as default values ie don't do name_list=[] !!\n", " # instead do: name_list=None\n", " # and then check if user user has provided a value:\n", " if name_list is None:\n", " name_list = [\"World\"]\n", "\n", " for name in name_list:\n", " print(f\"{greeting} {name}!\")\n", "\n", "if '__main__' == __name__:\n", " greet_from_list(\"Hi\", [\"Bob\", \"Jack\"])\n", " # >>> Hi Bob!\n", " # >>> Hi Jack!\n", "\n", " # positional parameters can also be set \"as\" keyword parameters\n", " greet_from_list(greeting=\"Hello\")\n", " # >>> Hello World!" ], "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "gLV-qjmrIpii", "outputId": "c0b575d8-88e8-4bda-d78e-4ba2b7c82fdb" }, "execution_count": 2, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Hi Bob!\n", "Hi Jack!\n", "Hello World!\n" ] } ] }, { "cell_type": "code", "source": [ "# Allows the function to accept an unspecified amount of arguments\n", "# * - stores all unspecified parameters into a list called (conventionally called )\n", "# = [arg1, arg2, arg3, ...]\n", "\n", "# ** - stores all unspecified parameters into a dictionary called (conventionally called )\n", "# = {keyword1: value, keyword2: value, ...}\n", "\n", "# FULL SYNTAX:\n", "# def (arg1, arg2, ..., keyarg1=value, keyarg2=value, ..., *args, **kwargs):\n", "# ...\n", "\n", "def print_all_args(*args):\n", " for i, arg in enumerate(args):\n", " print(f\"[{i}]: {arg}\")\n", "\n", "def print_all_kwargs(**kwargs):\n", " for key, value in kwargs.items():\n", " print(f\"{key}: {value}\")\n", "\n", "if '__main__' == __name__:\n", " print_all_args(\"world\", \"Steve\", \"Bob\")\n", " # >>> [0]: world\n", " # >>> [1]: Steve\n", " # >>> [2]: Bob\n", "\n", " print_all_kwargs(world=\"Earth\", system=\"Solar\")\n", " # >>> world: Earth\n", " # >>> system: Solar" ], "metadata": { "id": "kdqSTgm-ItFV" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# TODO: print\n", "# (docstring)\n", "\n", "# returns to an outer scope of the function\n", "\n", "def prepend_greeting(greeting: str, name: str) -> str:\n", " \"joins the strings together in format ' !' and returns the result\"\n", " return f\"{greeting} {name}\"\n", "\n", "# functions by default return None\n", "def func_without_return():\n", " pass\n", "\n", "if '__main__' == __name__:\n", " # func prepend_greeting returns \"Hi Steve!\" which is then printed\n", " print(prepend_greeting(\"Hi\", \"Steve\"))\n", " # >>> Hi Steve!\n", "\n", " # if the returned value is not saved or passed on it is discarded/garbage collected\n", " prepend_greeting(\"Hi\", \"Steve\")\n", " # >>>\n", "\n", " print(type(func_without_return()))\n", " # >>> \n", "\n", " print(func_without_return() is None)\n", " # >>> True" ], "metadata": { "id": "VS8guT_OIyl1" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "\n", "# Functions in python are first-class objects ie behave like any other objects (ints, strings, ...)\n", "def empty():\n", " \"empty function\"\n", " pass\n", "\n", "if '__main__' == __name__:\n", " # function can be passed on to another function (without ever calling )\n", " # dir() returns a list of all properties of an object\n", " # Using it we can inspect all of empty's properties\n", " print(dir(empty))\n", " # >>> ['__annotations__', '__builtins__', '__call__', ...]\n", "\n", " # Note-worthy properties:\n", " # name of the function\n", " print(empty.__name__)\n", " # >>> empty\n", "\n", " # function's docstring\n", " print(empty.__doc__)\n", " # >>> empty function" ], "metadata": { "id": "OuIkmHbnI0BM" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# decorators are used to transform/alter function behaviour\n", "\n", "# usage syntax:\n", "# @\n", "# def ():\n", "# ...\n", "\n", "# Pseudocode of what's happening under the hood:\n", "# def ():\n", "# ...\n", "# = ()\n", "# we are overwriting the function with the new, altered version\n", "\n", "# Decorators are just functions that take another function as an argument\n", "# wrap some additional logic around it and then return the new - edited function\n", "\n", "# Creating decorators:\n", "# 1) Define a function with desired with a single argument (for the function)\n", "def decorator_example(func):\n", " print(\"This is called on func definition\")\n", " # 2) Define a `wrapping` function that will `wrap` logic around the func we are transforming\n", " # We don't know what function might be used with our decorator\n", " # Because the function might require arguments we will use beforementioned *args **kwargs\n", " # That way it will handle functions with any number of arguments\n", " def wrapper(*args, **kwargs):\n", " # Logic before func call\n", " print(f\"This is called before {func.__name__}()\")\n", "\n", " # call the func with args and kwargs\n", " # This will \"unfold\" arguments into separate ones instead of passing it the list and dictionary\n", " ### func(args, kwargs) != func(*args, **kwargs) ###\n", " func(*args, **kwargs)\n", "\n", " # logic after func call\n", " print(f\"This is called after {func.__name__}()\")\n", "\n", " # 3) Return the function we used to wrap func\n", " return wrapper\n", "\n", "\n", "# Now we can do\n", "@decorator_example\n", "def helloworld():\n", " print(f\"Hello World!\")\n", "### Because the decorator itself contains a print statement we will get an output on helloworld definition\n", "### >>> This is called on func definition\n", "\n", "if '__main__' == __name__:\n", " helloworld()\n", " # >>> This is called before helloworld()\n", " # >>> Hello World!\n", " # >>> This is called after helloworld()" ], "metadata": { "id": "1ZiQuuh7I1Rs" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# decorators can also take in arguments\n", "\n", "# usage syntax:\n", "# @()\n", "# def ():\n", "# ...\n", "\n", "# Decorators themselves cannot have arguments so what we do instead is\n", "# we create a wrapper that returns a modified (based on args) decorator\n", "# that we then use to modify the function\n", "# Pseudocode explanation:\n", "# def ():\n", "# ...\n", "# modified_decorator = () #returns a decorator (ie function)\n", "# = modified_decorator(function)\n", "\n", "# Essentially we are making a decorator for a decorator -ish\n", "\n", "# Example of a decorator that will call its function n times\n", "# Creating decorators with arguments:\n", "# 1) Define a function with desired and required arguments\n", "def decorator_example(n=1):\n", "\n", " # 2) create a function that returns the modified decorator\n", " def decorator_wrapper(func):\n", "\n", " # 3) Define a `wrapping` function that will `wrap` logic around the func we are transforming\n", " def function_wrapper(*args, **kwargs):\n", "\n", " # call the func (in this example n-times)\n", " for i in range(n):\n", " func(*args, **kwargs)\n", "\n", " # 4) Return the function we used to wrap func\n", " return function_wrapper\n", " # 5) Return the modified decorator\n", " return decorator_wrapper\n", "\n", "\n", "# Now we can do\n", "@decorator_example(n=3)\n", "def helloworld():\n", " print(f\"Hello World!\")\n", "\n", "if '__main__' == __name__:\n", " helloworld()\n", " # >>> Hello World!\n", " # >>> Hello World!\n", " # >>> Hello World!" ], "metadata": { "id": "CumuefVjI2ks" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Python like many other languages has a short-hand way of creating functions\n", "\n", "# Syntax:\n", "# lambda , , ...: #do something\n", "\n", "#Example:\n", "print_argument_lambda = lambda x: print(x)\n", "\n", "#Identical example using functions:\n", "def print_argument_func(x):\n", " print(x)\n", "\n", "if __name__ == \"__main__\":\n", " print_argument_lambda(3)\n", " # >>> 3\n", "\n", " print_argument_func(3)\n", " # >>> 3\n" ], "metadata": { "id": "goCOhnoaI5EG" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Lambdas are most commonly used when we need to pass a temporary function as a parameter to another function\n", "# For example when sorting a list of objects we need to first define by what property we are ordering\n", "\n", "# example class\n", "class obj:\n", " # constructor with some propery x\n", " def __init__(self, x):\n", " self.x = x\n", "\n", " # when we pass a non-string object to print()\n", " # python will call that objects __repr__() function\n", " # and print the string it returns instead\n", "\n", " # here I'm overriding it to print it in a useful/readable way\n", " def __repr__(self):\n", " return f\"obj({self.x})\"\n", "\n", "\n", "if __name__ == \"__main__\":\n", " # example list of objects\n", " list_of_objects = [obj(10), obj(3), obj(-6), obj(31), obj(0)]\n", "\n", " sorted_list_of_objects = sorted(\n", " # iterable we are sorting\n", " list_of_objects,\n", " # sorted() will pass each item of the iterable to the lambda as an argument\n", " # the lambda then returns the property x and sorted() uses it to well... sort\n", " key=lambda list_element: list_element.x\n", " )\n", "\n", " print(sorted_list_of_objects)\n", " # >>> [obj(-6), obj(0), obj(3), obj(10), obj(31)]" ], "metadata": { "id": "1sN2hrovI9wM" }, "execution_count": null, "outputs": [] } ] }