From 14152b9a307fd073b426eb29fa4f14e380b73c2f Mon Sep 17 00:00:00 2001 From: Cacahuete Date: Mon, 30 Nov 2020 23:58:29 +0100 Subject: [PATCH] First commit with 2 exercises. --- .gitignore | 136 ++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + add/add.py | 6 ++ add/add_v1.py | 7 +++ add/test_add.py | 63 +++++++++++++++++++ circle/circle.py | 37 ++++++++++++ circle/test_circle.py | 72 ++++++++++++++++++++++ 7 files changed, 322 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 add/add.py create mode 100644 add/add_v1.py create mode 100644 add/test_add.py create mode 100644 circle/circle.py create mode 100644 circle/test_circle.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c475c9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# VSCode project settings +.vscode + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Project specific +test-folder \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..356c34c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Here are my solutions and all the tinkering going around [Python Morsels](https://www.pythonmorsels.com/)' exercises. \ No newline at end of file diff --git a/add/add.py b/add/add.py new file mode 100644 index 0000000..16ea2af --- /dev/null +++ b/add/add.py @@ -0,0 +1,6 @@ +def add(*matrices): + get_shape = lambda matrix: [len(vector) for vector in matrix] + shape = get_shape(matrices[0]) + if any(get_shape(matrix) != shape for matrix in matrices): + raise ValueError("Given matrices are not the same size.") + return [[sum(elt) for elt in zip(*vectors)] for vectors in zip(*matrices)] diff --git a/add/add_v1.py b/add/add_v1.py new file mode 100644 index 0000000..2cddc87 --- /dev/null +++ b/add/add_v1.py @@ -0,0 +1,7 @@ +def add(*matrices): + m_sizes = [len(matrix) for matrix in matrices] + v_sizes = [len(vectors) for matrix in matrices for vectors in matrix] + same_values = lambda l: all(e == l[0] for e in l[1:]) + if not same_values(m_sizes) or not same_values(v_sizes): + raise ValueError("Given matrices are not the same size.") + return [[sum(elt) for elt in zip(*vectors)] for vectors in zip(*matrices)] diff --git a/add/test_add.py b/add/test_add.py new file mode 100644 index 0000000..bd67918 --- /dev/null +++ b/add/test_add.py @@ -0,0 +1,63 @@ +from copy import deepcopy +import unittest + +from add import add + + +class AddTests(unittest.TestCase): + + """Tests for add.""" + + def test_single_items(self): + self.assertEqual(add([[5]], [[-2]]), [[3]]) + + def test_two_by_two_matrixes(self): + m1 = [[6, 6], [3, 1]] + m2 = [[1, 2], [3, 4]] + m3 = [[7, 8], [6, 5]] + self.assertEqual(add(m1, m2), m3) + + def test_two_by_three_matrixes(self): + m1 = [[1, 2, 3], [4, 5, 6]] + m2 = [[-1, -2, -3], [-4, -5, -6]] + m3 = [[0, 0, 0], [0, 0, 0]] + self.assertEqual(add(m1, m2), m3) + + def test_input_unchanged(self): + m1 = [[6, 6], [3, 1]] + m2 = [[1, 2], [3, 4]] + m1_original = deepcopy(m1) + m2_original = deepcopy(m2) + add(m1, m2) + self.assertEqual(m1, m1_original) + self.assertEqual(m2, m2_original) + + # To test the Bonus part of this exercise, comment out the following line + # @unittest.expectedFailure + def test_any_number_of_matrixes(self): + m1 = [[6, 6], [3, 1]] + m2 = [[1, 2], [3, 4]] + m3 = [[2, 1], [3, 4]] + m4 = [[9, 9], [9, 9]] + m5 = [[31, 32], [27, 24]] + self.assertEqual(add(m1, m2, m3), m4) + self.assertEqual(add(m2, m3, m1, m1, m2, m4, m1), m5) + + # To test the Bonus part of this exercise, comment out the following line + # @unittest.expectedFailure + def test_different_matrix_size(self): + m1 = [[6, 6], [3, 1]] + m2 = [[1, 2], [3, 4], [5, 6]] + m3 = [[6, 6], [3, 1, 2]] + with self.assertRaises(ValueError): + add(m1, m2) + with self.assertRaises(ValueError): + add(m1, m3) + with self.assertRaises(ValueError): + add(m1, m1, m1, m3, m1, m1) + with self.assertRaises(ValueError): + add(m1, m1, m1, m2, m1, m1) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/circle/circle.py b/circle/circle.py new file mode 100644 index 0000000..29ea74e --- /dev/null +++ b/circle/circle.py @@ -0,0 +1,37 @@ +from math import pi + + +class Circle: + def __init__(self, radius=1): + self.radius = radius + + def __repr__(self): + return f"Circle({self.radius})" + + @property + def radius(self): + return self._radius + + @radius.setter + def radius(self, radius): + if radius < 0: + raise ValueError("Radius cannot be negative") + self._radius = radius + + @property + def diameter(self): + return 2 * self._radius + + @diameter.setter + def diameter(self, diameter): + if diameter < 0: + raise ValueError("Diameter cannot be negative") + self._radius = diameter / 2 + + @property + def area(self): + return pi * self._radius ** 2 + + @area.setter + def area(self, area): + raise AttributeError("Can't set attribute") diff --git a/circle/test_circle.py b/circle/test_circle.py new file mode 100644 index 0000000..43c9260 --- /dev/null +++ b/circle/test_circle.py @@ -0,0 +1,72 @@ +import math +import unittest + +from circle import Circle + + +class CircleTests(unittest.TestCase): + + """Tests for Circle.""" + + def test_radius(self): + circle = Circle(5) + self.assertEqual(circle.radius, 5) + + def test_default_radius(self): + circle = Circle() + self.assertEqual(circle.radius, 1) + + def test_diameter(self): + circle = Circle(2) + self.assertEqual(circle.diameter, 4) + + def test_area(self): + circle = Circle(2) + self.assertEqual(circle.area, math.pi * 4) + circle = Circle(1) + self.assertEqual(circle.area, math.pi) + + def test_string_representation(self): + circle = Circle(2) + self.assertEqual(str(circle), "Circle(2)") + self.assertEqual(repr(circle), "Circle(2)") + circle.radius = 1 + self.assertEqual(repr(circle), "Circle(1)") + + # To test the Bonus part of this exercise, comment out the following line + # @unittest.expectedFailure + def test_diameter_and_area_change_based_on_radius(self): + circle = Circle(2) + self.assertEqual(circle.diameter, 4) + circle.radius = 3 + self.assertEqual(circle.diameter, 6) + self.assertEqual(circle.area, math.pi * 9) + + # To test the Bonus part of this exercise, comment out the following line + # @unittest.expectedFailure + def test_diameter_changeable_but_area_not(self): + circle = Circle(2) + self.assertEqual(circle.diameter, 4) + self.assertEqual(circle.area, math.pi * 4) + circle.diameter = 3 + self.assertEqual(circle.radius, 1.5) + with self.assertRaises(AttributeError): + circle.area = 3 + + # To test the Bonus part of this exercise, comment out the following line + # @unittest.expectedFailure + def test_no_negative_radius(self): + with self.assertRaises(ValueError) as context: + circle = Circle(-2) + self.assertEqual(str(context.exception).lower(), "radius cannot be negative") + circle = Circle(2) + with self.assertRaises(ValueError) as context: + circle.radius = -10 + self.assertEqual(str(context.exception).lower(), "radius cannot be negative") + with self.assertRaises(ValueError): + circle.diameter = -20 + self.assertEqual(circle.radius, 2) + + +if __name__ == "__main__": + unittest.main(verbosity=2)