{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Brief Introduction to Decorators in python\n## Some easy utility to glean from decorators demonstrated in this Notebook\n\n### source of my learning: [DigitalSreeni](https://www.youtube.com/watch?v=ZSMRgFQRSoU&ab_channel=DigitalSreeni)","metadata":{}},{"cell_type":"markdown","source":"> import time library","metadata":{}},{"cell_type":"code","source":"import time","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:31:45.502181Z","iopub.execute_input":"2021-08-28T10:31:45.502783Z","iopub.status.idle":"2021-08-28T10:31:45.514671Z","shell.execute_reply.started":"2021-08-28T10:31:45.502667Z","shell.execute_reply":"2021-08-28T10:31:45.512542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## A decorator for any function","metadata":{}},{"cell_type":"markdown","source":"> creating a decorator that can be added to any function without throwing an error by using args and kwargs","metadata":{}},{"cell_type":"code","source":"def non_invasive_decorator(func): #this function simply wraps our function in the wrapper function. the wrapper function itself only contains print statements\n    def wrapper(*args, **kwargs):\n        print('\\npositional args are defined as: ', args)\n        print('the keywords args are defined as: ', kwargs, '\\n')\n        \n        #decorator utility\n        func(*args, **kwargs)\n        #decorator utility \n        \n        print('end of decorator')\n    return wrapper\n\n@non_invasive_decorator\ndef add(n, m):\n    print(n + m)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:36:37.24638Z","iopub.execute_input":"2021-08-28T10:36:37.24675Z","iopub.status.idle":"2021-08-28T10:36:37.251647Z","shell.execute_reply.started":"2021-08-28T10:36:37.246708Z","shell.execute_reply":"2021-08-28T10:36:37.250957Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> we demonstrate the result of the decorator with a basic add function that has been wrapped with our decorator","metadata":{}},{"cell_type":"code","source":"add(2,4)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:36:37.528116Z","iopub.execute_input":"2021-08-28T10:36:37.528596Z","iopub.status.idle":"2021-08-28T10:36:37.533196Z","shell.execute_reply.started":"2021-08-28T10:36:37.528565Z","shell.execute_reply":"2021-08-28T10:36:37.532292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## A Decorator for finding how long a given function takes\n### can be applied to anything, from a basic add/subtract function to a 20 layer conv net and produce the time passed ","metadata":{}},{"cell_type":"markdown","source":"## time elapsed in a simple calculation ","metadata":{}},{"cell_type":"code","source":"def time_elapsed(func): #this function simply wraps our function in the wrapper function. the wrapper function itself only contains print statements\n    def wrapper(*args, **kwargs):\n        \n        start = time.time()\n        func(*args, **kwargs)\n        end = time.time()\n        \n        print('end of function\\ntime elapsed: ', end-start)\n        \n        \n    return wrapper\n\n@time_elapsed\ndef subtract(n, m):\n    print(n - m)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:59:31.928911Z","iopub.execute_input":"2021-08-28T10:59:31.929519Z","iopub.status.idle":"2021-08-28T10:59:31.934486Z","shell.execute_reply.started":"2021-08-28T10:59:31.929475Z","shell.execute_reply":"2021-08-28T10:59:31.933816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"subtract(6,4)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:59:32.680619Z","iopub.execute_input":"2021-08-28T10:59:32.680992Z","iopub.status.idle":"2021-08-28T10:59:32.688424Z","shell.execute_reply.started":"2021-08-28T10:59:32.680959Z","shell.execute_reply":"2021-08-28T10:59:32.687545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## time elapsed in a training process\n\n#### to highlight how modular this is, here is literally a model training and prediction straight from the [sklearn docs](https://scikit-learn.org/stable/auto_examples/linear_model/plot_ols.html) that has been wrapped with our function","metadata":{}},{"cell_type":"markdown","source":"> grab relevant model building components(basic linear reg example)","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport numpy as np\nfrom sklearn import datasets, linear_model\n\n# Load the diabetes dataset\ndiabetes_X, diabetes_y = datasets.load_diabetes(return_X_y=True)\n\n# Use only one feature\ndiabetes_X = diabetes_X[:, np.newaxis, 2]\n\n# Split the data into training/testing sets\ndiabetes_X_train = diabetes_X[:-20]\ndiabetes_X_test = diabetes_X[-20:]\n\n# Split the targets into training/testing sets\ndiabetes_y_train = diabetes_y[:-20]\ndiabetes_y_test = diabetes_y[-20:]\n\n# Create linear regression object\nregr = linear_model.LinearRegression()\n\nX = diabetes_X_train\ny = diabetes_y_train","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:59:34.195713Z","iopub.execute_input":"2021-08-28T10:59:34.196228Z","iopub.status.idle":"2021-08-28T10:59:34.216695Z","shell.execute_reply.started":"2021-08-28T10:59:34.196195Z","shell.execute_reply":"2021-08-28T10:59:34.215921Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> here comes the magic","metadata":{}},{"cell_type":"code","source":"# Train the model using the training sets\n@time_elapsed\ndef fit(X, y):\n    print(\"training model....\\n\")\n    return regr.fit(X, y)\n\nfit(X, y)","metadata":{"execution":{"iopub.status.busy":"2021-08-28T10:59:35.209003Z","iopub.execute_input":"2021-08-28T10:59:35.209568Z","iopub.status.idle":"2021-08-28T10:59:35.217501Z","shell.execute_reply.started":"2021-08-28T10:59:35.209535Z","shell.execute_reply":"2021-08-28T10:59:35.216478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"> time elapsed would increase as you increase the complexity of your training and data on which you train on","metadata":{}},{"cell_type":"code","source":"# Make predictions using the testing set\ndiabetes_y_pred = regr.predict(diabetes_X_test)","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# this marks the end of this brief foray into decorators. Thanks for reading, feel free to comment your previous uses of decorators so that I can read up and build my own knowledge base more=> Thanks!!","metadata":{}}]}