From 71df33b20e270a8de19f33caff772ce53b94393a Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 20 Sep 2025 11:26:09 +0300 Subject: [PATCH 01/25] Solution Task 1 --- solutions/lesson02/task1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/solutions/lesson02/task1.py b/solutions/lesson02/task1.py index c782dcd8..6f294e39 100644 --- a/solutions/lesson02/task1.py +++ b/solutions/lesson02/task1.py @@ -1,4 +1,5 @@ def get_factorial(num: int) -> int: factorial = 1 - # ваш код + for i in range(num): + factorial*=i return factorial From 346f3cd724a52274ece297abe1e02b6cc22b9649 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Wed, 24 Sep 2025 10:15:26 +0300 Subject: [PATCH 02/25] Homework for 27.09.2025 --- .coverage | Bin 0 -> 53248 bytes .github/workflows/trying.ipynb | 154 +++++++++++++++++++++++++++++++++ solutions/lesson02/task1.py | 4 +- solutions/lesson02/task2.py | 5 +- solutions/lesson02/task3.py | 8 +- solutions/lesson02/task4.py | 10 ++- solutions/lesson02/task5.py | 8 +- solutions/lesson02/task6.py | 22 ++++- solutions/lesson02/task7.py | 10 ++- tests/test_lesson02_tasks.py | 16 ++-- 10 files changed, 217 insertions(+), 20 deletions(-) create mode 100644 .coverage create mode 100644 .github/workflows/trying.ipynb diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..39bd56607f64ce65e3f38886373139b6aca26e45 GIT binary patch literal 53248 zcmeI)&5sjh90%~3PCL_C+8vaTX0svtB)%*gb{7^D1QQUE0|5i*#sjPj)Arfjk#=U8 znF5<=uwX)>@uWxNAE0M%dh&qrVz`-Te7P7r_?GCwV9?)hUbZc;ghLw^{7$x=>C8O8 zKF?>Kmp<*bFCRbRMpDfCLDPxE4rRTfs>*XhD2igztxUIUF=!*7J)pJPwY}P=sZ5`H zr)<5Vl=OEM>s0xuHC6hc{N%c8rKb7zx(^Mbm5~wk(i@Re zZOABaYBEUf%LiUMxPSJbnBD*Uk%J=HCpHfWTBfGNei8WR#gYufyxWk%_3CcTiCk|% zM2j*zyA{eh4|KGThB`iRI!@19wp*uEkzAkFfMq|X+~3sJIzf=GA4 z<1@tOH2H)G#UZ1zVS{=_#$|GX8vU*7DGlxD6y?x71*-W$ zopWCe9Iv)0!%5++l(6PgJ}*Q`X#>BxHS$Hn_2OcBp&PlrC*%cLYelkt#{y0=TDA~g zcWEfrX)e&|BxU+sT{@k_cSxC@o+h5B%z1vs+DXVyO^M??d#z@b$X{zUm-ymPisXqS zf3=jWrH%Zwu=jjz?r<$TT}E9HgoY=d5J*-k6+Xox`MPVMh>PF$phS^hTR(7va zb@=$|^@dwXZzbVY)MyBQK5jCcS|k^TYIAp+*_vTE0XH<^rzcWQ?`v)2%u*giwc%HL z>snl6LP-sT`hx7V`IcAXrV?@)YW_JHI16$*J(_e2_q!rd)tNE%%E7VKs?JIkuxC~_ zhuR~$Q5hXoFPU*QO0QPBGJ2{|b|vr7jb?|M(g|BZ){)$;lgv_dddaMfq(`VYQ-)sI zKe}3R((~+@bfefd@+uAV&hbn=@7=seEs>8y$PbZXwm{4jKBSmj|W+>`AUOlD0|B-Ui4GX`^QPE&zZ2Z6#a(J#wHZcR=t`b~M>56;Ymexnut9>iQjhN16m+ddaL;hA0Z zmGTqC!NlIlv5mpRewt(JgNgkV$1V&e_70AnA584+99tVq>=}yvYK~&>>u(*jZP#-n z+otfdDutiwOZcv+{Q19G`a-cjwvG({KK%ahGv#l~ZK0uX=z1Rwwb2tWV=5P$##`X!*Lno{P^ z|5fWBh5oQW00Izz00bZa0SG_<0uX=z1R(Gb3TRr{jKBYHeWF-@TYp$TTR&P~TVGjM ztTt`qARee_z)P-eY`c=(Jw1 z-yAavyixen((`;Ie^b|Zt?kuwyw0fwHE){3YW)3w>vP4rVf|_SV*O-&PwxO+v%aKV zSReoa2tWV=5P$##AOHafKmY;|SUUnd int:\n", + " multiplications_amount = 0\n", + " if num == 1:\n", + " return 0\n", + " while num > 1:\n", + " if num % 2 != 0:\n", + " multiplications_amount += 1\n", + " num -= 1\n", + " continue\n", + " multiplications_amount += 1\n", + " num //= 2\n", + " return multiplications_amount\n", + "\n", + "\n", + "print(get_multiplications_amount(999))" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fd7d5d7d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "21\n" + ] + } + ], + "source": [ + "def get_gcd(num1: int, num2: int) -> int:\n", + " while max(num1, num2) % min(num1, num2) != 0:\n", + " if num1 > num2:\n", + " num1 = num1 % num2\n", + " else:\n", + " num2 = num2 % num1\n", + " return min(num1, num2)\n", + "\n", + "\n", + "print(get_gcd(1071, 147))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2a62ad6c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{2, 3}\n", + "5\n" + ] + } + ], + "source": [ + "def IsPrime(num: int) -> bool:\n", + " d = 2\n", + " while num % d != 0:\n", + " d += 1\n", + " return d == num\n", + "\n", + "\n", + "def get_sum_of_prime_divisors(num: int) -> int:\n", + " sum_of_divisors = 0\n", + " divisors = []\n", + " i = 2\n", + " while num != 1:\n", + " if num % i == 0:\n", + " if IsPrime(i):\n", + " divisors.append(i)\n", + " num //= i\n", + " i = 2\n", + "\n", + " else:\n", + " i += 1\n", + " divisors = set(divisors)\n", + " for i in divisors:\n", + " sum_of_divisors += i\n", + " return sum_of_divisors\n", + "\n", + "\n", + "print(get_sum_of_prime_divisors(12))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "4fd42f4f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n" + ] + } + ], + "source": [ + "def is_palindrome(num: int) -> bool:\n", + " num_reversed = str(num)\n", + " num_reversed = num_reversed[::-1]\n", + " num_reversed = int(num_reversed)\n", + " return num == num_reversed\n", + "\n", + "\n", + "print(is_palindrome(13))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.13.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/solutions/lesson02/task1.py b/solutions/lesson02/task1.py index 6f294e39..f344e470 100644 --- a/solutions/lesson02/task1.py +++ b/solutions/lesson02/task1.py @@ -1,5 +1,5 @@ def get_factorial(num: int) -> int: factorial = 1 - for i in range(num): - factorial*=i + for i in range(num, 1, -1): + factorial *= i return factorial diff --git a/solutions/lesson02/task2.py b/solutions/lesson02/task2.py index b91420c5..e96073ee 100644 --- a/solutions/lesson02/task2.py +++ b/solutions/lesson02/task2.py @@ -1,4 +1,7 @@ def get_doubled_factorial(num: int) -> int: factorial = 1 - # ваш код + for i in range(num, 0, -2): + if i == 0 or i == 1: + break + factorial *= i return factorial diff --git a/solutions/lesson02/task3.py b/solutions/lesson02/task3.py index ee2a84ec..d0ccbcf3 100644 --- a/solutions/lesson02/task3.py +++ b/solutions/lesson02/task3.py @@ -1,4 +1,10 @@ def get_amount_of_ways_to_climb(stair_amount: int) -> int: step_prev, step_curr = 1, 1 - # ваш код + if stair_amount == 1: + return step_curr + else: + for i in range(2, stair_amount + 1): + temp = step_curr + step_curr = step_curr + step_prev + step_prev = temp return step_curr diff --git a/solutions/lesson02/task4.py b/solutions/lesson02/task4.py index 45ff4bb4..7bbe6132 100644 --- a/solutions/lesson02/task4.py +++ b/solutions/lesson02/task4.py @@ -1,4 +1,12 @@ def get_multiplications_amount(num: int) -> int: multiplications_amount = 0 - # ваш код + if num == 1: + return 0 + while num > 1: + if num % 2 != 0: + multiplications_amount += 1 + num -= 1 + continue + multiplications_amount += 1 + num //= 2 return multiplications_amount diff --git a/solutions/lesson02/task5.py b/solutions/lesson02/task5.py index 8fb9a048..f4cb43c6 100644 --- a/solutions/lesson02/task5.py +++ b/solutions/lesson02/task5.py @@ -1,3 +1,7 @@ def get_gcd(num1: int, num2: int) -> int: - # ваш код - return num1 + while max(num1, num2) % min(num1, num2) != 0: + if num1 > num2: + num1 = num1 % num2 + else: + num2 = num2 % num1 + return min(num1, num2) diff --git a/solutions/lesson02/task6.py b/solutions/lesson02/task6.py index bec4b6cd..369a11a1 100644 --- a/solutions/lesson02/task6.py +++ b/solutions/lesson02/task6.py @@ -1,4 +1,24 @@ +def IsPrime(num: int) -> bool: + d = 2 + while num % d != 0: + d += 1 + return d == num + + def get_sum_of_prime_divisors(num: int) -> int: sum_of_divisors = 0 - # ваш код + divisors = [] + i = 2 + while num != 1: + if num % i == 0: + if IsPrime(i): + divisors.append(i) + num //= i + i = 2 + + else: + i += 1 + divisors = set(divisors) + for i in divisors: + sum_of_divisors += i return sum_of_divisors diff --git a/solutions/lesson02/task7.py b/solutions/lesson02/task7.py index 4b2d73be..5a1a2e9e 100644 --- a/solutions/lesson02/task7.py +++ b/solutions/lesson02/task7.py @@ -1,5 +1,7 @@ def is_palindrome(num: int) -> bool: - num_reversed = 0 - num_origin = num - # ваш код - return num_origin == num_reversed + if num < 0: + return False + num_reversed = str(abs(num)) + num_reversed = num_reversed[::-1] + num_reversed = int(num_reversed) + return num == num_reversed diff --git a/tests/test_lesson02_tasks.py b/tests/test_lesson02_tasks.py index ae134994..6a2b8f87 100644 --- a/tests/test_lesson02_tasks.py +++ b/tests/test_lesson02_tasks.py @@ -10,7 +10,7 @@ @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 0, @@ -44,7 +44,7 @@ def test_get_factorial(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 0, @@ -83,7 +83,7 @@ def test_get_doubled_factorial(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -125,7 +125,7 @@ def test_get_amount_of_ways_to_climb( @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -162,7 +162,7 @@ def test_get_multiplications_amount( @pytest.mark.parametrize( - "num1, num2, result_expected", + "num1, num2, result_expected", ( pytest.param( 1, @@ -229,7 +229,7 @@ def test_get_gcd( @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( 1, @@ -273,10 +273,10 @@ def test_get_sum_of_prime_divisors(num: int, result_expected: int) -> None: @pytest.mark.parametrize( - "num, result_expected", + "num, result_expected", ( pytest.param( - -10**10, + -(10**10), False, id="negative-ten-billion", ), From 387781ff7395f4d8aec5a0921e4b112bf31123d9 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Wed, 24 Sep 2025 10:25:54 +0300 Subject: [PATCH 03/25] Commented task 3 --- solutions/lesson02/task3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/solutions/lesson02/task3.py b/solutions/lesson02/task3.py index d0ccbcf3..ee47a316 100644 --- a/solutions/lesson02/task3.py +++ b/solutions/lesson02/task3.py @@ -7,4 +7,5 @@ def get_amount_of_ways_to_climb(stair_amount: int) -> int: temp = step_curr step_curr = step_curr + step_prev step_prev = temp + #Stair amount equals to element with index n+1 of fibonacci sequence return step_curr From 0d6b3e4cfb4d20e1113483a3e8506e5ef409512c Mon Sep 17 00:00:00 2001 From: Shanechka Date: Wed, 24 Sep 2025 10:26:52 +0300 Subject: [PATCH 04/25] Ruff reform Comment task 3 --- solutions/lesson02/task3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solutions/lesson02/task3.py b/solutions/lesson02/task3.py index ee47a316..280a3be3 100644 --- a/solutions/lesson02/task3.py +++ b/solutions/lesson02/task3.py @@ -7,5 +7,5 @@ def get_amount_of_ways_to_climb(stair_amount: int) -> int: temp = step_curr step_curr = step_curr + step_prev step_prev = temp - #Stair amount equals to element with index n+1 of fibonacci sequence + # Stair amount equals to element with index n+1 of fibonacci sequence return step_curr From a5158465c8a51b3540b6223d3603853169b438a5 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 26 Sep 2025 01:52:35 +0300 Subject: [PATCH 05/25] Homework 26.09.2025 no lists fix --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 50 ++++++++++++++++++--------------- solutions/lesson02/task6.py | 27 ++++++------------ solutions/lesson02/task7.py | 11 ++++++-- 4 files changed, 43 insertions(+), 45 deletions(-) diff --git a/.coverage b/.coverage index 39bd56607f64ce65e3f38886373139b6aca26e45..16b14145184f49022d7cd25db19eacf87b1ed257 100644 GIT binary patch delta 68 zcmZozz}&Ead4qTZ^FIEqn*{}C^Xss4vM_QAaQ>5L=KwJ{{@Js!0~vhm_t@D$0&GAT PRuF^r9{1*l{qha~Jnj#9 delta 68 zcmZozz}&Ead4qTZ^Ira~n*{}C^Xss2vM_S;aoTZk0BIqPfB$RP*g+!f_t@Ehv;f;b Rd3IJ1gLM!8=7;_A4gf&`54QjS diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 7333c56a..16dad942 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "08c46c22", "metadata": {}, "outputs": [ @@ -10,26 +10,25 @@ "name": "stdout", "output_type": "stream", "text": [ - "16\n" + "9999999999\n" ] } ], "source": [ - "def get_multiplications_amount(num: int) -> int:\n", - " multiplications_amount = 0\n", - " if num == 1:\n", - " return 0\n", - " while num > 1:\n", - " if num % 2 != 0:\n", - " multiplications_amount += 1\n", - " num -= 1\n", - " continue\n", - " multiplications_amount += 1\n", - " num //= 2\n", - " return multiplications_amount\n", + "def get_sum_of_prime_divisors(num: int) -> int:\n", + " n = num\n", + " sum_of_divisors = 0\n", + " for i in range(2, int(num**0.5) + 1):\n", + " if num % i == 0:\n", + " sum_of_divisors += i\n", + " while num % i == 0:\n", + " num //= i\n", + " if sum_of_divisors == 0:\n", + " return n\n", + " return sum_of_divisors\n", "\n", "\n", - "print(get_multiplications_amount(999))" + "print(get_sum_of_prime_divisors(10000000000 - 1))" ] }, { @@ -106,7 +105,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 8, "id": "4fd42f4f", "metadata": {}, "outputs": [ @@ -114,19 +113,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "False\n" + "True\n" ] } ], "source": [ "def is_palindrome(num: int) -> bool:\n", - " num_reversed = str(num)\n", - " num_reversed = num_reversed[::-1]\n", - " num_reversed = int(num_reversed)\n", + " if num < 0:\n", + " return False\n", + " num_reversed = 0\n", + " num_reversed += num%10\n", + " num_current = num\n", + " num_current //= 10\n", + " while num_current > 0:\n", + " num_reversed *= 10\n", + " num_reversed += num_current % 10\n", + " num_current //= 10\n", " return num == num_reversed\n", - "\n", - "\n", - "print(is_palindrome(13))" + "print(is_palindrome(121))" ] } ], diff --git a/solutions/lesson02/task6.py b/solutions/lesson02/task6.py index 369a11a1..1896ca12 100644 --- a/solutions/lesson02/task6.py +++ b/solutions/lesson02/task6.py @@ -1,24 +1,13 @@ -def IsPrime(num: int) -> bool: - d = 2 - while num % d != 0: - d += 1 - return d == num - - def get_sum_of_prime_divisors(num: int) -> int: + n = num + if n == 1: + return 0 sum_of_divisors = 0 - divisors = [] - i = 2 - while num != 1: + for i in range(2, int(num**0.5) + 1): if num % i == 0: - if IsPrime(i): - divisors.append(i) + sum_of_divisors += i + while num % i == 0: num //= i - i = 2 - - else: - i += 1 - divisors = set(divisors) - for i in divisors: - sum_of_divisors += i + if sum_of_divisors == 0: + return n return sum_of_divisors diff --git a/solutions/lesson02/task7.py b/solutions/lesson02/task7.py index 5a1a2e9e..d97d936d 100644 --- a/solutions/lesson02/task7.py +++ b/solutions/lesson02/task7.py @@ -1,7 +1,12 @@ def is_palindrome(num: int) -> bool: if num < 0: return False - num_reversed = str(abs(num)) - num_reversed = num_reversed[::-1] - num_reversed = int(num_reversed) + num_reversed = 0 + num_reversed += num % 10 + num_current = num + num_current //= 10 + while num_current > 0: + num_reversed *= 10 + num_reversed += num_current % 10 + num_current //= 10 return num == num_reversed From 8981f15358f99b852d9aa70de125af4b8385935f Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 10 Oct 2025 19:22:38 +0300 Subject: [PATCH 06/25] Homework for 11.10.2025 --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 146 ++++++++++++++++++++++++++++++++- solutions/lesson03/task1.py | 28 ++++++- solutions/lesson03/task2.py | 24 +++++- solutions/lesson03/task3.py | 13 ++- 5 files changed, 204 insertions(+), 7 deletions(-) diff --git a/.coverage b/.coverage index 16b14145184f49022d7cd25db19eacf87b1ed257..d4c372535e9e2287aeaddd34b988d661751938f5 100644 GIT binary patch delta 326 zcmZozz}&Eac>{}s3=j8a2L4t2-hA)*Ci9u|-sY|6RpvR)lg-1=y?L{sz+`TwD4xlU zz5zVOF(rw`*~WSWl~LSC0!APKE+hd%kN_u8AZ)X|?^7nA1zQ;SSM&Suec+qIXTf`i zw}DrM=LAm6;%?0F#Z83;AaNjjrIAW#?gGNiw>1C&c`jrr delta 151 zcmZozz}&Eac>{}s7$?th2L4t2-hA)*Ci9u|-sY|6RpvRqSx{gt4^tH91%H-*oF_YQ9ZuL{ozpt^NDlea_%ZkCLFtt7_DzmP^MB!g%m0l3K2XI){!{#if$}T)Hy@sFYXAVF Cv^8k} diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 16dad942..1f9dde58 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -122,7 +122,7 @@ " if num < 0:\n", " return False\n", " num_reversed = 0\n", - " num_reversed += num%10\n", + " num_reversed += num % 10\n", " num_current = num\n", " num_current //= 10\n", " while num_current > 0:\n", @@ -130,8 +130,152 @@ " num_reversed += num_current % 10\n", " num_current //= 10\n", " return num == num_reversed\n", + "\n", + "\n", "print(is_palindrome(121))" ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "047df546", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1001\n" + ] + } + ], + "source": [ + "num = 9\n", + "answer = 0\n", + "position = 1\n", + "while num > 0:\n", + " answer = answer + position * (num % 2)\n", + " position *= 10\n", + " num //= 2\n", + "print(answer)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8b93d7a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1001\n", + "9\n" + ] + }, + { + "ename": "", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[1;31mThe Kernel crashed while executing code in the current cell or a previous cell. \n", + "\u001b[1;31mPlease review the code in the cell(s) to identify a possible cause of the failure. \n", + "\u001b[1;31mClick here for more info. \n", + "\u001b[1;31mView Jupyter log for further details." + ] + } + ], + "source": [ + "def flip_bits_in_range(num: int, left_bit: int, right_bit: int) -> int:\n", + " answer = 0\n", + " position = 1\n", + " curr_bit = 1\n", + " while num > 0:\n", + " if curr_bit >= left_bit and curr_bit <= right_bit:\n", + " if num % 2 == 1:\n", + " answer = answer + position * 0\n", + " else:\n", + " answer = answer + position * 1\n", + " else:\n", + " answer = answer + position * (num % 2)\n", + " position *= 10\n", + " num //= 2\n", + " curr_bit += 1\n", + " num = 0\n", + " power = 0\n", + " print(answer)\n", + " while answer > 0:\n", + " digit = answer % 10\n", + " num += digit * (2**power)\n", + " answer //= 10\n", + " power += 1\n", + " return num\n", + "\n", + "\n", + "print(flip_bits_in_range(0b1011, 2, 2))" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "693fdb92", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def get_nth_digit(num: int) -> int:\n", + " count_digit = 1\n", + " flag_finish = False\n", + " answer = 0\n", + " i = 0\n", + " while True:\n", + " k = i * 2\n", + " while k > 0:\n", + " if num == count_digit:\n", + " answer = k % 10\n", + " flag_finish = True\n", + " break\n", + " k //= 10\n", + " count_digit += 1\n", + " if flag_finish:\n", + " break\n", + " i += 1\n", + " return answer\n", + "\n", + "\n", + "get_nth_digit(5)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "570f59c2", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "object of type 'int' has no len()", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[31], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m100\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[1;31mTypeError\u001b[0m: object of type 'int' has no len()" + ] + } + ], + "source": [] } ], "metadata": { diff --git a/solutions/lesson03/task1.py b/solutions/lesson03/task1.py index f1d8fe26..a403cb56 100644 --- a/solutions/lesson03/task1.py +++ b/solutions/lesson03/task1.py @@ -1,3 +1,27 @@ def flip_bits_in_range(num: int, left_bit: int, right_bit: int) -> int: - # ваш код - return num \ No newline at end of file + answer = 0 + position = 1 + curr_bit = 1 + while num > 0: + if curr_bit >= left_bit and curr_bit <= right_bit: + if num % 2 == 1: + answer = answer + position * 0 + else: + answer = answer + position * 1 + else: + answer = answer + position * (num % 2) + position *= 10 + num //= 2 + curr_bit += 1 + while curr_bit <= right_bit: + answer = answer + position * 1 + position *= 10 + curr_bit += 1 + num = 0 + power = 0 + while answer > 0: + digit = answer % 10 + num += digit * (2**power) + answer //= 10 + power += 1 + return num diff --git a/solutions/lesson03/task2.py b/solutions/lesson03/task2.py index a3a738c2..b9d835d5 100644 --- a/solutions/lesson03/task2.py +++ b/solutions/lesson03/task2.py @@ -1,3 +1,23 @@ def get_cube_root(n: float, eps: float) -> float: - # ваш код - return n \ No newline at end of file + if n < 0: + return -get_cube_root(-n, eps) + if n >= 1: + low_border, high_border = 0, n + else: + low_border, high_border = 0, 1 + while ( + abs( + ((low_border + high_border) / 2) + * ((low_border + high_border) / 2) + * ((low_border + high_border) / 2) + - n + ) + >= eps + ): + mid = (low_border + high_border) / 2 + mid_cube = mid * mid * mid + if mid_cube < n: + low_border = mid + else: + high_border = mid + return (low_border + high_border) / 2 diff --git a/solutions/lesson03/task3.py b/solutions/lesson03/task3.py index 5e91a6ac..95f70473 100644 --- a/solutions/lesson03/task3.py +++ b/solutions/lesson03/task3.py @@ -1,3 +1,12 @@ def get_nth_digit(num: int) -> int: - # ваш код - return 0 + kolv_cifr = 0 + digit_sum = 5 + if num <= digit_sum: + return (num - 1) * 2 + while num > digit_sum: + digit_sum += 45 * (10**kolv_cifr) * (kolv_cifr + 2) + kolv_cifr += 1 + k = num - (digit_sum - 45 * (10 ** (kolv_cifr - 1)) * (kolv_cifr + 1)) - 1 + k1 = k // (kolv_cifr + 1) + finall_humber = 10**kolv_cifr + k1 * 2 + return (finall_humber // (10 ** ((kolv_cifr) - (k % (kolv_cifr + 1))))) % 10 From a0774cd40e3dc5122a9bbdf51018a30e0b3b978a Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 17 Oct 2025 23:48:32 +0300 Subject: [PATCH 07/25] 18.10.2025 HomeWork --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 47 ++++++-- solutions/lesson04/task1.py | 13 +- solutions/lesson04/task2.py | 22 +++- solutions/lesson04/task3.py | 6 +- solutions/lesson04/task4.py | 10 +- solutions/lesson04/task5.py | 14 ++- solutions/lesson04/task6.py | 16 ++- tests/test_lesson04_tasks.py | 214 ++++++++++++++++----------------- 9 files changed, 209 insertions(+), 133 deletions(-) diff --git a/.coverage b/.coverage index d4c372535e9e2287aeaddd34b988d661751938f5..dad4699d2590b6689b9987d8c41599fa9fecce2c 100644 GIT binary patch delta 538 zcmYk2J4*vW6osP+S$F4gX6FxB#3ERTZ>?0&Mogg=8e%}xs)eA9AebUuM8Orr%5;K) z2pc;|V-c|tJ3&!wd=#RBO^B?{EQnK`d%nwXKPF#_=1b9&*icg1#Rv{D#^1Te``9g8 zX9-%R3lzjB(@x#lcZTg}yXd=O)^5y5&rOZf?(9l#d7-y$X*J^-php8?fNl+7fG!QV z0Xj8c2I$a$Dlp}fR4uCfHX=AeihuAO-p}q>p0&^mx<~~nleF{gl$;U!#okbUbGBEW znIQji(i7RguH9UtYc*kXZ3d%jF>ZA2#Z;g6Nw$_qO5`G_;S;ZT#0{=cMFpqoBTLxD zCJI=?GUky}T0I!GWURp!G8nX6J-9)iCG;SIXdG(b7--@uB@ccEET&g1Xtt;xXwck5 z^o~?2_uk=FM%^w%P{$YE@Pr3-{Ti1z$C delta 194 zcmWm7u?j&^9LMp?CF%Un`CVpZ-~numc6VV=n=A&h$fhV+ zltCzq6prmTd_Vo1zUlPMaVVc4rVI>G5H}%3mhX9sr)Wua0#hnxNfxY 1\u001b[0m \u001b[38;5;28;43mlen\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m100\u001b[39;49m\u001b[43m)\u001b[49m\n", - "\u001b[1;31mTypeError\u001b[0m: object of type 'int' has no len()" + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1, 4]]\n" ] } ], + "source": [ + "def merge_intervals(intervals: list[list[int, int]]) -> list[list[int, int]]:\n", + " intervals.sort(key=lambda x: x[0])\n", + " if len(intervals) == 0:\n", + " return []\n", + " newintervals = []\n", + " start, finish = intervals[0][0], intervals[0][1]\n", + " flag1 = True\n", + " for i in intervals:\n", + " if flag1:\n", + " flag1 = False\n", + " continue # скип первого элемента(костыль)\n", + " if start < i[0] and finish > i[1]:\n", + " continue\n", + " elif finish >= i[0] and start <= i[0]:\n", + " finish = i[1]\n", + " else:\n", + " newintervals.append([start, finish])\n", + " start = i[0]\n", + " finish = i[1]\n", + " newintervals.append([start, finish])\n", + " return newintervals\n", + "\n", + "\n", + "print(merge_intervals([[1, 4], [2, 3], [1, 4]]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb839bd5", + "metadata": {}, + "outputs": [], "source": [] } ], diff --git a/solutions/lesson04/task1.py b/solutions/lesson04/task1.py index 47384423..075af3a5 100644 --- a/solutions/lesson04/task1.py +++ b/solutions/lesson04/task1.py @@ -1,3 +1,12 @@ def is_arithmetic_progression(lst: list[list[int]]) -> bool: - # ваш код - return False \ No newline at end of file + lst.sort() + if len(lst) in [0, 1]: + return True + flag = True + change = lst[1] - lst[0] + i = 1 + while flag and i < len(lst): + if lst[i] - lst[i - 1] != change: + flag = False + i += 1 + return flag diff --git a/solutions/lesson04/task2.py b/solutions/lesson04/task2.py index 4591d0a3..0a552647 100644 --- a/solutions/lesson04/task2.py +++ b/solutions/lesson04/task2.py @@ -1,3 +1,21 @@ def merge_intervals(intervals: list[list[int, int]]) -> list[list[int, int]]: - # ваш код - return [[0,0]] \ No newline at end of file + intervals.sort(key=lambda x: x[0]) + if len(intervals) == 0: + return [] + newintervals = [] + start, finish = intervals[0][0], intervals[0][1] + flag1 = True + for i in intervals: + if flag1: + flag1 = False + continue # скип первого элемента(костыль) + if start < i[0] and finish > i[1]: + continue + elif finish >= i[0] and start <= i[0]: + finish = i[1] + else: + newintervals.append([start, finish]) + start = i[0] + finish = i[1] + newintervals.append([start, finish]) + return newintervals diff --git a/solutions/lesson04/task3.py b/solutions/lesson04/task3.py index 7253f6cb..68802acc 100644 --- a/solutions/lesson04/task3.py +++ b/solutions/lesson04/task3.py @@ -1,3 +1,5 @@ def find_single_number(nums: list[int]) -> int: - # ваш код - return 0 + result = 0 + for num in nums: + result ^= num + return result diff --git a/solutions/lesson04/task4.py b/solutions/lesson04/task4.py index b21bc5a3..d22c3694 100644 --- a/solutions/lesson04/task4.py +++ b/solutions/lesson04/task4.py @@ -1,3 +1,9 @@ def move_zeros_to_end(nums: list[int]) -> list[int]: - # ваш код - return 0 \ No newline at end of file + index = 0 + for i in range(len(nums)): + if nums[i] != 0: + nums[index] = nums[i] + index += 1 + for i in range(index, len(nums)): + nums[i] = 0 + return index diff --git a/solutions/lesson04/task5.py b/solutions/lesson04/task5.py index 02d7742b..85c40eec 100644 --- a/solutions/lesson04/task5.py +++ b/solutions/lesson04/task5.py @@ -1,3 +1,13 @@ def find_row_with_most_ones(matrix: list[list[int]]) -> int: - # ваш код - return 0 \ No newline at end of file + if not matrix or not matrix[0]: + return 0 + row = 0 + col = len(matrix[0]) - 1 + result_index = 0 + while row < len(matrix) and col >= 0: + if matrix[row][col] == 1: + result_index = row + col -= 1 + else: + row += 1 + return result_index diff --git a/solutions/lesson04/task6.py b/solutions/lesson04/task6.py index 16df27ca..00c5d5ad 100644 --- a/solutions/lesson04/task6.py +++ b/solutions/lesson04/task6.py @@ -1,3 +1,13 @@ -def count_cycles(arr: list[int]) -> int: - # ваш код - return 0 \ No newline at end of file +def count_cycles(arr: list[int]) -> int: + if not arr: + return 0 + visited = [False] * len(arr) + cycle_count = 0 + for i in range(len(arr)): + if not visited[i]: + cycle_count += 1 + current = i + while not visited[current]: + visited[current] = True + current = arr[current] + return cycle_count diff --git a/tests/test_lesson04_tasks.py b/tests/test_lesson04_tasks.py index c8278329..58567b8d 100644 --- a/tests/test_lesson04_tasks.py +++ b/tests/test_lesson04_tasks.py @@ -1,4 +1,4 @@ -import pytest +import pytest import random from solutions.lesson04.task1 import is_arithmetic_progression @@ -8,62 +8,89 @@ from solutions.lesson04.task5 import find_row_with_most_ones from solutions.lesson04.task6 import count_cycles -@pytest.mark.parametrize("lst, expected", [ - pytest.param([], True, id="empty_list"), - pytest.param([5], True, id="single_element"), - pytest.param([1, 3], True, id="two_elements"), - pytest.param([3, 1], True, id="two_elements_unsorted"), - pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), - pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), - pytest.param([1, 2, 4], False, id="not_ap"), - pytest.param([10, 5, 0, -5], True, id="negative_difference"), - pytest.param([1, 1, 1, 1], True, id="constant_sequence"), - pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), - pytest.param([0, 0, 1], False, id="two_same_one_different"), - pytest.param([10**5 + i*10**2 for i in range(1000)], True, id="long_list_true"), - pytest.param([10**5 + i*10**2 for i in range(999)] + [1], False, id="long_list_false"), -]) + +@pytest.mark.parametrize( + "lst, expected", + [ + pytest.param([], True, id="empty_list"), + pytest.param([5], True, id="single_element"), + pytest.param([1, 3], True, id="two_elements"), + pytest.param([3, 1], True, id="two_elements_unsorted"), + pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), + pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), + pytest.param([1, 2, 4], False, id="not_ap"), + pytest.param([10, 5, 0, -5], True, id="negative_difference"), + pytest.param([1, 1, 1, 1], True, id="constant_sequence"), + pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), + pytest.param([0, 0, 1], False, id="two_same_one_different"), + pytest.param([10**5 + i * 10**2 for i in range(1000)], True, id="long_list_true"), + pytest.param([10**5 + i * 10**2 for i in range(999)] + [1], False, id="long_list_false"), + ], +) def test_is_arithmetic_progression_parametrized(lst, expected): if len(lst) > 500: random.shuffle(lst) assert is_arithmetic_progression(lst) == expected -@pytest.mark.parametrize("intervals, expected", [ - pytest.param([], [], id="empty"), - pytest.param([[1, 3]], [[1, 3]], id="single_interval"), - pytest.param([[10, 13], [1, 3], [2, 6], [8, 10], [15, 18]], [[1, 6], [8, 13], [15, 18]], id="classic_merge"), - pytest.param([[1, 4], [4, 5]], [[1, 5]], id="touching_intervals"), - pytest.param([[1, 4], [2, 3]], [[1, 4]], id="nested_interval"), - pytest.param([[5, 7], [1, 3], [15, 20], [0, 0], [2, 4], [6, 10], [0, 2]], [[0, 4], [5, 10], [15, 20]], id="unsorted_input"), - pytest.param([[1, 2], [3, 4], [5, 6]], [[1, 2], [3, 4], [5, 6]], id="no_overlap"), - pytest.param([[1, 10], [2, 3], [4, 5], [6, 7]], [[1, 10]], id="all_merged"), -]) +@pytest.mark.parametrize( + "intervals, expected", + [ + pytest.param([], [], id="empty"), + pytest.param([[1, 3]], [[1, 3]], id="single_interval"), + pytest.param( + [[10, 13], [1, 3], [2, 6], [8, 10], [15, 18]], + [[1, 6], [8, 13], [15, 18]], + id="classic_merge", + ), + pytest.param([[1, 4], [4, 5]], [[1, 5]], id="touching_intervals"), + pytest.param([[1, 4], [2, 3]], [[1, 4]], id="nested_interval"), + pytest.param( + [[5, 7], [1, 3], [15, 20], [0, 0], [2, 4], [6, 10], [0, 2]], + [[0, 4], [5, 10], [15, 20]], + id="unsorted_input", + ), + pytest.param([[1, 2], [3, 4], [5, 6]], [[1, 2], [3, 4], [5, 6]], id="no_overlap"), + pytest.param([[1, 10], [2, 3], [4, 5], [6, 7]], [[1, 10]], id="all_merged"), + ], +) def test_merge_intervals(intervals, expected): assert merge_intervals(intervals) == expected -@pytest.mark.parametrize("nums, expected", [ - pytest.param([2, 2, 1], 1, id="simple_case"), - pytest.param([4, 1, 2, 1, 2], 4, id="middle_single"), - pytest.param([1], 1, id="single_element"), - pytest.param([100, 200, 300, 200, 100], 300, id="large_numbers"), - pytest.param([0, 1, 0], 1, id="with_zero"), - pytest.param([7, 8, 9, 8, 7], 9, id="unsorted"), - pytest.param([i + 10**5 for i in range(500)] + [i + 10**5 for i in range(500)] + [69], 69, id="long_list"), -]) + +@pytest.mark.parametrize( + "nums, expected", + [ + pytest.param([2, 2, 1], 1, id="simple_case"), + pytest.param([4, 1, 2, 1, 2], 4, id="middle_single"), + pytest.param([1], 1, id="single_element"), + pytest.param([100, 200, 300, 200, 100], 300, id="large_numbers"), + pytest.param([0, 1, 0], 1, id="with_zero"), + pytest.param([7, 8, 9, 8, 7], 9, id="unsorted"), + pytest.param( + [i + 10**5 for i in range(500)] + [i + 10**5 for i in range(500)] + [69], + 69, + id="long_list", + ), + ], +) def test_find_single_number(nums, expected): assert find_single_number(nums) == expected -@pytest.mark.parametrize("input_list, expected_list, expected_index", [ - pytest.param([0, 1, 0, 3, 12], [1, 3, 12, 0, 0], 3, id="basic"), - pytest.param([0, 0, 1], [1, 0, 0], 1, id="zeros_first"), - pytest.param([1, 2, 3], [1, 2, 3], 3, id="no_zeros"), - pytest.param([0, 0, 0], [0, 0, 0], 0, id="all_zeros"), - pytest.param([1, 0, 2, 0, 3, 0], [1, 2, 3, 0, 0, 0], 3, id="interleaved"), - pytest.param([], [], 0, id="empty"), - pytest.param([0], [0], 0, id="single_zero"), - pytest.param([42], [42], 1, id="single_nonzero"), -]) + +@pytest.mark.parametrize( + "input_list, expected_list, expected_index", + [ + pytest.param([0, 1, 0, 3, 12], [1, 3, 12, 0, 0], 3, id="basic"), + pytest.param([0, 0, 1], [1, 0, 0], 1, id="zeros_first"), + pytest.param([1, 2, 3], [1, 2, 3], 3, id="no_zeros"), + pytest.param([0, 0, 0], [0, 0, 0], 0, id="all_zeros"), + pytest.param([1, 0, 2, 0, 3, 0], [1, 2, 3, 0, 0, 0], 3, id="interleaved"), + pytest.param([], [], 0, id="empty"), + pytest.param([0], [0], 0, id="single_zero"), + pytest.param([42], [42], 1, id="single_nonzero"), + ], +) def test_move_zeros_to_end_parametrized(input_list, expected_list, expected_index): arr = input_list[:] result_index = move_zeros_to_end(arr) @@ -71,89 +98,54 @@ def test_move_zeros_to_end_parametrized(input_list, expected_list, expected_inde assert result_index == expected_index -@pytest.mark.parametrize("matrix, expected_row", [ - pytest.param( - [[0, 0, 1, 1], - [0, 1, 1, 1], - [0, 0, 0, 1], - [1, 1, 1, 1], - [0, 1, 1, 1]], - 3, - id="classic" - ), - pytest.param( - [[0, 0, 0], - [0, 0, 0], - [0, 0, 0]], - 0, - id="all_zeros" - ), - pytest.param( - [[1, 1, 1], - [1, 1, 1], - [1, 1, 1]], - 0, - id="all_ones_first" - ), - pytest.param( - [[0, 1], - [1, 1]], - 1, - id="two_rows" - ), - pytest.param( - [[0]], - 0, - id="single_zero" - ), - pytest.param( - [[1]], - 0, - id="single_one" - ), - pytest.param( - [], - 0, - id="empty_matrix" - ), - pytest.param( - [[0, 0, 1], - [0, 1, 1], - [0, 1, 1]], - 1, - id="tie" - ), -]) +@pytest.mark.parametrize( + "matrix, expected_row", + [ + pytest.param( + [[0, 0, 1, 1], [0, 1, 1, 1], [0, 0, 0, 1], [1, 1, 1, 1], [0, 1, 1, 1]], 3, id="classic" + ), + pytest.param([[0, 0, 0], [0, 0, 0], [0, 0, 0]], 0, id="all_zeros"), + pytest.param([[1, 1, 1], [1, 1, 1], [1, 1, 1]], 0, id="all_ones_first"), + pytest.param([[0, 1], [1, 1]], 1, id="two_rows"), + pytest.param([[0]], 0, id="single_zero"), + pytest.param([[1]], 0, id="single_one"), + pytest.param([], 0, id="empty_matrix"), + pytest.param([[0, 0, 1], [0, 1, 1], [0, 1, 1]], 1, id="tie"), + ], +) def test_find_row_with_most_ones(matrix, expected_row): assert find_row_with_most_ones(matrix) == expected_row def test_find_row_with_most_ones_big_data(): size = 10000 - matrix = [[0]*size for i in range(size)] - matrix[size-1][size-1] = 1 + matrix = [[0] * size for i in range(size)] + matrix[size - 1][size - 1] = 1 for i in range(50): assert find_row_with_most_ones(matrix) == 9999 size = 10000 - matrix = [[1]*size for i in range(size)] + matrix = [[1] * size for i in range(size)] matrix[0][0] = 0 for i in range(50): assert find_row_with_most_ones(matrix) == 1 -@pytest.mark.parametrize("input_arr, expected", [ - pytest.param([0], 1, id="self_loop"), - pytest.param([1, 0], 1, id="two_cycle"), - pytest.param([1, 2, 0], 1, id="three_cycle"), - pytest.param([0, 1, 2], 3, id="three_self_loops"), - pytest.param([1, 0, 3, 2], 2, id="two_2_cycles"), - pytest.param([2, 0, 1, 4, 3], 2, id="mixed_cycles"), - pytest.param([10, 6, 2, 9, 4, 0, 3, 8, 7, 1, 5], 5, id="mixed_cycles"), - pytest.param([], 0, id="empty"), -]) +@pytest.mark.parametrize( + "input_arr, expected", + [ + pytest.param([0], 1, id="self_loop"), + pytest.param([1, 0], 1, id="two_cycle"), + pytest.param([1, 2, 0], 1, id="three_cycle"), + pytest.param([0, 1, 2], 3, id="three_self_loops"), + pytest.param([1, 0, 3, 2], 2, id="two_2_cycles"), + pytest.param([2, 0, 1, 4, 3], 2, id="mixed_cycles"), + pytest.param([10, 6, 2, 9, 4, 0, 3, 8, 7, 1, 5], 5, id="mixed_cycles"), + pytest.param([], 0, id="empty"), + ], +) def test_count_cycles(input_arr, expected): arr = input_arr[:] assert count_cycles(arr) == expected From 056e145b1c86592eb970a18730dbf3fb6c68f619 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Thu, 23 Oct 2025 21:37:06 +0300 Subject: [PATCH 08/25] Homework 25.10.2025 --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 104 ++++++++++++- conditions/lesson05/tasks.md | 2 +- solutions/lesson05/task1.py | 4 +- solutions/lesson05/task2.py | 12 +- solutions/lesson05/task3.py | 8 +- solutions/lesson05/task4.py | 10 +- solutions/lesson05/task5.py | 30 +++- solutions/lesson05/task6.py | 17 ++- tests/test_lesson05_tasks.py | 269 ++++++++++++++++++--------------- 10 files changed, 316 insertions(+), 140 deletions(-) diff --git a/.coverage b/.coverage index dad4699d2590b6689b9987d8c41599fa9fecce2c..b3ffce4651f7e646fcb462dcabf44648b4bf25aa 100644 GIT binary patch delta 511 zcmY+8&q@MO6vmroym!QyW6s~ZRIrsrR@QP{X0%X*!oiwmmk1SPK~SO4GN`x@kkJbS zv#l4XO+7&iH$6d1>z;||w))QZjO1l>wc9-W4P6AH?6$GvXmJm1+SVUk;U;%-JAp7&QEWnnjO$>Z~Q(K2h zI}AH}on9E?Feio^@u-$3!ZO8aU0BZQZ~BqW=o5`-N?5)Se%Wed{uCJ>2EQE*J*lIh zD|Pg9q>g$vcFE4#)&{AGAMA`FB;H-2CPCAR-gn0A@^bUI@6SN zj*YuY+7p-MjlbSA)D>seJy(e%4#OE!8-e`^#6n;h#Kbfe9$npw|4mM>#s4!nhJDzD l2m)w|{|w4R>=ee5@Vq2kHwni{!nTvJtRze`zPLS|{|4^`hQ9y+ delta 91 zcmZozz}&Eac>{|Bvk+(ZWOfJD&4L2)oJ>(dlNWl0ZGP;j$Oe??namyll1Sj3%o-lJ o`EI1T8nY08;$-#)kf=Qr&5&WB5=GPbi0MmIJi2wiq diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 7afb3136..16f5e85b 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -303,8 +303,108 @@ "execution_count": null, "id": "fb839bd5", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "def is_punctuation(text: str) -> bool:\n", + " for i in text:\n", + " numb_code = ord(i)\n", + " if not (\n", + " (numb_code >= 32 and numb_code <= 47)\n", + " or (numb_code >= 160 and numb_code <= 255)\n", + " or (numb_code >= 8192 and numb_code <= 8303)\n", + " or (numb_code >= 11776 and numb_code <= 11903)\n", + " or (numb_code >= 12288 and numb_code <= 12351)\n", + " ):\n", + " return False\n", + " return True\n", + "\n", + "\n", + "print(is_punctuation(\"!!!!!;q';\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c55fc9ef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "def unzip(compress_text: str) -> str:\n", + " result = \"\"\n", + " compress_text = compress_text.split(\" \")\n", + " for i in compress_text:\n", + " if \"*\" not in i:\n", + " result += i\n", + " else:\n", + " result += i[:-2] * int(i[-1])\n", + " return result\n", + "\n", + "\n", + "print(unzip(\"a*10\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "66072e10", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n" + ] + } + ], + "source": [ + "def reg_validator(reg_expr: str, text: str) -> bool:\n", + " i, j = 0, 0\n", + " while i < len(reg_expr) and j < len(text):\n", + " symbol = reg_expr[i]\n", + " if symbol == \"d\":\n", + " if not text[j].isdigit():\n", + " print(i)\n", + " return False\n", + " while j < len(text) and text[j].isdigit():\n", + " j += 1\n", + " elif symbol == \"p\":\n", + " if not text[j].isalpha():\n", + " return False\n", + " while j < len(text) and text[j].isalpha():\n", + " j += 1\n", + " elif symbol == \"s\":\n", + " if not (text[j].isalpha() or text[j].isdigit()):\n", + " return False\n", + " while j < len(text) and (text[j].isalpha() or text[j].isdigit()):\n", + " j += 1\n", + " else:\n", + " if text[j] != symbol:\n", + " return False\n", + " j += 1\n", + " i += 1\n", + "\n", + " return i == len(reg_expr) and j == len(text)\n", + "\n", + "\n", + "print(reg_validator(\"w\", \"hello\"))" + ] } ], "metadata": { diff --git a/conditions/lesson05/tasks.md b/conditions/lesson05/tasks.md index 148654ae..d4ae114c 100644 --- a/conditions/lesson05/tasks.md +++ b/conditions/lesson05/tasks.md @@ -5,7 +5,7 @@ Ваша задача — определить, является ли заданная строка палиндромом. -Допишите код в файле [task1](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson05/task1.py). +Допишите код в файле [task1](https://github.com/EvgrafovMichail/python_mipt_dafe_tasks/blob/main/solutions/lesson05/task1.py) **Входные данные:** - `text` — строка, состоящая из букв английского алфавита в верхнем и нижнем регистре длиной от 0 до 1000. diff --git a/solutions/lesson05/task1.py b/solutions/lesson05/task1.py index 9a17211e..8d94d0ac 100644 --- a/solutions/lesson05/task1.py +++ b/solutions/lesson05/task1.py @@ -1,3 +1,3 @@ def is_palindrome(text: str) -> bool: - # ваш код - return False \ No newline at end of file + text = text.lower() + return text == text[::-1] diff --git a/solutions/lesson05/task2.py b/solutions/lesson05/task2.py index 36750380..eded933a 100644 --- a/solutions/lesson05/task2.py +++ b/solutions/lesson05/task2.py @@ -1,3 +1,11 @@ def are_anagrams(word1: str, word2: str) -> bool: - # ваш код - return False \ No newline at end of file + if len(word1) != len(word2): + return False + char_index = [0] * 128 + for i in word1: + char_index[ord(i)] += 1 + for i in word2: + char_index[ord(i)] -= 1 + if char_index[ord(i)] < 0: + return False + return True diff --git a/solutions/lesson05/task3.py b/solutions/lesson05/task3.py index e368e2f4..8352c22d 100644 --- a/solutions/lesson05/task3.py +++ b/solutions/lesson05/task3.py @@ -1,3 +1,7 @@ def is_punctuation(text: str) -> bool: - # ваш код - return False + if not text: + return False + for i in text: + if i not in "!#$%&'()*+,-./:;<=>?@[\]^_{|}`\"~": + return False + return True diff --git a/solutions/lesson05/task4.py b/solutions/lesson05/task4.py index 4c4e9086..fd1c8bd7 100644 --- a/solutions/lesson05/task4.py +++ b/solutions/lesson05/task4.py @@ -1,3 +1,9 @@ def unzip(compress_text: str) -> str: - # ваш код - return compress_text \ No newline at end of file + result = "" + compress_text = compress_text.split(" ") + for i in compress_text: + if "*" not in i: + result += i + else: + result += i[: i.find("*")] * int(i[i.find("*") + 1 :]) + return result diff --git a/solutions/lesson05/task5.py b/solutions/lesson05/task5.py index 076c5bb6..44b40704 100644 --- a/solutions/lesson05/task5.py +++ b/solutions/lesson05/task5.py @@ -1,3 +1,27 @@ -def reg_validator(reg_expr: str, text: str) -> bool: - # ваш код - return False \ No newline at end of file +def reg_validator(reg_expr: str, text: str) -> bool: + i, j = 0, 0 + text = text.lower() + while i < len(reg_expr) and j < len(text): + symbol = reg_expr[i] + if symbol == "d": + if not text[j].isdigit(): + return False + while j < len(text) and text[j].isdigit(): + j += 1 + elif symbol == "w": + if not text[j].isalpha(): + return False + while j < len(text) and text[j].isalpha(): + j += 1 + elif symbol == "s": + if not (text[j].isalpha() or text[j].isdigit()): + return False + while j < len(text) and (text[j].isalpha() or text[j].isdigit()): + j += 1 + else: + if text[j] != symbol: + return False + j += 1 + i += 1 + + return i == len(reg_expr) and j == len(text) diff --git a/solutions/lesson05/task6.py b/solutions/lesson05/task6.py index 1b914ada..6513d768 100644 --- a/solutions/lesson05/task6.py +++ b/solutions/lesson05/task6.py @@ -1,3 +1,16 @@ def simplify_path(path: str) -> str: - # ваш код - return path \ No newline at end of file + path = path.split("/") + queue = [] + for dir in path: + if dir == "" or dir == ".": + continue + elif dir == "..": + if queue: + queue.pop() + else: + return "" + else: + queue.append(dir) + final_path = "/" + "/".join(queue) + + return final_path diff --git a/tests/test_lesson05_tasks.py b/tests/test_lesson05_tasks.py index 2ab63e3b..8bc609ef 100644 --- a/tests/test_lesson05_tasks.py +++ b/tests/test_lesson05_tasks.py @@ -1,4 +1,4 @@ -import pytest +import pytest from solutions.lesson05.task1 import is_palindrome from solutions.lesson05.task2 import are_anagrams @@ -7,145 +7,166 @@ from solutions.lesson05.task5 import reg_validator from solutions.lesson05.task6 import simplify_path -@pytest.mark.parametrize("s, expected", [ - pytest.param("", True, id="empty_string"), - pytest.param("a", True, id="single_char"), - pytest.param("aa", True, id="two_same"), - pytest.param("ab", False, id="two_different"), - pytest.param("aba", True, id="odd_palindrome"), - pytest.param("abba", True, id="even_palindrome"), - pytest.param("abcba", True, id="long_odd_palindrome"), - pytest.param("abccba", True, id="long_even_palindrome"), - pytest.param("abc", False, id="not_palindrome"), - pytest.param("Aa", True, id="case_sensitive_mismatch"), - pytest.param("Racecar", True, id="real_word_case_sensitive"), - pytest.param("aA", True, id="reverse_case"), - pytest.param("abcdefedcba", True, id="long_true"), - pytest.param("abcdefedcbx", False, id="long_false"), -]) + +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("", True, id="empty_string"), + pytest.param("a", True, id="single_char"), + pytest.param("aa", True, id="two_same"), + pytest.param("ab", False, id="two_different"), + pytest.param("aba", True, id="odd_palindrome"), + pytest.param("abba", True, id="even_palindrome"), + pytest.param("abcba", True, id="long_odd_palindrome"), + pytest.param("abccba", True, id="long_even_palindrome"), + pytest.param("abc", False, id="not_palindrome"), + pytest.param("Aa", True, id="case_sensitive_mismatch"), + pytest.param("Racecar", True, id="real_word_case_sensitive"), + pytest.param("aA", True, id="reverse_case"), + pytest.param("abcdefedcba", True, id="long_true"), + pytest.param("abcdefedcbx", False, id="long_false"), + ], +) def test_is_palindrome(s, expected): assert is_palindrome(s) == expected -@pytest.mark.parametrize("w1, w2, expected", [ - pytest.param("listen", "silent", True, id="classic_anagram"), - pytest.param("evil", "vile", True, id="another_anagram"), - pytest.param("a", "a", True, id="single_char_same"), - pytest.param("A", "A", True, id="single_upper_same"), - pytest.param("A", "a", False, id="case_sensitive_diff"), - pytest.param("Listen", "Silent", False, id="mixed_case_not_anagram"), - pytest.param("Aa", "aA", True, id="same_chars_permuted"), - pytest.param("Ab", "ab", False, id="one_letter_case_diff"), - pytest.param("abc", "cba", True, id="permuted_same_case"), - pytest.param("abc", "Cba", False, id="case_breaks_anagram"), - pytest.param("aabbcc", "abcabc", True, id="repeated_letters"), - pytest.param("aabbcc", "aabbcd", False, id="extra_different_char"), -]) +@pytest.mark.parametrize( + "w1, w2, expected", + [ + pytest.param("listen", "silent", True, id="classic_anagram"), + pytest.param("evil", "vile", True, id="another_anagram"), + pytest.param("a", "a", True, id="single_char_same"), + pytest.param("A", "A", True, id="single_upper_same"), + pytest.param("A", "a", False, id="case_sensitive_diff"), + pytest.param("Listen", "Silent", False, id="mixed_case_not_anagram"), + pytest.param("Aa", "aA", True, id="same_chars_permuted"), + pytest.param("Ab", "ab", False, id="one_letter_case_diff"), + pytest.param("abc", "cba", True, id="permuted_same_case"), + pytest.param("abc", "Cba", False, id="case_breaks_anagram"), + pytest.param("aabbcc", "abcabc", True, id="repeated_letters"), + pytest.param("aabbcc", "aabbcd", False, id="extra_different_char"), + ], +) def test_are_anagrams_linear(w1, w2, expected): assert are_anagrams(w1, w2) == expected -@pytest.mark.parametrize("s, expected", [ - pytest.param("!!!", True, id="only_exclamations"), - pytest.param("...?", True, id="dots_and_question"), - pytest.param("", False, id="empty_string"), - pytest.param("a", False, id="letter"), - pytest.param("1", False, id="digit"), - pytest.param(" ! ", False, id="space_inside"), - pytest.param("!?.", True, id="symbols_only"), - pytest.param("!a!", False, id="letter_in_middle"), - pytest.param(" ", False, id="only_space"), - pytest.param(".,;", True, id="commas_dots_semicolons"), - pytest.param("", False, id="commas_dots_semicolons"), -]) +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("!!!", True, id="only_exclamations"), + pytest.param("...?", True, id="dots_and_question"), + pytest.param("", False, id="empty_string"), + pytest.param("a", False, id="letter"), + pytest.param("1", False, id="digit"), + pytest.param(" ! ", False, id="space_inside"), + pytest.param("!?.", True, id="symbols_only"), + pytest.param("!a!", False, id="letter_in_middle"), + pytest.param(" ", False, id="only_space"), + pytest.param(".,;", True, id="commas_dots_semicolons"), + pytest.param("", False, id="commas_dots_semicolons"), + ], +) def test_is_only_punctuation(s, expected): assert is_punctuation(s) == expected -@pytest.mark.parametrize("compressed, expected", [ - pytest.param("AbcD*4 ef GhI*2", "AbcDAbcDAbcDAbcDefGhIGhI", id="example"), - pytest.param("a*3 b*2", "aaabb", id="simple_letters"), - pytest.param("Hello", "Hello", id="star_one"), - pytest.param("xyz", "xyz", id="no_compression"), - pytest.param("", "", id="empty_input"), - pytest.param("Test*2 Space", "TestTestSpace", id="mixed"), - pytest.param("a*10", "aaaaaaaaaa", id="ten_a"), - pytest.param("x y z", "xyz", id="three_plain"), - pytest.param("Word word", "Wordword", id="case_sensitive"), -]) + +@pytest.mark.parametrize( + "compressed, expected", + [ + pytest.param("AbcD*4 ef GhI*2", "AbcDAbcDAbcDAbcDefGhIGhI", id="example"), + pytest.param("a*3 b*2", "aaabb", id="simple_letters"), + pytest.param("Hello", "Hello", id="star_one"), + pytest.param("xyz", "xyz", id="no_compression"), + pytest.param("", "", id="empty_input"), + pytest.param("Test*2 Space", "TestTestSpace", id="mixed"), + pytest.param("a*10", "aaaaaaaaaa", id="ten_a"), + pytest.param("x y z", "xyz", id="three_plain"), + pytest.param("Word word", "Wordword", id="case_sensitive"), + ], +) def test_decompress(compressed, expected): assert unzip(compressed) == expected -@pytest.mark.parametrize("regexp, s, expected", [ - pytest.param("d", "123", True, id="d_valid_number"), - pytest.param("d", "0", True, id="d_zero"), - pytest.param("d", "abc", False, id="d_letters_instead_of_digits"), - pytest.param("d", "", False, id="d_empty_string"), - pytest.param("w", "hello", True, id="w_lowercase_word"), - pytest.param("w", "HelloWorld", True, id="w_mixed_case_word"), - pytest.param("w", "hello123", False, id="w_word_with_digits"), - pytest.param("w", "", False, id="w_empty_string"), - pytest.param("s", "abc123", True, id="s_alphanum"), - pytest.param("s", "ABC99", True, id="s_uppercase_and_digits"), - pytest.param("s", "abc_123", False, id="s_contains_underscore"), - pytest.param("s", "", False, id="s_empty_string"), - pytest.param("d-d", "12-34", True, id="d_dash_d_valid"), - pytest.param("d-d", "12--34", False, id="d_dash_d_double_dash"), - pytest.param("d-d", "12-abc", False, id="d_dash_d_letters_after_dash"), - pytest.param("d-d", "1234", False, id="d_dash_d_missing_dash"), - pytest.param("w.w", "hi.there", True, id="w_dot_w_valid"), - pytest.param("w.w", "hi..there", False, id="w_dot_w_double_dot"), - pytest.param("w.w", "hi1.there", False, id="w_dot_w_digit_in_first_word"), - pytest.param("s.s", "h1i.th32ere", True, id="s_dot_s_valid"), - pytest.param("s.s", "hi4..t2here", False, id="s_dot_s_double_dot"), - pytest.param("d-dw", "12-45abc", True, id="example_valid"), - pytest.param("d-dw", "1-abs", False, id="example_second_part_not_digit"), - pytest.param("d-dw", "1-b123r", False, id="example_letter_after_dash"), - pytest.param("d-dw", "1--123vdg", False, id="example_double_dash"), - pytest.param("d-dw", "123-456XYZ", True, id="d-dw_all_caps"), - pytest.param("d-dw", "0-0a", True, id="d-dw_minimal_valid"), - pytest.param("d@d", "5@7", True, id="d_at_d_valid"), - pytest.param("d@d", "5@@7", False, id="d_at_d_double_at"), - pytest.param("w s", "hi 123", True, id="w_space_s_valid"), - pytest.param("w s", "hi123", False, id="w_space_s_missing_space"), - pytest.param("w s", "hi 123!", False, id="w_space_s_extra_char_in_s"), - pytest.param("", "", True, id="empty_regexp_empty_string"), - pytest.param("", "a", False, id="empty_regexp_non_empty_string"), - pytest.param("d", "", False, id="non_empty_regexp_empty_string"), - pytest.param("d!", "5!", True, id="d_exclam_valid"), - pytest.param("d!", "5", False, id="d_exclam_missing_exclam"), - pytest.param("d!", "5!!", False, id="d_exclam_extra_exclam"), - pytest.param("s", "a1", True, id="s_letter_digit"), - pytest.param("s", "1a", True, id="s_digit_letter"), - pytest.param("s", "a!1", False, id="s_contains_exclamation"), - pytest.param("d-w-s", "123-abc-XY1Z23", True, id="d_w_s_valid"), - pytest.param("d-w-s", "123-abc-XYZ_123", False, id="d_w_s_underscore_in_s"), -]) + +@pytest.mark.parametrize( + "regexp, s, expected", + [ + pytest.param("d", "123", True, id="d_valid_number"), + pytest.param("d", "0", True, id="d_zero"), + pytest.param("d", "abc", False, id="d_letters_instead_of_digits"), + pytest.param("d", "", False, id="d_empty_string"), + pytest.param("w", "hello", True, id="w_lowercase_word"), + pytest.param("w", "HelloWorld", True, id="w_mixed_case_word"), + pytest.param("w", "hello123", False, id="w_word_with_digits"), + pytest.param("w", "", False, id="w_empty_string"), + pytest.param("s", "abc123", True, id="s_alphanum"), + pytest.param("s", "ABC99", True, id="s_uppercase_and_digits"), + pytest.param("s", "abc_123", False, id="s_contains_underscore"), + pytest.param("s", "", False, id="s_empty_string"), + pytest.param("d-d", "12-34", True, id="d_dash_d_valid"), + pytest.param("d-d", "12--34", False, id="d_dash_d_double_dash"), + pytest.param("d-d", "12-abc", False, id="d_dash_d_letters_after_dash"), + pytest.param("d-d", "1234", False, id="d_dash_d_missing_dash"), + pytest.param("w.w", "hi.there", True, id="w_dot_w_valid"), + pytest.param("w.w", "hi..there", False, id="w_dot_w_double_dot"), + pytest.param("w.w", "hi1.there", False, id="w_dot_w_digit_in_first_word"), + pytest.param("s.s", "h1i.th32ere", True, id="s_dot_s_valid"), + pytest.param("s.s", "hi4..t2here", False, id="s_dot_s_double_dot"), + pytest.param("d-dw", "12-45abc", True, id="example_valid"), + pytest.param("d-dw", "1-abs", False, id="example_second_part_not_digit"), + pytest.param("d-dw", "1-b123r", False, id="example_letter_after_dash"), + pytest.param("d-dw", "1--123vdg", False, id="example_double_dash"), + pytest.param("d-dw", "123-456XYZ", True, id="d-dw_all_caps"), + pytest.param("d-dw", "0-0a", True, id="d-dw_minimal_valid"), + pytest.param("d@d", "5@7", True, id="d_at_d_valid"), + pytest.param("d@d", "5@@7", False, id="d_at_d_double_at"), + pytest.param("w s", "hi 123", True, id="w_space_s_valid"), + pytest.param("w s", "hi123", False, id="w_space_s_missing_space"), + pytest.param("w s", "hi 123!", False, id="w_space_s_extra_char_in_s"), + pytest.param("", "", True, id="empty_regexp_empty_string"), + pytest.param("", "a", False, id="empty_regexp_non_empty_string"), + pytest.param("d", "", False, id="non_empty_regexp_empty_string"), + pytest.param("d!", "5!", True, id="d_exclam_valid"), + pytest.param("d!", "5", False, id="d_exclam_missing_exclam"), + pytest.param("d!", "5!!", False, id="d_exclam_extra_exclam"), + pytest.param("s", "a1", True, id="s_letter_digit"), + pytest.param("s", "1a", True, id="s_digit_letter"), + pytest.param("s", "a!1", False, id="s_contains_exclamation"), + pytest.param("d-w-s", "123-abc-XY1Z23", True, id="d_w_s_valid"), + pytest.param("d-w-s", "123-abc-XYZ_123", False, id="d_w_s_underscore_in_s"), + ], +) def test_match_pattern(regexp, s, expected): assert reg_validator(regexp, s) == expected -@pytest.mark.parametrize("path, expected", [ - pytest.param("/home/", "/home", id="trailing_slash"), - pytest.param("/../", "", id="go_above_root"), - pytest.param("/home//foo/", "/home/foo", id="double_slash"), - pytest.param("/home/./foo/", "/home/foo", id="current_dir_dot"), - pytest.param("/./././", "/", id="only_dots_and_slashes"), - pytest.param("/a/./b/../../c/", "/c", id="complex_up_and_down"), - pytest.param("/a/b/c/../../../", "/", id="back_to_root"), - pytest.param("/", "/", id="root_only"), - pytest.param("/.", "/", id="root_with_dot"), - pytest.param("/..", "", id="root_with_double_dot"), - pytest.param("/...", "/...", id="triple_dot_as_name"), - pytest.param("/..a", "/..a", id="dot_dot_a_as_name"), - pytest.param("/a.b/c.d", "/a.b/c.d", id="names_with_dots"), - pytest.param("/a//b////c/d//././/..", "/a/b/c", id="messy_path"), - pytest.param("/a/./b/./c/./d", "/a/b/c/d", id="dots_everywhere"), - pytest.param("/a/./b/../../c/./d/", "/c/d", id="up_down_with_dots"), - pytest.param("/../foo", "", id="up_then_valid"), - pytest.param("/../../foo", "", id="multiple_up_then_valid"), - pytest.param("/../../../", "", id="three_up_from_root"), - pytest.param("/home/foo/./../../../", "", id="too_many_up"), - pytest.param("/_a.b/c__1/..", "/_a.b", id="names_with_underscores_and_dots"), -]) +@pytest.mark.parametrize( + "path, expected", + [ + pytest.param("/home/", "/home", id="trailing_slash"), + pytest.param("/../", "", id="go_above_root"), + pytest.param("/home//foo/", "/home/foo", id="double_slash"), + pytest.param("/home/./foo/", "/home/foo", id="current_dir_dot"), + pytest.param("/./././", "/", id="only_dots_and_slashes"), + pytest.param("/a/./b/../../c/", "/c", id="complex_up_and_down"), + pytest.param("/a/b/c/../../../", "/", id="back_to_root"), + pytest.param("/", "/", id="root_only"), + pytest.param("/.", "/", id="root_with_dot"), + pytest.param("/..", "", id="root_with_double_dot"), + pytest.param("/...", "/...", id="triple_dot_as_name"), + pytest.param("/..a", "/..a", id="dot_dot_a_as_name"), + pytest.param("/a.b/c.d", "/a.b/c.d", id="names_with_dots"), + pytest.param("/a//b////c/d//././/..", "/a/b/c", id="messy_path"), + pytest.param("/a/./b/./c/./d", "/a/b/c/d", id="dots_everywhere"), + pytest.param("/a/./b/../../c/./d/", "/c/d", id="up_down_with_dots"), + pytest.param("/../foo", "", id="up_then_valid"), + pytest.param("/../../foo", "", id="multiple_up_then_valid"), + pytest.param("/../../../", "", id="three_up_from_root"), + pytest.param("/home/foo/./../../../", "", id="too_many_up"), + pytest.param("/_a.b/c__1/..", "/_a.b", id="names_with_underscores_and_dots"), + ], +) def test_simplify_path(path, expected): - assert simplify_path(path) == expected \ No newline at end of file + assert simplify_path(path) == expected From 94b14b1ee7032b6ee09b1c4945682b9b76b8c681 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 31 Oct 2025 14:52:21 +0300 Subject: [PATCH 09/25] 01.11.2025 Homework --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 166 +++++++++++++++++++++++++++- solutions/lesson06/task1.py | 42 ++++++- solutions/lesson06/task2.py | 14 ++- solutions/lesson06/task3.py | 24 +++- solutions/lesson06/task4.py | 13 ++- tests/test_lesson06_tasks.py | 195 ++++++++++++++++++--------------- 7 files changed, 352 insertions(+), 102 deletions(-) diff --git a/.coverage b/.coverage index b3ffce4651f7e646fcb462dcabf44648b4bf25aa..53fbe1e39bb43ada61b1a835652b32399817e7b0 100644 GIT binary patch delta 332 zcmZozz}&Eac>{|Bvn=bB$?Ois9BeDtd{{rQPT4pyh?OZycJe~603NfLlEmU{6TO1U zC>bOHV~~I}l7JCNKnh8~5F{W85}3^A!L~WZeFX>5qN$VF1ArE-Wb=ZkdX90TW%>JLqh7`|NHlLGf!6Pf5a@yZ$6oQ p0?-T}eph}wesiE1U-@L%fXWzUWx{|Bvm`sqWOfJR&4L1_*qEXuCol90n9S$FwmHUq1qV=)buxPZNb)q> s bool:\n", + " if len(nums) < 2:\n", + " return False\n", + " elif len(nums) == 2:\n", + " sum = 0\n", + " for subarr in nums:\n", + " sum+=subarr\n", + " if sum%k==0:\n", + " return True\n", + " else:\n", + " return False\n", + " for i in range(0,len(nums) - 1):\n", + " for j in range(i+2, len(nums)+1):\n", + " sum=0\n", + " print(nums[i:j])\n", + " for subarr in nums[i:j]:\n", + " sum += subarr\n", + " if sum % k == 0:\n", + " return True\n", + " return False\n", + "print(is_there_any_good_subarray([1, 2, 3], 5))" + ] + }, { "cell_type": "code", "execution_count": 2, @@ -320,9 +361,9 @@ " (numb_code >= 32 and numb_code <= 47)\n", " or (numb_code >= 160 and numb_code <= 255)\n", " or (numb_code >= 8192 and numb_code <= 8303)\n", - " or (numb_code >= 11776 and numb_code <= 11903)\n", + " or (numb_code >=ё 11776 and numb_code <= 11903)\n", " or (numb_code >= 12288 and numb_code <= 12351)\n", - " ):\n", + " )\n", " return False\n", " return True\n", "\n", @@ -405,6 +446,127 @@ "\n", "print(reg_validator(\"w\", \"hello\"))" ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7a1295ce", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n" + ] + } + ], + "source": [ + "def get_sum_of_prime_divisors(num: int) -> int:\n", + " n = num\n", + " if n == 1:\n", + " return 0\n", + " sum_of_divisors = 0\n", + " for i in range(2, int(num**0.5) + 1):\n", + " if num % i == 0:\n", + " sum_of_divisors += i\n", + " while num % i == 0:\n", + " num //= i\n", + " if sum_of_divisors == 0:\n", + " return n\n", + " return sum_of_divisors\n", + "print(get_sum_of_prime_divisors(26))" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "f34569a9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n", + "XL\n" + ] + } + ], + "source": [ + "def int_to_roman(num: int) -> str:\n", + " roman_numbers = { 1000 : \"M\", 500 : \"D\", 100 : \"C\", \n", + " 50 : \"L\", 10 : \"X\", 5 : \"V\", 1 : \"I\"}\n", + " roman_digits = [1000, 500, 100, 50, 10, 5, 1]\n", + " index = 0\n", + " result = ''\n", + " flag_close = False\n", + " while index < len(roman_digits) and num > 0:\n", + " if not flag_close and (num - roman_digits[index]) < 0:\n", + " flag_close = True\n", + " elif not flag_close:\n", + " result += roman_numbers[roman_digits[index]]\n", + " num -= roman_digits[index]\n", + " elif flag_close:\n", + " if (index == 0 or index == 2 or index == 4) and (num - (roman_digits[index] - 10 ** ((str(roman_digits[index]).count('0')) - 1))) >= 0:\n", + " num -= (roman_digits[index] - 10 ** ((str(roman_digits[index]).count('0')) - 1))\n", + " result += roman_numbers[10 ** ((str(roman_digits[index]).count('0')) - 1)]+roman_numbers[roman_digits[index]]\n", + " flag_close = False\n", + " index+=1 \n", + " elif index != 2 and index!= 0 and index != 4 and (num - (roman_digits[index] - 10 ** str(roman_digits[index]).count('0'))) >= 0:\n", + " print( 10 ** str(roman_digits[index]).count('0'))\n", + " result += roman_numbers[10 ** str(roman_digits[index]).count('0')]+roman_numbers[roman_digits[index]]\n", + " num -= (roman_digits[index] - 10 ** str(roman_digits[index]).count('0'))\n", + " flag_close = False\n", + " index+=1\n", + " else:\n", + " flag_close = False\n", + " index+=1\n", + " return result\n", + "print(int_to_roman(40))" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "2ae168bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(set('pwwwkew'))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "fbde457b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "18.329926258226045 0.06225318726752254\n" + ] + } + ], + "source": [ + "rad = float(input())\n", + "i = 5.1*10**(-4)*(1453*2+576.02)*(rad**2) - 5.5\n", + "progri = i * (4*(0.005/rad)**2 + (1/(1453+1453+576.02))**2+0.002**2)**0.5\n", + "print(i, progri)" + ] } ], "metadata": { diff --git a/solutions/lesson06/task1.py b/solutions/lesson06/task1.py index 2d1e30e9..af4c66fb 100644 --- a/solutions/lesson06/task1.py +++ b/solutions/lesson06/task1.py @@ -1,3 +1,41 @@ def int_to_roman(num: int) -> str: - # ваш код - return "" \ No newline at end of file + roman_numbers = {1000: "M", 500: "D", 100: "C", 50: "L", 10: "X", 5: "V", 1: "I"} + roman_digits = [1000, 500, 100, 50, 10, 5, 1] + index = 0 + result = "" + flag_close = False + while index < len(roman_digits) and num > 0: + if not flag_close and (num - roman_digits[index]) < 0: + flag_close = True + elif not flag_close: + result += roman_numbers[roman_digits[index]] + num -= roman_digits[index] + elif flag_close: + if (index == 0 or index == 2 or index == 4) and ( + num - (roman_digits[index] - 10 ** ((str(roman_digits[index]).count("0")) - 1)) + ) >= 0: + num -= roman_digits[index] - 10 ** ((str(roman_digits[index]).count("0")) - 1) + result += ( + roman_numbers[10 ** ((str(roman_digits[index]).count("0")) - 1)] + + roman_numbers[roman_digits[index]] + ) + flag_close = False + index += 1 + elif ( + index != 2 + and index != 0 + and index != 4 + and (num - (roman_digits[index] - 10 ** str(roman_digits[index]).count("0"))) >= 0 + ): + print(10 ** str(roman_digits[index]).count("0")) + result += ( + roman_numbers[10 ** str(roman_digits[index]).count("0")] + + roman_numbers[roman_digits[index]] + ) + num -= roman_digits[index] - 10 ** str(roman_digits[index]).count("0") + flag_close = False + index += 1 + else: + flag_close = False + index += 1 + return result diff --git a/solutions/lesson06/task2.py b/solutions/lesson06/task2.py index f535b5a0..be64a537 100644 --- a/solutions/lesson06/task2.py +++ b/solutions/lesson06/task2.py @@ -1,3 +1,13 @@ def get_len_of_longest_substring(text: str) -> int: - # ваш код - return 0 \ No newline at end of file + start = 0 + maxlen = 0 + while start < len(text): + substr = "" + for i in range(start, len(text)): + if text[i] not in substr: + substr += text[i] + else: + break + maxlen = max(maxlen, len(substr)) + start += 1 + return maxlen diff --git a/solutions/lesson06/task3.py b/solutions/lesson06/task3.py index 7449a1e7..1451f188 100644 --- a/solutions/lesson06/task3.py +++ b/solutions/lesson06/task3.py @@ -1,7 +1,19 @@ -def is_there_any_good_subarray( - nums: list[int], - k: int, -) -> bool: - - # ваш код +def is_there_any_good_subarray(nums: list[int], k: int) -> bool: + if len(nums) < 2: + return False + elif len(nums) == 2: + sum = 0 + for subarr in nums: + sum += subarr + if sum % k == 0: + return True + else: + return False + for i in range(0, len(nums) - 1): + for j in range(i + 2, len(nums) + 1): + sum = 0 + for subarr in nums[i:j]: + sum += subarr + if sum % k == 0: + return True return False diff --git a/solutions/lesson06/task4.py b/solutions/lesson06/task4.py index 5b75a110..161c4b44 100644 --- a/solutions/lesson06/task4.py +++ b/solutions/lesson06/task4.py @@ -1,3 +1,12 @@ +import string + + def count_unique_words(text: str) -> int: - # ваш код - return 0 \ No newline at end of file + text = text.lower() + words = text.split(" ") + unique_words = set() + for i in range(len(words)): + word = words[i].strip(string.punctuation) + if word or any(letter.isalnum() for letter in word): + unique_words.add(word) + return len(unique_words) diff --git a/tests/test_lesson06_tasks.py b/tests/test_lesson06_tasks.py index 0abade4f..e6e6f276 100644 --- a/tests/test_lesson06_tasks.py +++ b/tests/test_lesson06_tasks.py @@ -1,4 +1,4 @@ -import pytest +import pytest from solutions.lesson06.task1 import int_to_roman from solutions.lesson06.task2 import get_len_of_longest_substring @@ -6,103 +6,122 @@ from solutions.lesson06.task4 import count_unique_words -@pytest.mark.parametrize("num, expected", [ - pytest.param(1, "I", id="one"), - pytest.param(2, "II", id="two"), - pytest.param(3, "III", id="three"), - pytest.param(4, "IV", id="four"), - pytest.param(5, "V", id="five"), - pytest.param(6, "VI", id="six"), - pytest.param(9, "IX", id="nine"), - pytest.param(10, "X", id="ten"), - pytest.param(11, "XI", id="eleven"), - pytest.param(14, "XIV", id="fourteen"), - pytest.param(19, "XIX", id="nineteen"), - pytest.param(27, "XXVII", id="twenty_seven"), - pytest.param(40, "XL", id="forty"), - pytest.param(44, "XLIV", id="forty_four"), - pytest.param(50, "L", id="fifty"), - pytest.param(58, "LVIII", id="fifty_eight"), - pytest.param(90, "XC", id="ninety"), - pytest.param(99, "XCIX", id="ninety_nine"), - pytest.param(100, "C", id="hundred"), - pytest.param(400, "CD", id="four_hundred"), - pytest.param(500, "D", id="five_hundred"), - pytest.param(900, "CM", id="nine_hundred"), - pytest.param(1000, "M", id="thousand"), - pytest.param(1994, "MCMXCIV", id="mcmxciv"), - pytest.param(3999, "MMMCMXCIX", id="max_value"), - pytest.param(2023, "MMXXIII", id="current_year"), - pytest.param(1984, "MCMLXXXIV", id="classic"), -]) +@pytest.mark.parametrize( + "num, expected", + [ + pytest.param(1, "I", id="one"), + pytest.param(2, "II", id="two"), + pytest.param(3, "III", id="three"), + pytest.param(4, "IV", id="four"), + pytest.param(5, "V", id="five"), + pytest.param(6, "VI", id="six"), + pytest.param(9, "IX", id="nine"), + pytest.param(10, "X", id="ten"), + pytest.param(11, "XI", id="eleven"), + pytest.param(14, "XIV", id="fourteen"), + pytest.param(19, "XIX", id="nineteen"), + pytest.param(27, "XXVII", id="twenty_seven"), + pytest.param(40, "XL", id="forty"), + pytest.param(44, "XLIV", id="forty_four"), + pytest.param(50, "L", id="fifty"), + pytest.param(58, "LVIII", id="fifty_eight"), + pytest.param(90, "XC", id="ninety"), + pytest.param(99, "XCIX", id="ninety_nine"), + pytest.param(100, "C", id="hundred"), + pytest.param(400, "CD", id="four_hundred"), + pytest.param(500, "D", id="five_hundred"), + pytest.param(900, "CM", id="nine_hundred"), + pytest.param(1000, "M", id="thousand"), + pytest.param(1994, "MCMXCIV", id="mcmxciv"), + pytest.param(3999, "MMMCMXCIX", id="max_value"), + pytest.param(2023, "MMXXIII", id="current_year"), + pytest.param(1984, "MCMLXXXIV", id="classic"), + ], +) def test_int_to_roman(num, expected): assert int_to_roman(num) == expected -@pytest.mark.parametrize("s, expected", [ - pytest.param("", 0, id="empty_string"), - pytest.param("a", 1, id="single_char"), - pytest.param("aa", 1, id="two_same_chars"), - pytest.param("ab", 2, id="two_different_chars"), - pytest.param("abcabcbb", 3, id="classic_example_abc"), - pytest.param("bbbbb", 1, id="all_same"), - pytest.param("pwwkew", 3, id="pwwkew_example"), - pytest.param("abcdef", 6, id="all_unique"), - pytest.param("abcabcbbxyz", 4, id="long_tail_unique"), - pytest.param("aab", 2, id="aab"), - pytest.param("dvdf", 3, id="dvdf"), - pytest.param(" ", 1, id="single_space"), - pytest.param("a b c", 3, id="letters_and_spaces_unique"), - pytest.param("a b a", 3, id="space_in_middle_with_repeat"), - pytest.param("1234567890", 10, id="digits_all_unique"), - pytest.param("112233", 2, id="repeating_digits"), - pytest.param("abcdefghijklmnopqrstuvwxyz", 26, id="all_lowercase_letters"), - pytest.param("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", 63, id="max_unique_set"), - pytest.param("a" * 10000, 1, id="ten_thousand_same"), - pytest.param("abc" * 3333 + "d", 4, id="long_repeating_with_new_char"), -]) + +@pytest.mark.parametrize( + "s, expected", + [ + pytest.param("", 0, id="empty_string"), + pytest.param("a", 1, id="single_char"), + pytest.param("aa", 1, id="two_same_chars"), + pytest.param("ab", 2, id="two_different_chars"), + pytest.param("abcabcbb", 3, id="classic_example_abc"), + pytest.param("bbbbb", 1, id="all_same"), + pytest.param("pwwkew", 3, id="pwwkew_example"), + pytest.param("abcdef", 6, id="all_unique"), + pytest.param("abcabcbbxyz", 4, id="long_tail_unique"), + pytest.param("aab", 2, id="aab"), + pytest.param("dvdf", 3, id="dvdf"), + pytest.param(" ", 1, id="single_space"), + pytest.param("a b c", 3, id="letters_and_spaces_unique"), + pytest.param("a b a", 3, id="space_in_middle_with_repeat"), + pytest.param("1234567890", 10, id="digits_all_unique"), + pytest.param("112233", 2, id="repeating_digits"), + pytest.param("abcdefghijklmnopqrstuvwxyz", 26, id="all_lowercase_letters"), + pytest.param( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 ", + 63, + id="max_unique_set", + ), + pytest.param("a" * 10000, 1, id="ten_thousand_same"), + pytest.param("abc" * 3333 + "d", 4, id="long_repeating_with_new_char"), + ], +) def test_get_len_of_longest_substring(s, expected): assert get_len_of_longest_substring(s) == expected -@pytest.mark.parametrize("nums, k, expected", [ - pytest.param([23, 2, 4, 6, 7], 6, True, id="subarray_2_4_sum_6"), - pytest.param([23, 2, 6, 4, 7], 6, True, id="total_sum_42_div_by_6"), - pytest.param([23, 2, 6, 4, 7], 13, False, id="no_valid_subarray"), - pytest.param([0, 0], 1, True, id="two_zeros_any_k"), - pytest.param([1, 0], 2, False, id="length_2_sum_1_not_div_by_2"), - pytest.param([1, 2, 3], 5, True, id="subarray_2_3_sum_5"), - pytest.param([1], 1, False, id="single_element_too_short"), - pytest.param([5, 0, 0], 3, True, id="zeros_after_nonzero"), - pytest.param([1, 2], 3, True, id="exact_sum_equals_k"), - pytest.param([1, 2], 4, False, id="sum_not_divisible_by_k"), - pytest.param([0], 1, False, id="single_zero_too_short"), - pytest.param([1, 0, 2], 2, True, id="subarray_0_2_sum_2"), - pytest.param([4, 2, 4], 6, True, id="first_two_sum_6"), - pytest.param([1, 1, 1], 2, True, id="first_two_ones_sum_2"), - pytest.param([1, 2, 4, 8], 8, False, id="no_subarray_divisible_by_8"), - pytest.param([0, 1, 0], 2, False, id="zeros_with_one_sum_1"), - pytest.param([0, 1, 0, 0], 2, True, id="last_two_zeros_sum_0_div_by_any"), -]) + +@pytest.mark.parametrize( + "nums, k, expected", + [ + pytest.param([23, 2, 4, 6, 7], 6, True, id="subarray_2_4_sum_6"), + pytest.param([23, 2, 6, 4, 7], 6, True, id="total_sum_42_div_by_6"), + pytest.param([23, 2, 6, 4, 7], 13, False, id="no_valid_subarray"), + pytest.param([0, 0], 1, True, id="two_zeros_any_k"), + pytest.param([1, 0], 2, False, id="length_2_sum_1_not_div_by_2"), + pytest.param([1, 2, 3], 5, True, id="subarray_2_3_sum_5"), + pytest.param([1], 1, False, id="single_element_too_short"), + pytest.param([5, 0, 0], 3, True, id="zeros_after_nonzero"), + pytest.param([1, 2], 3, True, id="exact_sum_equals_k"), + pytest.param([1, 2], 4, False, id="sum_not_divisible_by_k"), + pytest.param([0], 1, False, id="single_zero_too_short"), + pytest.param([1, 0, 2], 2, True, id="subarray_0_2_sum_2"), + pytest.param([4, 2, 4], 6, True, id="first_two_sum_6"), + pytest.param([1, 1, 1], 2, True, id="first_two_ones_sum_2"), + pytest.param([1, 2, 4, 8], 8, False, id="no_subarray_divisible_by_8"), + pytest.param([0, 1, 0], 2, False, id="zeros_with_one_sum_1"), + pytest.param([0, 1, 0, 0], 2, True, id="last_two_zeros_sum_0_div_by_any"), + ], +) def test_is_there_any_good_subarray(nums, k, expected): assert is_there_any_good_subarray(nums, k) == expected import pytest -@pytest.mark.parametrize("text, expected", [ - pytest.param("", 0, id="empty_string"), - pytest.param(" ", 0, id="only_spaces"), - pytest.param("hello", 1, id="single_word"), - pytest.param("Hello hello", 1, id="case_insensitive"), - pytest.param("Hello, world!", 2, id="punctuation_around"), - pytest.param("Hello, hello, world!", 2, id="duplicates_with_punct"), - pytest.param("The quick brown fox jumps over the lazy dog.", 8, id="classic_pangram"), - pytest.param("!!! ???", 0, id="only_punctuation"), - pytest.param("word1 word2 word1", 2, id="digits_in_words"), - pytest.param("Don't stop believing!", 3, id="apostrophe_inside"), - pytest.param(" Hello , World ! ", 2, id="extra_whitespace"), - pytest.param("A a A a", 1, id="repeated_case_variants"), - pytest.param("word... word!!!", 1, id="multiple_punct_at_end"), - pytest.param("123 456 123", 2, id="numbers_as_words"), -]) + +@pytest.mark.parametrize( + "text, expected", + [ + pytest.param("", 0, id="empty_string"), + pytest.param(" ", 0, id="only_spaces"), + pytest.param("hello", 1, id="single_word"), + pytest.param("Hello hello", 1, id="case_insensitive"), + pytest.param("Hello, world!", 2, id="punctuation_around"), + pytest.param("Hello, hello, world!", 2, id="duplicates_with_punct"), + pytest.param("The quick brown fox jumps over the lazy dog.", 8, id="classic_pangram"), + pytest.param("!!! ???", 0, id="only_punctuation"), + pytest.param("word1 word2 word1", 2, id="digits_in_words"), + pytest.param("Don't stop believing!", 3, id="apostrophe_inside"), + pytest.param(" Hello , World ! ", 2, id="extra_whitespace"), + pytest.param("A a A a", 1, id="repeated_case_variants"), + pytest.param("word... word!!!", 1, id="multiple_punct_at_end"), + pytest.param("123 456 123", 2, id="numbers_as_words"), + ], +) def test_count_unique_words(text, expected): - assert count_unique_words(text) == expected \ No newline at end of file + assert count_unique_words(text) == expected From fa621a3c8bc177efe849e78f56f28e3b794fa161 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 15 Nov 2025 00:01:11 +0300 Subject: [PATCH 10/25] homework: --- .coverage | Bin 53248 -> 53248 bytes solutions/lesson08/task1.py | 18 ++++++++++++++---- solutions/lesson08/task2.py | 27 +++++++++++++++++++++------ tests/test_lesson08_tasks.py | 14 +++++++++----- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/.coverage b/.coverage index 53fbe1e39bb43ada61b1a835652b32399817e7b0..bf74cfda65d43c8c3170ae53d56585c435036a0f 100644 GIT binary patch delta 281 zcmZozz}&Eac>{|BvjR))WOfH@Hda%Xn=G{(Cz`S_MJY^P=oP?Y5mS;_oNc66P#GnU zBwz>>h?SeH=NZ6n5fdMunU`4-9}iL_3sN+Bo*UcdH?9&~K!fWhvj+eTHeTsC*ntqy6N-Fke0tbLD`_!Z*JOmC#~V;JY`Oy}_D| z?<3y}zI&Sm1*Y*yb11MdatbNj`)|)K4`d0*?~-Se12XvJ7&rgy<7Q-5;8&W=KEaxe i-+*6}UkRw`7M~~^PywTY0+^DQ2UBu#o6YB+Gynh`6;Zzc delta 98 zcmZozz}&Eac>{|Bvn=bB$?Oi+8z%;_GDXQwUg#Atd7c~F<~ObqTtK<0li34+a>1;V w>p}t{5}^`W%(8qxC$l$LZx$5T&bRqzA2%bjEWi0=_6Z=7uY8-$=btnH0AV^I%>V!Z diff --git a/solutions/lesson08/task1.py b/solutions/lesson08/task1.py index 4390f6c8..5dffe2c5 100644 --- a/solutions/lesson08/task1.py +++ b/solutions/lesson08/task1.py @@ -1,5 +1,15 @@ -from typing import Callable +import typing -def make_averager(accumulation_period: int) -> Callable[[float], float]: - # ваш код - pass \ No newline at end of file + +def make_averager(accumulation_period: int) -> typing.Callable[[float], float]: + sum_change = [] + + def get_avg(value: float): + nonlocal sum_change + sum_change.append(value) + if len(sum_change) > accumulation_period: + sum_change.pop(0) + avg = sum(sum_change) / len(sum_change) + return avg + + return get_avg diff --git a/solutions/lesson08/task2.py b/solutions/lesson08/task2.py index 6e4af870..86367abe 100644 --- a/solutions/lesson08/task2.py +++ b/solutions/lesson08/task2.py @@ -1,10 +1,25 @@ +import time +from functools import wraps from typing import Callable, TypeVar T = TypeVar("T") -def collect_statistic( - statistics: dict[str, list[float, int]] -) -> Callable[[T], T]: - - # ваш код - pass \ No newline at end of file + +def collect_statistic(statistics: dict[str, list[float, int]]) -> Callable[[T], T]: + def checker(func: Callable): + @wraps(func) + def wrapper(*args, **kwargs): + start = time.time() + result = func(*args, **kwargs) + end = time.time() + time_length = end - start + if func.__name__ not in statistics: + statistics[func.__name__] = [0.0, 0] + old_avg, old_count = statistics[func.__name__] + new_avg = (old_avg * old_count + time_length) / (old_count + 1) + statistics[func.__name__] = [new_avg, old_count + 1] + return result + + return wrapper + + return checker diff --git a/tests/test_lesson08_tasks.py b/tests/test_lesson08_tasks.py index 7d71e479..8f22770e 100644 --- a/tests/test_lesson08_tasks.py +++ b/tests/test_lesson08_tasks.py @@ -1,10 +1,11 @@ -import pytest +import pytest import math import time from solutions.lesson08.task1 import make_averager from solutions.lesson08.task2 import collect_statistic + def test_make_averager(): get_avg = make_averager(2) @@ -15,6 +16,7 @@ def test_make_averager(): assert math.isclose(get_avg(5), 1) assert math.isclose(get_avg(5), 5) + def test_make_averager2(): get_avg = make_averager(5) @@ -27,6 +29,7 @@ def test_make_averager2(): assert math.isclose(get_avg(-7), 0) assert math.isclose(get_avg(-2), -1) + def test_collect_statistic(): statistics: list[str, list[float, int]] = {} @@ -37,7 +40,7 @@ def func1() -> None: @collect_statistic(statistics) def func2() -> None: time.sleep(0.1) - + for _ in range(3): func1() @@ -58,10 +61,11 @@ def test_collect_statistic_inout(): @collect_statistic(statistics) def func(a, b, *, c, d): return a + b + c + d - + assert func(1, 2, c=3, d=4) == 10 assert statistics[func.__name__][1] == 1 + def test_collect_statistic_count_call(): statistics: list[str, list[float, int]] = {} @@ -76,7 +80,7 @@ def func(): count_call += 1 return func - + func = func_fab() func() - assert statistics[func.__name__][1] == 1 \ No newline at end of file + assert statistics[func.__name__][1] == 1 From 0de0dc9920781616996b1d0de492bc22ae9f2c39 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sun, 30 Nov 2025 00:53:35 +0300 Subject: [PATCH 11/25] Hw1 done --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 23 ++ homeworks/hw1/aggregate_segmentation.py | 89 ++++- homeworks/hw1/backoff.py | 24 +- homeworks/hw1/cache.py | 31 +- homeworks/hw1/convert_exception.py | 18 +- tests_hw/test_hw1_task1.py | 424 ++++++++++++++++++++++++ tests_hw/test_hw1_task4.py | 139 ++++++++ 8 files changed, 734 insertions(+), 14 deletions(-) create mode 100644 tests_hw/test_hw1_task1.py create mode 100644 tests_hw/test_hw1_task4.py diff --git a/.coverage b/.coverage index bf74cfda65d43c8c3170ae53d56585c435036a0f..fc3d9e0069f2e1d9bbc99f7175f7a89cd90b0224 100644 GIT binary patch delta 607 zcmZ{fO-lk%6o#+Po!f`Z93>Y)AGE8WPa-IaY8Ne{TvP_ZFb&sHL`QV!V^j76BFeqC zu1zpMAd@yNS}H99cTrFasYMZFXC#pcTwTsR&-)&p^tq5e7p~}nmq|qe7qBWn%ct^` zRJ45F8;PlmlB31U>C((V*og66BNo=jB@&OAih?)V2_Z}>5x9ha{31`wXHp4u3liO?C>vOx?Qi6o$+93tS7dvf zY{OMtr>KfVmy@WgQk|kH?uVTFrl!IsMNtg=7gd&9>lN0!;_LG=un2rX1s>rZ@?fD| z4(2?Jy4pHfVVZoFT^`XMXrYB1RI3ujp<9%X=Slu&M(7Z591z^@g}azV1(H4dyq?cg kPCBsofxtI>z#Ej|2_CTc7W{Uvi?P~6#o8lUuQ~tNFZPkmiU0rr literal 53248 zcmeI4OKcm*8OL||*yZxELp>>qlDJ48vJgqJ?AVG^)Qal}s1XOT8=((J4ZWgP(#9lL z-d)NzP$yLqz(C#f(55H~6m4#O0ll=nw5cyWr0AvftuH9;rF9Q&@E4QV0N@~*d#^ntAw3Jp}k=V1Dr`S~bTbYlhuBNNnmr~zWO|1+k=pX__fCvzQ z|8D}DC(~*!pO;^}=$lIw%XiJPD#qc!(X$)(sj>T#)S^@V5!4`)4V>WO7=3;@~u^nSa0U6- zSQjRFf##?A)_Fh301?YxtzijcPqX@j;aV$}Yt_n@7kIJ9UYO;O`RO}jWX3evG z+o>7Wd8=Iat>rs5a1x<~2f%A@4QQRl2AvLSHb1M4POI`AYBq~EE3bfaH7CT{s)!#A zmATKgdUXkqKUc4=W9LAP;Ddc2s>%4ci0@qt5v+g_meF-|hGy^Z`mwOUwu2_^W zo#laocuGsQ?`Q4hO@$l4AwS+&VgudA2SbyFiaN* zn&C7Hf~WU2+R$0>!C0y|OP#UCBgV5}fWTZ37F(&;$~aU!Y(v@Euv~N1nr@yAriJ?* zNDw+Rnv$C@?i4!PAyAsxP9`_=ikd4FwU-`N=@+MFuYGVo@c|d zip!SwXqan^ubCdsLf*KsA?`aMKcs_p2Zm})Sq4lCa799 zmxJMsozkXwZq2$)E?A8oH+{>utCoR9xHCMk&Y7N3b}cvwYw5&z^u59Wd#f~queeK2 z;HBJVQC4#gJ}7V2n}ac!XG$I{>QyszyZJWK5hS;qY{kn{FyX@0PJ5gnyEYv7Huwab z+=3ol3Mho7n%6qX3Ebez?1lsnI*0%fAOb{y2oM1xKm>>Y5g-CYfCvzQ4<3Q29FbMr z|3}!LCH7Bvp@Rqz0U|&IhyW2F0z`la5CI}U1c(3;*p&p*k?1H3|H5N$R*n|(&A$Pd zou1h{eJ}~Fim+=EyT;z$l?^1Pi2xBG0z`la5CI}U1c(3;AOb{y2oQm70_o_e9Nq?q zB%=kbc^3e`|2JY6CH4dMg#Iu6Yx->FkC_)TA4Z45_cHr`4G9J>_`(t@x|)1F_$O03Aet2oQmLA#lowNt=g`KDzkiu@eiW6Rxvr zR;%#4=aT2wmu<^iJaNHab84lsQ+GXUaUFgS?%QQ+YR#!y=N$LUqUTiV{8yZd70dIS z+TKSNebYO$4}J)CswhT#2DCpv2HJDo^}AHEYqno1q3fgFcKsm7@-S+Th|%7U+66J% zygP@*Xk&Mt%6D7)0Ox%WwTHxLA3*J#7;PRWgJQIKoD7K3=5f+5Mw<`&KG5#!us^Wa zKJ0tD?fS#5FzgYd&BHJ&Mw^Er6Qj+;P#2@k!!RR8n@?BiZfo!7al)CT#AtISNio`- ziPmlHeVoZGPO}Ly+I)FX#c12oPd@=Lk^smyDbT;*q)Mu0LCSOZFu3gi< ztW71ZChCd2`U}-m#n!<|D%Gmap4{jq>T%AL6A0f=Wy4xTl@dKAZ@(B8xo|A7kD{A z+88H;g0wMC1_Wtiob-2n*mvyz`?~IWYyaOXNE^ejN02s#VOEefh9MKAjbW$@(#9~% zbX~i(|4$3jMkXmi+Q=l?b?w&vUlXK_`~QR>Z5)@XAZ;9%iXd$qmvKSb7=tlE+8Be; zt}hQA`~S#7Y;#KM%EfZqwI==lzkAxSP+=lK1c(3;AOb{y2oM1xKm>>Y5g-D)jX+e6 zN*VnAUuN%0@SuYT5CI}U1c(3;AOb{y2oM1xKm>>Y5%>TSh(5#TlU4fYlGGW!Da*mKNepJON4C)s0cmQAt;*Z@nji2ja#UB3nb zbPxd|Km>>Y5g-CYfCvx)B0vO)01>!v1QJF})_RStn{#RrQrY6qipm%y^f6f-#f;H^ z7ZW2`W~6b0sRhg`G(MghhQ!ctG=cCFXB=o_C zI)E7i|7A))X7uYyAKE@y?Zp$ljXAXk6FrStHH(RCBdRbg!VV{Nu)TWY=3zC1r!tMA zn#M%BQB0)pTx}_?Zp}?6qCgNKuC5C5W z4JC>;dRU2|jmU91rfIs&fB(-Okk}3OANCLSSN0~{0l3P3&3?&VhkF1&V&8?k0N-R^ zWtZVTz(uyf9JU5`0#31~*>N_{4#T~Gy=(#m=pX__fCvx)B0vO)01+SpM1Tko0V1%g z3E+-eHVkfxMQ+B%xEUSgW@Ln$LV=s%VQ%tyZia@q$>q2i9OPzTfSdk)ZuFwpF zr-z$tmK(;n(RFSz8E(>PZc?zd2hWm8ZZwUXM1mWY-v^Ktg};l(xrxQNiAK4JL}G9Q zfPeolWp{N4k@G}=2oM1xKm>>Y5g-CYfCvx)B0vO)z+DL7_y4s2zY8QfO$3Ml5g-CY zfCvx)B0vO)01+SpL|~T_z~BGV{(qNuBDqflhyW2F0z`la5CI}U1c(3;AOb|-E(HDu DuQ6_L diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 623825fd..3e5591d3 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -567,6 +567,29 @@ "progri = i * (4*(0.005/rad)**2 + (1/(1453+1453+576.02))**2+0.002**2)**0.5\n", "print(i, progri)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "114c276f", + "metadata": {}, + "outputs": [ + { + "ename": "ImportError", + "evalue": "cannot import name 'NoneType' from 'typing' (c:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\typing.py)", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mImportError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[3], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mtyping\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m NoneType\n\u001b[0;32m 2\u001b[0m test \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 3\u001b[0m \u001b[38;5;28misinstance\u001b[39m(test, NoneType)\n", + "\u001b[1;31mImportError\u001b[0m: cannot import name 'NoneType' from 'typing' (c:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\typing.py)" + ] + } + ], + "source": [ + "test = None\n", + "isinstance(test, NoneType)" + ] } ], "metadata": { diff --git a/homeworks/hw1/aggregate_segmentation.py b/homeworks/hw1/aggregate_segmentation.py index 1cdc176a..dc45a2b4 100644 --- a/homeworks/hw1/aggregate_segmentation.py +++ b/homeworks/hw1/aggregate_segmentation.py @@ -5,9 +5,56 @@ } + +def check_validation(audio_segment: dict, used_id: list[list[str, str]]) -> list[bool, int]: + """Проверка данных на валидность + Args: + audio_segment: словарь, в котором хранится сегемент который в данный момент находится на проверкеимеет те же поля, что и segmentation_data + used_id : cписок сегментов прошедших проверку хранит внутри массивы в каждом из которых первыйм идет audio_id вторым segment_id + Return: + [True,0] если значение валидно и на всех полях не None + [True,1] если значение валидно и на во всех полях None + [False,0] если значение невалидно + + """ + + + required_keys: tuple = ("segment_id", "segment_start", "segment_end", "type") + + if not all(k in audio_segment for k in required_keys): + return [False, 0] + + if not isinstance(audio_segment["segment_id"], str): + return [False, 0] + + start: float = audio_segment["segment_start"] + end: float = audio_segment["segment_end"] + audio_type: str = audio_segment["type"] + + if all(v is None for v in (start, end, audio_type)): + return [True, 1] + + if any(v is None for v in (start, end, audio_type)): + return [False, 0] + + if not isinstance(start, float) or not isinstance(end, float) or not isinstance(audio_type, str): + return [False, 0] + + + if audio_segment["type"] not in ALLOWED_TYPES: + return [False, 0] + + id_check = [audio_segment["audio_id"], audio_segment["segment_id"]] + + + return [not(id_check in used_id),0] + + + + + def aggregate_segmentation( - segmentation_data: list[dict[str, str | float | None]], -) -> tuple[dict[str, dict[str, dict[str, str | float]]], list[str]]: + segmentation_data: list[dict[str, str | float | None]],) -> tuple[dict[str, dict[str, dict[str, str | float]]], list[str]]: """ Функция для валидации и агрегации данных разметки аудио сегментов. @@ -23,6 +70,40 @@ def aggregate_segmentation( Словарь с валидными сегментами, объединёнными по `audio_id`; Список `audio_id` (str), которые требуют переразметки. """ + result : list[dict[str, dict[str, dict[str, str | float]]], list[str]] = [{}, []] + audio_id_set = set(i["audio_id"] for i in segmentation_data) + redo_set: set = set() + used_id : list[list] = [] + + + unique_segments: list = [] #удаляем прям одинаковые элементы чтобы потом не проверять + unique_segments_checked: set = set() + for segment in segmentation_data: + key = tuple(sorted(segment.items())) #делаем tuple чтобы добавлять в множество тк массив нельзя(чтобы не забыть на сдаче) + if key not in unique_segments_checked: + unique_segments_checked.add(key) + unique_segments.append(segment) + segmentation_data = unique_segments + + + - # ваш код - return {}, [] + for audio in audio_id_set: + audio_rn: dict = {} + for segment in segmentation_data: + if segment["audio_id"] == audio: + checker = check_validation(segment, used_id) + if checker[0] and checker[1] == 0: + audio_rn[segment["segment_id"]] = {"start": segment["segment_start"], "end" : segment["segment_end"], + "type" : segment["type"]} + used_id.append([segment["audio_id"],segment["segment_id"]]) + elif checker[0] and checker[1] == 1: + used_id.append([segment["audio_id"],segment["segment_id"]]) + else: + redo_set.add(audio) + break + if audio in redo_set: + continue + result[0][audio] = audio_rn + result[1] = list(redo_set) + return tuple(result) diff --git a/homeworks/hw1/backoff.py b/homeworks/hw1/backoff.py index 696ffa73..46328b76 100644 --- a/homeworks/hw1/backoff.py +++ b/homeworks/hw1/backoff.py @@ -5,11 +5,13 @@ ParamSpec, TypeVar, ) +from functools import wraps P = ParamSpec("P") R = TypeVar("R") + def backoff( retry_amount: int = 3, timeout_start: float = 0.5, @@ -33,6 +35,24 @@ def backoff( Raises: ValueError, если были переданы невозможные аргументы. """ + if (retry_amount <= 0 or timeout_max <= 0 or timeout_start <= 0 + or backoff_scale <= 0): + raise ValueError("Detected negative number, use positive numbers for decorator") + def decorator(func: Callable): + @wraps(func) + def wrapper(*args, **kargs): + delay = timeout_start + for i in range(retry_amount + 1): + try: + return func(*args,**kargs) + except backoff_triggers as exc: + if i == retry_amount + 1: + raise + delay_amount = min(timeout_max, delay ) + uniform(0, 0.5) + sleep(delay_amount) + delay *= backoff_scale + except Exception: + raise + return wrapper + return decorator - # ваш код - pass diff --git a/homeworks/hw1/cache.py b/homeworks/hw1/cache.py index 9eb1d5d2..6a2b88d4 100644 --- a/homeworks/hw1/cache.py +++ b/homeworks/hw1/cache.py @@ -3,11 +3,10 @@ ParamSpec, TypeVar, ) - +from collections import OrderedDict +from functools import wraps P = ParamSpec("P") R = TypeVar("R") - - def lru_cache(capacity: int) -> Callable[[Callable[P, R]], Callable[P, R]]: """ Параметризованный декоратор для реализации LRU-кеширования. @@ -23,5 +22,27 @@ def lru_cache(capacity: int) -> Callable[[Callable[P, R]], Callable[P, R]]: для получения целого числа. ValueError, если после округления capacity - число, меньшее 1. """ - # ваш код - pass + try: + capacity = round(capacity) + except Exception: + raise TypeError + if capacity < 1: + raise ValueError + + def decorator(func: Callable) -> Callable: + cashe = OrderedDict() + @wraps(func) + def wrapper(*args,**kargs): + key = (args, tuple(sorted(kargs.items()))) + if key in cashe: + cashe.move_to_end(key) + return cashe[key] + res = func(*args, **kargs) + cashe[key] = res + cashe.move_to_end(key) + if len(cashe) > capacity: + cashe.popitem(last = False) + return res + return wrapper + return decorator + diff --git a/homeworks/hw1/convert_exception.py b/homeworks/hw1/convert_exception.py index fe5c770f..cc7dc724 100644 --- a/homeworks/hw1/convert_exception.py +++ b/homeworks/hw1/convert_exception.py @@ -3,7 +3,7 @@ ParamSpec, TypeVar, ) - +from functools import wraps P = ParamSpec("P") R = TypeVar("R") @@ -24,5 +24,17 @@ def convert_exceptions_to_api_compitable_ones( Декоратор для непосредственного использования. """ - # ваш код - pass + def decorator(func: Callable): + @wraps(func) + def wrapper(*args,**kwargs): + try: + return func(*args,**kwargs) + except Exception as exc: + exc_type = type(exc) + if exc_type in exception_to_api_exception: + if isinstance(exception_to_api_exception[exc_type], type): + raise exception_to_api_exception[exc_type]() + raise exception_to_api_exception[exc_type] + raise + return wrapper + return decorator diff --git a/tests_hw/test_hw1_task1.py b/tests_hw/test_hw1_task1.py new file mode 100644 index 00000000..100ecd3b --- /dev/null +++ b/tests_hw/test_hw1_task1.py @@ -0,0 +1,424 @@ +import pytest +from typing import Any + +# Импортируем тестируемую функцию и константу +from homeworks.hw1.aggregate_segmentation import aggregate_segmentation, ALLOWED_TYPES + + +class TestAggregateSegmentation: + """Тесты для функции агрегации сегментации аудио данных.""" + + def test_valid_segments_different_audio_ids(self): + """Тест валидных сегментов с разными audio_id.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-456", + "segment_id": "seg-2", + "segment_start": 1.0, + "segment_end": 3.0, + "type": "voice_bot", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert "audio-123" in valid + assert "audio-456" in valid + assert valid["audio-123"]["seg-1"]["type"] == "voice_human" + assert valid["audio-456"]["seg-2"]["type"] == "voice_bot" + + def test_valid_segments_same_audio_id(self): + """Тест валидных сегментов с одинаковым audio_id.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": 3.0, + "segment_end": 5.0, + "type": "spotter_word", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert len(valid["audio-123"]) == 2 + assert "seg-1" in valid["audio-123"] + assert "seg-2" in valid["audio-123"] + + def test_segments_without_voice_all_none(self): + """Тест сегментов без голоса (все поля None) - должен создать пустой словарь.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": None, + "segment_end": None, + "type": None, + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": None, + "segment_end": None, + "type": None, + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert valid["audio-123"] == {} + + def test_mixed_voice_and_no_voice_segments(self): + """Тест смешанных сегментов (с голосом и без) для одного audio_id.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": None, + "segment_end": None, + "type": None, + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert len(valid["audio-123"]) == 1 # Только сегмент с голосом + assert "seg-1" in valid["audio-123"] + assert "seg-2" not in valid["audio-123"] + + def test_duplicate_segments_same_data(self): + """Тест дубликатов сегментов с одинаковыми данными (должны игнорироваться).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert len(valid["audio-123"]) == 1 + + def test_missing_segment_id(self): + """Тест отсутствующего segment_id.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "", # Пустой ID + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": 1.0, + "segment_end": 3.0, + "type": "voice_bot", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert len(valid.get("audio-123", {})) == 0 + + @pytest.mark.parametrize("field,value", [ + ("type", 123), # type не строка + ("segment_start", "not_float"), # start не float + ("segment_end", "not_float"), # end не float + ]) + def test_invalid_field_types(self, field: str, value: Any): + """Тест невалидных типов полей.""" + base_segment = { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + } + base_segment[field] = value + data = [base_segment] + + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert len(valid) == 0 + + @pytest.mark.parametrize("segment_start,segment_end,type_val", [ + (None, 2.5, "voice_human"), # Только start None + (0.5, None, "voice_human"), # Только end None + (0.5, 2.5, None), # Только type None + (None, None, "voice_human"), # Только type не None + (0.5, None, None), # Только start не None + ]) + def test_partial_none_values(self, segment_start: Any, segment_end: Any, type_val: Any): + """Тест частичных None значений (должны быть невалидными).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": segment_start, + "segment_end": segment_end, + "type": type_val, + } + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert len(valid) == 0 + + def test_type_not_in_allowed_types(self): + """Тест type не из ALLOWED_TYPES.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "invalid_type", + } + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert len(valid) == 0 + + def test_duplicate_segments_different_data(self): + """Тест дубликатов сегментов с разными данными (должно быть невалидно).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 1.0, # Другой start + "segment_end": 3.0, + "type": "voice_human", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + # Проверяем, что валидные сегменты не добавлены + assert len(valid.get("audio-123", {})) == 0 + + def test_missing_audio_id(self): + """Тест отсутствующего audio_id (должен игнорироваться).""" + data = [ + { + "audio_id": "", # Пустой ID + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": 1.0, + "segment_end": 3.0, + "type": "voice_bot", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert "audio-123" in valid + assert len(valid["audio-123"]) == 1 + + def test_empty_input(self): + """Тест пустого входного списка.""" + valid, invalid = aggregate_segmentation([]) + + assert len(valid) == 0 + assert len(invalid) == 0 + + def test_audio_id_with_mixed_valid_invalid_segments(self): + """Тест audio_id с валидными и невалидными сегментами (весь audio_id невалиден).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "", # Невалидный сегмент + "segment_start": 1.0, + "segment_end": 3.0, + "type": "voice_bot", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert len(valid) == 0 + + def test_multiple_invalid_segments_same_audio_id(self): + """Тест нескольких невалидных сегментов для одного audio_id (в списке должен быть один раз).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "", # Невалидный + "segment_start": 0.5, + "segment_end": 2.5, + "type": "voice_human", + }, + { + "audio_id": "audio-123", + "segment_id": "seg-2", + "segment_start": None, # Невалидный (частичный None) + "segment_end": 3.0, + "type": "voice_bot", + }, + { + "audio_id": "audio-456", + "segment_id": "seg-3", + "segment_start": 1.0, + "segment_end": 2.0, + "type": "voice_human", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert "audio-123" in invalid + assert "audio-456" not in invalid + assert invalid.count("audio-123") == 1 # Только один раз + assert len(valid) == 1 + assert "audio-456" in valid + + def test_time_validation_edge_cases(self): + """Тест краевых случаев времени.""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 0.0, # Ноль + "segment_end": 10.0, # Максимум + "type": "voice_human", + }, + { + "audio_id": "audio-456", + "segment_id": "seg-2", + "segment_start": 5.0, + "segment_end": 5.0, # Нулевая длительность + "type": "spotter_word", + }, + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert len(valid) == 2 + + @pytest.mark.performance + def test_large_dataset(self): + """Производственный тест на большом наборе данных.""" + data = [] + # 100 разных audio_id, каждый с 10 сегментами + for i in range(100): + audio_id = f"audio-{i}" + for j in range(10): + data.append({ + "audio_id": audio_id, + "segment_id": f"seg-{i}-{j}", + "segment_start": float(j), + "segment_end": float(j + 1), + "type": "voice_human" if j % 2 == 0 else "voice_bot", + }) + + valid, invalid = aggregate_segmentation(data) + + assert len(valid) == 100 + assert len(invalid) == 0 + assert all(len(segments) == 10 for segments in valid.values()) + + def test_mixed_allowed_types(self): + """Тест всех разрешенных типов.""" + data = [ + { + "audio_id": f"audio-{i}", + "segment_id": f"seg-{i}", + "segment_start": 0.5, + "segment_end": 2.5, + "type": voice_type, + } + for i, voice_type in enumerate(ALLOWED_TYPES) + ] + valid, invalid = aggregate_segmentation(data) + + assert len(invalid) == 0 + assert len(valid) == len(ALLOWED_TYPES) + + def test_negative_time_values(self): + """Тест отрицательных значений времени (должны быть валидны, так как не проверяются).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": -1.0, + "segment_end": -0.5, + "type": "voice_human", + } + ] + valid, invalid = aggregate_segmentation(data) + + # В текущей реализации это считается валидным + assert "audio-123" not in invalid + assert "audio-123" in valid + + def test_start_greater_than_end(self): + """Тест когда start > end (должно быть валидно, так как не проверяется).""" + data = [ + { + "audio_id": "audio-123", + "segment_id": "seg-1", + "segment_start": 5.0, + "segment_end": 2.0, + "type": "voice_human", + } + ] + valid, invalid = aggregate_segmentation(data) + + # В текущей реализации это считается валидным + assert "audio-123" not in invalid + assert "audio-123" in valid + assert valid["audio-123"]["seg-1"]["start"] == 5.0 + assert valid["audio-123"]["seg-1"]["end"] == 2.0 \ No newline at end of file diff --git a/tests_hw/test_hw1_task4.py b/tests_hw/test_hw1_task4.py new file mode 100644 index 00000000..c5e70ece --- /dev/null +++ b/tests_hw/test_hw1_task4.py @@ -0,0 +1,139 @@ +import pytest +from unittest.mock import Mock + +# Импортируем тестируемую функцию +from homeworks.hw1.cache import lru_cache + +""" + + Списки с аргументами: + + * первый параметр - количество аргументов + * второй параметр - возможные типы + * третий параметр - повторение аргументов + * четвертый параметр - размер + +""" + +ARGS_DICT: dict[str, list[tuple[object]]] = { + + 'one_int_without_repeating_args_small': [ + (2), + (1), + ], + + 'one_int_with_repeating_args_small': [ + (2), + (1), + (2), + (1), + (1), + ], + + 'one_int_with_repeating_args_huge': [ + (1), #c + (2), #c + (2), + (1), + (2), + (1), + (1), + (2), + (1), + (1), + (3), #c + (1), + (1), + (3), + (2), #c + (1), #c + (1), + (1), + (1), + (3), #c + (1), + (3), + (2), #c + (1), #c + (3), #c + (2), #c + (1), #c + (1), + ], + + 'three_str_with_repeating_args_small': [ + ('first', 'second', 'third'), #1 o + ('first', 'third', 'second'), #2 o + ('third', 'first', 'third'), #3 o + ('first', 'second', 'third'), #1 o + ('first', 'third', 'second'), #2 o + ('first', 'second', 'first'), #4 o + ('third', 'first', 'third'), #3 o + ('first', 'second', 'third'), #1 o + ], + + 'two_multi_with_repeating_args_small': [ + (42, 'str'), #o #o + (5.6, TypeError), #o #o + (7 + 7j, None), #o #o + (5.6, TypeError), + (7 + 7j, None), + (5.6, TypeError), + (42, 'str'), #o + (5.6, TypeError), + (True, False), #o #o + (42, 'str'), #o + (7 + 7j, None), #o #o + (True, False), #o + (5.6, TypeError), #o #o + (42, 'str'), #o #o + (42, 'str'), + (7 + 7j, None), #o #o + (7 + 7j, None), + (42, 'str'), + (True, False), #o #o + ] +} + +"""Тесты для декоратора LRU-cache""" + +@pytest.mark.parametrize("capacity, call_count_expected, call_args", [ + (2, 2, ARGS_DICT['one_int_without_repeating_args_small']), + (2, 2, ARGS_DICT['one_int_with_repeating_args_small']), + (2, 11, ARGS_DICT['one_int_with_repeating_args_huge']), + (2, 8, ARGS_DICT['three_str_with_repeating_args_small']), + (2, 12, ARGS_DICT['two_multi_with_repeating_args_small']), + (5, 3, ARGS_DICT['one_int_with_repeating_args_huge']), + (3, 6, ARGS_DICT['three_str_with_repeating_args_small']), + (3, 9, ARGS_DICT['two_multi_with_repeating_args_small']), +]) + +def test_cache( + capacity: int, + call_count_expected: int, + call_args: list[tuple[object]] +) -> None: + + mock_func = Mock() + func_cached = lru_cache(capacity=capacity)(mock_func) + + for args in call_args: + func_cached(args) + + assert mock_func.call_count == call_count_expected + +def test_exception_value_error( +) -> None: + + capacity: float = 0.4 + mock_func = Mock() + with pytest.raises(ValueError): + lru_cache(capacity=capacity)(mock_func) + +def test_exception_type_error( +) -> None: + + capacity = None + mock_func = Mock() + with pytest.raises(TypeError): + lru_cache(capacity=capacity)(mock_func) \ No newline at end of file From 8597d6337145da27c4409993b1fe3a28a120dcb2 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sun, 30 Nov 2025 01:00:05 +0300 Subject: [PATCH 12/25] Hw1 done --- homeworks/hw1/aggregate_segmentation.py | 63 ++++++----- homeworks/hw1/backoff.py | 16 +-- homeworks/hw1/cache.py | 19 ++-- homeworks/hw1/convert_exception.py | 9 +- tests/test_lesson04_tasks.py | 3 +- tests/test_lesson08_tasks.py | 1 - tests_hw/test_hw1_task1.py | 95 ++++++++-------- tests_hw/test_hw1_task4.py | 140 +++++++++++------------- tests_hw/test_hw1_tasks.py | 54 +++++---- 9 files changed, 202 insertions(+), 198 deletions(-) diff --git a/homeworks/hw1/aggregate_segmentation.py b/homeworks/hw1/aggregate_segmentation.py index dc45a2b4..e2c7c4b6 100644 --- a/homeworks/hw1/aggregate_segmentation.py +++ b/homeworks/hw1/aggregate_segmentation.py @@ -5,20 +5,18 @@ } - def check_validation(audio_segment: dict, used_id: list[list[str, str]]) -> list[bool, int]: - """Проверка данных на валидность - Args: - audio_segment: словарь, в котором хранится сегемент который в данный момент находится на проверкеимеет те же поля, что и segmentation_data - used_id : cписок сегментов прошедших проверку хранит внутри массивы в каждом из которых первыйм идет audio_id вторым segment_id - Return: - [True,0] если значение валидно и на всех полях не None - [True,1] если значение валидно и на во всех полях None - [False,0] если значение невалидно + """Проверка данных на валидность + Args: + audio_segment: словарь, в котором хранится сегемент который в данный момент находится на проверкеимеет те же поля, что и segmentation_data + used_id : cписок сегментов прошедших проверку хранит внутри массивы в каждом из которых первыйм идет audio_id вторым segment_id + Return: + [True,0] если значение валидно и на всех полях не None + [True,1] если значение валидно и на во всех полях None + [False,0] если значение невалидно """ - required_keys: tuple = ("segment_id", "segment_start", "segment_end", "type") if not all(k in audio_segment for k in required_keys): @@ -37,24 +35,24 @@ def check_validation(audio_segment: dict, used_id: list[list[str, str]]) -> list if any(v is None for v in (start, end, audio_type)): return [False, 0] - if not isinstance(start, float) or not isinstance(end, float) or not isinstance(audio_type, str): + if ( + not isinstance(start, float) + or not isinstance(end, float) + or not isinstance(audio_type, str) + ): return [False, 0] - if audio_segment["type"] not in ALLOWED_TYPES: return [False, 0] - - id_check = [audio_segment["audio_id"], audio_segment["segment_id"]] - - - return [not(id_check in used_id),0] - + id_check = [audio_segment["audio_id"], audio_segment["segment_id"]] + return [id_check not in used_id, 0] def aggregate_segmentation( - segmentation_data: list[dict[str, str | float | None]],) -> tuple[dict[str, dict[str, dict[str, str | float]]], list[str]]: + segmentation_data: list[dict[str, str | float | None]], +) -> tuple[dict[str, dict[str, dict[str, str | float]]], list[str]]: """ Функция для валидации и агрегации данных разметки аудио сегментов. @@ -70,35 +68,36 @@ def aggregate_segmentation( Словарь с валидными сегментами, объединёнными по `audio_id`; Список `audio_id` (str), которые требуют переразметки. """ - result : list[dict[str, dict[str, dict[str, str | float]]], list[str]] = [{}, []] + result: list[dict[str, dict[str, dict[str, str | float]]], list[str]] = [{}, []] audio_id_set = set(i["audio_id"] for i in segmentation_data) redo_set: set = set() - used_id : list[list] = [] - + used_id: list[list] = [] - unique_segments: list = [] #удаляем прям одинаковые элементы чтобы потом не проверять + unique_segments: list = [] # удаляем прям одинаковые элементы чтобы потом не проверять unique_segments_checked: set = set() - for segment in segmentation_data: - key = tuple(sorted(segment.items())) #делаем tuple чтобы добавлять в множество тк массив нельзя(чтобы не забыть на сдаче) + for segment in segmentation_data: + key = tuple( + sorted(segment.items()) + ) # делаем tuple чтобы добавлять в множество тк массив нельзя(чтобы не забыть на сдаче) if key not in unique_segments_checked: unique_segments_checked.add(key) unique_segments.append(segment) segmentation_data = unique_segments - - - for audio in audio_id_set: audio_rn: dict = {} for segment in segmentation_data: if segment["audio_id"] == audio: checker = check_validation(segment, used_id) if checker[0] and checker[1] == 0: - audio_rn[segment["segment_id"]] = {"start": segment["segment_start"], "end" : segment["segment_end"], - "type" : segment["type"]} - used_id.append([segment["audio_id"],segment["segment_id"]]) + audio_rn[segment["segment_id"]] = { + "start": segment["segment_start"], + "end": segment["segment_end"], + "type": segment["type"], + } + used_id.append([segment["audio_id"], segment["segment_id"]]) elif checker[0] and checker[1] == 1: - used_id.append([segment["audio_id"],segment["segment_id"]]) + used_id.append([segment["audio_id"], segment["segment_id"]]) else: redo_set.add(audio) break diff --git a/homeworks/hw1/backoff.py b/homeworks/hw1/backoff.py index 46328b76..0a5194eb 100644 --- a/homeworks/hw1/backoff.py +++ b/homeworks/hw1/backoff.py @@ -1,3 +1,4 @@ +from functools import wraps from random import uniform from time import sleep from typing import ( @@ -5,13 +6,11 @@ ParamSpec, TypeVar, ) -from functools import wraps P = ParamSpec("P") R = TypeVar("R") - def backoff( retry_amount: int = 3, timeout_start: float = 0.5, @@ -35,24 +34,25 @@ def backoff( Raises: ValueError, если были переданы невозможные аргументы. """ - if (retry_amount <= 0 or timeout_max <= 0 or timeout_start <= 0 - or backoff_scale <= 0): + if retry_amount <= 0 or timeout_max <= 0 or timeout_start <= 0 or backoff_scale <= 0: raise ValueError("Detected negative number, use positive numbers for decorator") + def decorator(func: Callable): @wraps(func) def wrapper(*args, **kargs): delay = timeout_start for i in range(retry_amount + 1): try: - return func(*args,**kargs) - except backoff_triggers as exc: + return func(*args, **kargs) + except backoff_triggers: if i == retry_amount + 1: raise - delay_amount = min(timeout_max, delay ) + uniform(0, 0.5) + delay_amount = min(timeout_max, delay) + uniform(0, 0.5) sleep(delay_amount) delay *= backoff_scale except Exception: raise + return wrapper - return decorator + return decorator diff --git a/homeworks/hw1/cache.py b/homeworks/hw1/cache.py index 6a2b88d4..ef50b060 100644 --- a/homeworks/hw1/cache.py +++ b/homeworks/hw1/cache.py @@ -1,12 +1,15 @@ +from collections import OrderedDict +from functools import wraps from typing import ( Callable, ParamSpec, TypeVar, ) -from collections import OrderedDict -from functools import wraps + P = ParamSpec("P") R = TypeVar("R") + + def lru_cache(capacity: int) -> Callable[[Callable[P, R]], Callable[P, R]]: """ Параметризованный декоратор для реализации LRU-кеширования. @@ -28,21 +31,23 @@ def lru_cache(capacity: int) -> Callable[[Callable[P, R]], Callable[P, R]]: raise TypeError if capacity < 1: raise ValueError - + def decorator(func: Callable) -> Callable: cashe = OrderedDict() + @wraps(func) - def wrapper(*args,**kargs): + def wrapper(*args, **kargs): key = (args, tuple(sorted(kargs.items()))) if key in cashe: - cashe.move_to_end(key) + cashe.move_to_end(key) return cashe[key] res = func(*args, **kargs) cashe[key] = res cashe.move_to_end(key) if len(cashe) > capacity: - cashe.popitem(last = False) + cashe.popitem(last=False) return res + return wrapper - return decorator + return decorator diff --git a/homeworks/hw1/convert_exception.py b/homeworks/hw1/convert_exception.py index cc7dc724..68dc0bf2 100644 --- a/homeworks/hw1/convert_exception.py +++ b/homeworks/hw1/convert_exception.py @@ -1,9 +1,10 @@ +from functools import wraps from typing import ( Callable, ParamSpec, TypeVar, ) -from functools import wraps + P = ParamSpec("P") R = TypeVar("R") @@ -26,9 +27,9 @@ def convert_exceptions_to_api_compitable_ones( def decorator(func: Callable): @wraps(func) - def wrapper(*args,**kwargs): + def wrapper(*args, **kwargs): try: - return func(*args,**kwargs) + return func(*args, **kwargs) except Exception as exc: exc_type = type(exc) if exc_type in exception_to_api_exception: @@ -36,5 +37,7 @@ def wrapper(*args,**kwargs): raise exception_to_api_exception[exc_type]() raise exception_to_api_exception[exc_type] raise + return wrapper + return decorator diff --git a/tests/test_lesson04_tasks.py b/tests/test_lesson04_tasks.py index 58567b8d..3d0a5156 100644 --- a/tests/test_lesson04_tasks.py +++ b/tests/test_lesson04_tasks.py @@ -1,6 +1,7 @@ -import pytest import random +import pytest + from solutions.lesson04.task1 import is_arithmetic_progression from solutions.lesson04.task2 import merge_intervals from solutions.lesson04.task3 import find_single_number diff --git a/tests/test_lesson08_tasks.py b/tests/test_lesson08_tasks.py index 8f22770e..ee9116b6 100644 --- a/tests/test_lesson08_tasks.py +++ b/tests/test_lesson08_tasks.py @@ -1,4 +1,3 @@ -import pytest import math import time diff --git a/tests_hw/test_hw1_task1.py b/tests_hw/test_hw1_task1.py index 100ecd3b..03cbe9b8 100644 --- a/tests_hw/test_hw1_task1.py +++ b/tests_hw/test_hw1_task1.py @@ -1,8 +1,9 @@ -import pytest from typing import Any +import pytest + # Импортируем тестируемую функцию и константу -from homeworks.hw1.aggregate_segmentation import aggregate_segmentation, ALLOWED_TYPES +from homeworks.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation class TestAggregateSegmentation: @@ -27,7 +28,7 @@ def test_valid_segments_different_audio_ids(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert "audio-123" in valid assert "audio-456" in valid @@ -53,7 +54,7 @@ def test_valid_segments_same_audio_id(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert len(valid["audio-123"]) == 2 assert "seg-1" in valid["audio-123"] @@ -78,7 +79,7 @@ def test_segments_without_voice_all_none(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert valid["audio-123"] == {} @@ -101,7 +102,7 @@ def test_mixed_voice_and_no_voice_segments(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert len(valid["audio-123"]) == 1 # Только сегмент с голосом assert "seg-1" in valid["audio-123"] @@ -126,7 +127,7 @@ def test_duplicate_segments_same_data(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert len(valid["audio-123"]) == 1 @@ -149,15 +150,18 @@ def test_missing_segment_id(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert len(valid.get("audio-123", {})) == 0 - @pytest.mark.parametrize("field,value", [ - ("type", 123), # type не строка - ("segment_start", "not_float"), # start не float - ("segment_end", "not_float"), # end не float - ]) + @pytest.mark.parametrize( + "field,value", + [ + ("type", 123), # type не строка + ("segment_start", "not_float"), # start не float + ("segment_end", "not_float"), # end не float + ], + ) def test_invalid_field_types(self, field: str, value: Any): """Тест невалидных типов полей.""" base_segment = { @@ -169,19 +173,22 @@ def test_invalid_field_types(self, field: str, value: Any): } base_segment[field] = value data = [base_segment] - + valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert len(valid) == 0 - @pytest.mark.parametrize("segment_start,segment_end,type_val", [ - (None, 2.5, "voice_human"), # Только start None - (0.5, None, "voice_human"), # Только end None - (0.5, 2.5, None), # Только type None - (None, None, "voice_human"), # Только type не None - (0.5, None, None), # Только start не None - ]) + @pytest.mark.parametrize( + "segment_start,segment_end,type_val", + [ + (None, 2.5, "voice_human"), # Только start None + (0.5, None, "voice_human"), # Только end None + (0.5, 2.5, None), # Только type None + (None, None, "voice_human"), # Только type не None + (0.5, None, None), # Только start не None + ], + ) def test_partial_none_values(self, segment_start: Any, segment_end: Any, type_val: Any): """Тест частичных None значений (должны быть невалидными).""" data = [ @@ -194,7 +201,7 @@ def test_partial_none_values(self, segment_start: Any, segment_end: Any, type_va } ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert len(valid) == 0 @@ -210,7 +217,7 @@ def test_type_not_in_allowed_types(self): } ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert len(valid) == 0 @@ -233,7 +240,7 @@ def test_duplicate_segments_different_data(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid # Проверяем, что валидные сегменты не добавлены assert len(valid.get("audio-123", {})) == 0 @@ -257,7 +264,7 @@ def test_missing_audio_id(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert "audio-123" in valid assert len(valid["audio-123"]) == 1 @@ -265,7 +272,7 @@ def test_missing_audio_id(self): def test_empty_input(self): """Тест пустого входного списка.""" valid, invalid = aggregate_segmentation([]) - + assert len(valid) == 0 assert len(invalid) == 0 @@ -288,7 +295,7 @@ def test_audio_id_with_mixed_valid_invalid_segments(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert len(valid) == 0 @@ -318,7 +325,7 @@ def test_multiple_invalid_segments_same_audio_id(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert "audio-123" in invalid assert "audio-456" not in invalid assert invalid.count("audio-123") == 1 # Только один раз @@ -344,7 +351,7 @@ def test_time_validation_edge_cases(self): }, ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert len(valid) == 2 @@ -356,16 +363,18 @@ def test_large_dataset(self): for i in range(100): audio_id = f"audio-{i}" for j in range(10): - data.append({ - "audio_id": audio_id, - "segment_id": f"seg-{i}-{j}", - "segment_start": float(j), - "segment_end": float(j + 1), - "type": "voice_human" if j % 2 == 0 else "voice_bot", - }) - + data.append( + { + "audio_id": audio_id, + "segment_id": f"seg-{i}-{j}", + "segment_start": float(j), + "segment_end": float(j + 1), + "type": "voice_human" if j % 2 == 0 else "voice_bot", + } + ) + valid, invalid = aggregate_segmentation(data) - + assert len(valid) == 100 assert len(invalid) == 0 assert all(len(segments) == 10 for segments in valid.values()) @@ -383,7 +392,7 @@ def test_mixed_allowed_types(self): for i, voice_type in enumerate(ALLOWED_TYPES) ] valid, invalid = aggregate_segmentation(data) - + assert len(invalid) == 0 assert len(valid) == len(ALLOWED_TYPES) @@ -399,7 +408,7 @@ def test_negative_time_values(self): } ] valid, invalid = aggregate_segmentation(data) - + # В текущей реализации это считается валидным assert "audio-123" not in invalid assert "audio-123" in valid @@ -416,9 +425,9 @@ def test_start_greater_than_end(self): } ] valid, invalid = aggregate_segmentation(data) - + # В текущей реализации это считается валидным assert "audio-123" not in invalid assert "audio-123" in valid assert valid["audio-123"]["seg-1"]["start"] == 5.0 - assert valid["audio-123"]["seg-1"]["end"] == 2.0 \ No newline at end of file + assert valid["audio-123"]["seg-1"]["end"] == 2.0 diff --git a/tests_hw/test_hw1_task4.py b/tests_hw/test_hw1_task4.py index c5e70ece..b48652af 100644 --- a/tests_hw/test_hw1_task4.py +++ b/tests_hw/test_hw1_task4.py @@ -1,6 +1,7 @@ -import pytest from unittest.mock import Mock +import pytest + # Импортируем тестируемую функцию from homeworks.hw1.cache import lru_cache @@ -16,23 +17,20 @@ """ ARGS_DICT: dict[str, list[tuple[object]]] = { - - 'one_int_without_repeating_args_small': [ - (2), + "one_int_without_repeating_args_small": [ + (2), (1), ], - - 'one_int_with_repeating_args_small': [ - (2), - (1), - (2), - (1), + "one_int_with_repeating_args_small": [ + (2), + (1), + (2), + (1), (1), ], - - 'one_int_with_repeating_args_huge': [ - (1), #c - (2), #c + "one_int_with_repeating_args_huge": [ + (1), # c + (2), # c (2), (1), (2), @@ -41,79 +39,75 @@ (2), (1), (1), - (3), #c - (1), + (3), # c + (1), (1), (3), - (2), #c - (1), #c + (2), # c + (1), # c (1), (1), (1), - (3), #c + (3), # c (1), (3), - (2), #c - (1), #c - (3), #c - (2), #c - (1), #c + (2), # c + (1), # c + (3), # c + (2), # c + (1), # c (1), ], - - 'three_str_with_repeating_args_small': [ - ('first', 'second', 'third'), #1 o - ('first', 'third', 'second'), #2 o - ('third', 'first', 'third'), #3 o - ('first', 'second', 'third'), #1 o - ('first', 'third', 'second'), #2 o - ('first', 'second', 'first'), #4 o - ('third', 'first', 'third'), #3 o - ('first', 'second', 'third'), #1 o + "three_str_with_repeating_args_small": [ + ("first", "second", "third"), # 1 o + ("first", "third", "second"), # 2 o + ("third", "first", "third"), # 3 o + ("first", "second", "third"), # 1 o + ("first", "third", "second"), # 2 o + ("first", "second", "first"), # 4 o + ("third", "first", "third"), # 3 o + ("first", "second", "third"), # 1 o ], - - 'two_multi_with_repeating_args_small': [ - (42, 'str'), #o #o - (5.6, TypeError), #o #o - (7 + 7j, None), #o #o + "two_multi_with_repeating_args_small": [ + (42, "str"), # o #o + (5.6, TypeError), # o #o + (7 + 7j, None), # o #o (5.6, TypeError), (7 + 7j, None), (5.6, TypeError), - (42, 'str'), #o + (42, "str"), # o (5.6, TypeError), - (True, False), #o #o - (42, 'str'), #o - (7 + 7j, None), #o #o - (True, False), #o - (5.6, TypeError), #o #o - (42, 'str'), #o #o - (42, 'str'), - (7 + 7j, None), #o #o + (True, False), # o #o + (42, "str"), # o + (7 + 7j, None), # o #o + (True, False), # o + (5.6, TypeError), # o #o + (42, "str"), # o #o + (42, "str"), + (7 + 7j, None), # o #o (7 + 7j, None), - (42, 'str'), - (True, False), #o #o - ] + (42, "str"), + (True, False), # o #o + ], } """Тесты для декоратора LRU-cache""" -@pytest.mark.parametrize("capacity, call_count_expected, call_args", [ - (2, 2, ARGS_DICT['one_int_without_repeating_args_small']), - (2, 2, ARGS_DICT['one_int_with_repeating_args_small']), - (2, 11, ARGS_DICT['one_int_with_repeating_args_huge']), - (2, 8, ARGS_DICT['three_str_with_repeating_args_small']), - (2, 12, ARGS_DICT['two_multi_with_repeating_args_small']), - (5, 3, ARGS_DICT['one_int_with_repeating_args_huge']), - (3, 6, ARGS_DICT['three_str_with_repeating_args_small']), - (3, 9, ARGS_DICT['two_multi_with_repeating_args_small']), -]) -def test_cache( - capacity: int, - call_count_expected: int, - call_args: list[tuple[object]] -) -> None: - +@pytest.mark.parametrize( + "capacity, call_count_expected, call_args", + [ + (2, 2, ARGS_DICT["one_int_without_repeating_args_small"]), + (2, 2, ARGS_DICT["one_int_with_repeating_args_small"]), + (2, 11, ARGS_DICT["one_int_with_repeating_args_huge"]), + (2, 8, ARGS_DICT["three_str_with_repeating_args_small"]), + (2, 12, ARGS_DICT["two_multi_with_repeating_args_small"]), + (5, 3, ARGS_DICT["one_int_with_repeating_args_huge"]), + (3, 6, ARGS_DICT["three_str_with_repeating_args_small"]), + (3, 9, ARGS_DICT["two_multi_with_repeating_args_small"]), + ], +) +def test_cache(capacity: int, call_count_expected: int, call_args: list[tuple[object]]) -> None: mock_func = Mock() func_cached = lru_cache(capacity=capacity)(mock_func) @@ -122,18 +116,16 @@ def test_cache( assert mock_func.call_count == call_count_expected -def test_exception_value_error( -) -> None: - + +def test_exception_value_error() -> None: capacity: float = 0.4 mock_func = Mock() with pytest.raises(ValueError): lru_cache(capacity=capacity)(mock_func) -def test_exception_type_error( -) -> None: - + +def test_exception_type_error() -> None: capacity = None mock_func = Mock() with pytest.raises(TypeError): - lru_cache(capacity=capacity)(mock_func) \ No newline at end of file + lru_cache(capacity=capacity)(mock_func) diff --git a/tests_hw/test_hw1_tasks.py b/tests_hw/test_hw1_tasks.py index ebc3771c..e4fd76cb 100644 --- a/tests_hw/test_hw1_tasks.py +++ b/tests_hw/test_hw1_tasks.py @@ -1,17 +1,15 @@ -import pytest import uuid -from unittest.mock import MagicMock, patch, Mock +from unittest.mock import MagicMock, Mock, patch + +import pytest -from homeworks.hw1.aggregate_segmentation import aggregate_segmentation, ALLOWED_TYPES +from homeworks.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation from homeworks.hw1.backoff import backoff from homeworks.hw1.cache import lru_cache from homeworks.hw1.convert_exception import convert_exceptions_to_api_compitable_ones -from tests_hw.hw1_test_data.cache_test_data import ( - TESTCASE_DATA, - TESTCASE_IDS, -) -NAME_BACKOFF_MODULE = "homeworks.hw1.backoff" # название модуля с backoff +NAME_BACKOFF_MODULE = "homeworks.hw1.backoff" # название модуля с backoff + def test_valid_segments() -> None: """Тест: валидные сегменты.""" @@ -32,53 +30,53 @@ def test_valid_segments() -> None: "segment_id": segment_id_1, "segment_start": 0.0, "segment_end": 1.0, - "type": list_allow_types[0] + "type": list_allow_types[0], }, { "audio_id": audio_id_1, "segment_id": segment_id_2, "segment_start": 2.5, "segment_end": 3.5, - "type": list_allow_types[1] + "type": list_allow_types[1], }, { "audio_id": audio_id_2, "segment_id": segment_id_3, "segment_start": 4.5, "segment_end": 4.6, - "type": list_allow_types[0] + "type": list_allow_types[0], }, { "audio_id": audio_id_2, "segment_id": segment_id_4, "segment_start": 5.5, "segment_end": 6.5, - "type": list_allow_types[1] + "type": list_allow_types[1], }, { "audio_id": audio_id_3, "segment_id": segment_id_5, "segment_start": None, "segment_end": None, - "type": None + "type": None, }, { "audio_id": "audio3", "segment_id": "seg5", "segment_start": 0.0, "segment_end": 1.0, - "type": "invalid_type" + "type": "invalid_type", }, ] expected_valid = { audio_id_1: { segment_id_1: {"start": 0.0, "end": 1.0, "type": list_allow_types[0]}, - segment_id_2: {"start": 2.5, "end": 3.5, "type": list_allow_types[1]} + segment_id_2: {"start": 2.5, "end": 3.5, "type": list_allow_types[1]}, }, audio_id_2: { segment_id_3: {"start": 4.5, "end": 4.6, "type": list_allow_types[0]}, - segment_id_4: {"start": 5.5, "end": 6.5, "type": list_allow_types[1]} + segment_id_4: {"start": 5.5, "end": 6.5, "type": list_allow_types[1]}, }, audio_id_3: {}, } @@ -88,6 +86,7 @@ def test_valid_segments() -> None: assert result_valid == expected_valid assert result_forbidden == expected_forbidden + def test_convert_matching_exception() -> None: """Тест: исключение заменяется на API-совместимое.""" @@ -97,7 +96,7 @@ class ApiValueError(Exception): @convert_exceptions_to_api_compitable_ones({ValueError: ApiValueError}) def func(): raise ValueError("Внутренняя ошибка") - + @convert_exceptions_to_api_compitable_ones({ValueError: ApiValueError}) def func2(): raise KeyError("Внутренняя ошибка") @@ -108,7 +107,8 @@ def func2(): with pytest.raises(KeyError): func2() -@patch(NAME_BACKOFF_MODULE + '.sleep') + +@patch(NAME_BACKOFF_MODULE + ".sleep") def test_exponential_backoff_and_jitter(mock_sleep: MagicMock) -> None: """Тест: задержки увеличиваются, но не выше timeout_max и к ним добавляется дрожь.""" attempts = 0 @@ -116,12 +116,7 @@ def test_exponential_backoff_and_jitter(mock_sleep: MagicMock) -> None: retry_amount = 4 timeouts = [1, 2, 4, 4] - @backoff( - retry_amount=retry_amount, - timeout_start=1, - timeout_max=timeout_max, - backoff_scale=2.0 - ) + @backoff(retry_amount=retry_amount, timeout_start=1, timeout_max=timeout_max, backoff_scale=2.0) def func(): nonlocal attempts attempts += 1 @@ -138,22 +133,23 @@ def func(): for av_time, args in zip(timeouts, args_list): count_more_av_time += args > av_time assert av_time <= args <= av_time + 0.5 - - assert count_more_av_time # есть добавление "дрожи" + + assert count_more_av_time # есть добавление "дрожи" + def test_success() -> None: capacity = 2 - call_args = [ + call_args = [ (1, 2), (1, 2), (2, 2), ] call_count_expected = 2 - + mock_func = Mock() func_cached = lru_cache(capacity=capacity)(mock_func) for args in call_args: func_cached(args) - assert mock_func.call_count == call_count_expected \ No newline at end of file + assert mock_func.call_count == call_count_expected From a7218e76491b80de4fa42a3f2b81f4dd8be5a7e5 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sun, 30 Nov 2025 01:03:47 +0300 Subject: [PATCH 13/25] Hw1 done --- homeworks/hw1/aggregate_segmentation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/homeworks/hw1/aggregate_segmentation.py b/homeworks/hw1/aggregate_segmentation.py index e2c7c4b6..839fc120 100644 --- a/homeworks/hw1/aggregate_segmentation.py +++ b/homeworks/hw1/aggregate_segmentation.py @@ -8,8 +8,10 @@ def check_validation(audio_segment: dict, used_id: list[list[str, str]]) -> list[bool, int]: """Проверка данных на валидность Args: - audio_segment: словарь, в котором хранится сегемент который в данный момент находится на проверкеимеет те же поля, что и segmentation_data - used_id : cписок сегментов прошедших проверку хранит внутри массивы в каждом из которых первыйм идет audio_id вторым segment_id + audio_segment: словарь, в котором хранится сегемент который в данный момент находится \ + на проверкеимеет те же поля, что и segmentation_data + used_id : cписок сегментов прошедших проверку хранит внутри массивы в каждом\ + из которых первыйм идет audio_id вторым segment_id Return: [True,0] если значение валидно и на всех полях не None [True,1] если значение валидно и на во всех полях None From b3c0231cded4d4e39abf5b0f9056ed2844158442 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 6 Dec 2025 10:37:38 +0300 Subject: [PATCH 14/25] Vec2D Done --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 377 +++++++++++++++++++++++++++++++++ conditions/lesson11/task.md | 2 +- solutions/lesson11/task1.py | 109 +++++++++- tests_hw/test_hw1.py | 54 +++++ 5 files changed, 536 insertions(+), 6 deletions(-) create mode 100644 tests_hw/test_hw1.py diff --git a/.coverage b/.coverage index fc3d9e0069f2e1d9bbc99f7175f7a89cd90b0224..1d495755a6c4cfd6adb3ba8c9e97674175bd1d51 100644 GIT binary patch literal 53248 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 delta 607 zcmZ{fO-lk%6o#+Po!f`Z93>Y)AGE8WPa-IaY8Ne{TvP_ZFb&sHL`QV!V^j76BFeqC zu1zpMAd@yNS}H99cTrFasYMZFXC#pcTwTsR&-)&p^tq5e7p~}nmq|qe7qBWn%ct^` zRJ45F8;PlmlB31U>C((V*og66BNo=jB@&OAih?)V2_Z}>5x9ha{31`wXHp4u3liO?C>vOx?Qi6o$+93tS7dvf zY{OMtr>KfVmy@WgQk|kH?uVTFrl!IsMNtg=7gd&9>lN0!;_LG=un2rX1s>rZ@?fD| z4(2?Jy4pHfVVZoFT^`XMXrYB1RI3ujp<9%X=Slu&M(7Z591z^@g}azV1(H4dyq?cg kPCBsofxtI>z#Ej|2_CTc7W{Uvi?P~6#o8lUuQ~tNFZPkmiU0rr diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 3e5591d3..765066a4 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -590,6 +590,383 @@ "test = None\n", "isinstance(test, NoneType)" ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f6c933fd", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "Vector2D.__init__() missing 2 required positional arguments: '_Vector2D__abscissa' and '_Vector2D__ordinate'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[15], line 41\u001b[0m\n\u001b[0;32m 37\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;241m0\u001b[39m\n\u001b[0;32m 39\u001b[0m \u001b[38;5;66;03m# ваш код\u001b[39;00m\n\u001b[1;32m---> 41\u001b[0m vec\u001b[38;5;241m=\u001b[39m\u001b[43mVector2D\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 42\u001b[0m \u001b[38;5;28mprint\u001b[39m(vec)\n", + "\u001b[1;31mTypeError\u001b[0m: Vector2D.__init__() missing 2 required positional arguments: '_Vector2D__abscissa' and '_Vector2D__ordinate'" + ] + } + ], + "source": [ + "class Vector2D:\n", + " __abscissa : float = 0.0\n", + " __ordinate : float = 0.0\n", + " def __init__(self, abscissa: float, ordinate: float):\n", + " self.abscissa = abscissa\n", + " self.ordinate = ordinate\n", + " def get_x(self):\n", + " return self.abscissa\n", + " def get_y(self):\n", + " return self.ordinate\n", + " property(get_x)\n", + " property(get_y)\n", + " def conj(self) -> \"Vector2D\":\n", + " # ваш код\n", + " return Vector2D()\n", + " def __repr__(self):\n", + " return f\"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})\"\n", + " def __eq__(self, other: \"Vector2D\"):\n", + " return self.abscissa == other.abscissa and self.ordinate == other.ordinate\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return self.abscissa != other.abscissa or self.ordinate != other.ordinate\n", + " def __lt__(self, other: \"Vector2D\"):\n", + " return self.abscissa < other.abscissa or (self.ordinate < other.ordinate and self.abscissa == other.abscissa)\n", + " def __gt__(self, other: \"Vector2D\"):\n", + " return self.abscissa > other.abscissa or (self.ordinate > other.ordinate and self.abscissa == other.abscissa)\n", + " def __le__(self, other: \"Vector2D\"):\n", + " return not (self > other)\n", + " def __ge__(self, other: \"Vector2D\"):\n", + " return not (self < other)\n", + " def __abs__(self):\n", + " return (self.absciss**2 + self.ordinate**2) ** 0.5\n", + " def __bool__(self):\n", + " return abs(self) != 0\n", + " \n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " # ваш код\n", + " return 0\n", + "\n", + "vec=Vector2D()\n", + "print(vec)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "750d5ad3", + "metadata": {}, + "outputs": [ + { + "ename": "AttributeError", + "evalue": "'function' object has no attribute '__mro__'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mAttributeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[28], line 2\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mdataclasses\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m dataclass\n\u001b[1;32m----> 2\u001b[0m \u001b[38;5;28;43;01mclass\u001b[39;49;00m\u001b[38;5;250;43m \u001b[39;49m\u001b[38;5;21;43;01mVector2D\u001b[39;49;00m\u001b[43m:\u001b[49m\n\u001b[0;32m 3\u001b[0m \u001b[43m \u001b[49m\u001b[43m__abscissa\u001b[49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0.0\u001b[39;49m\n\u001b[0;32m 4\u001b[0m \u001b[43m \u001b[49m\u001b[43m__ordinate\u001b[49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0.0\u001b[39;49m\n", + "Cell \u001b[1;32mIn[28], line 5\u001b[0m, in \u001b[0;36mVector2D\u001b[1;34m()\u001b[0m\n\u001b[0;32m 3\u001b[0m __abscissa : \u001b[38;5;28mfloat\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.0\u001b[39m\n\u001b[0;32m 4\u001b[0m __ordinate : \u001b[38;5;28mfloat\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0.0\u001b[39m\n\u001b[1;32m----> 5\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;129;43m@dataclass\u001b[39;49m\n\u001b[0;32m 6\u001b[0m \u001b[43m\u001b[49m\u001b[38;5;28;43;01mdef\u001b[39;49;00m\u001b[38;5;250;43m \u001b[39;49m\u001b[38;5;21;43m__init__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mabscissa\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mordinate\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mfloat\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m:\u001b[49m\n\u001b[0;32m 7\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__abscissa\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mabscissa\u001b[49m\n\u001b[0;32m 8\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__ordinate\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m \u001b[49m\u001b[43mordinate\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\dataclasses.py:1305\u001b[0m, in \u001b[0;36mdataclass\u001b[1;34m(cls, init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots, weakref_slot)\u001b[0m\n\u001b[0;32m 1302\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m wrap\n\u001b[0;32m 1304\u001b[0m \u001b[38;5;66;03m# We're called as @dataclass without parens.\u001b[39;00m\n\u001b[1;32m-> 1305\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mwrap\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\dataclasses.py:1295\u001b[0m, in \u001b[0;36mdataclass..wrap\u001b[1;34m(cls)\u001b[0m\n\u001b[0;32m 1294\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mwrap\u001b[39m(\u001b[38;5;28mcls\u001b[39m):\n\u001b[1;32m-> 1295\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m_process_class\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mcls\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minit\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mrepr\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43meq\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43morder\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43munsafe_hash\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1296\u001b[0m \u001b[43m \u001b[49m\u001b[43mfrozen\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmatch_args\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mkw_only\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mslots\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1297\u001b[0m \u001b[43m \u001b[49m\u001b[43mweakref_slot\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\dataclasses.py:961\u001b[0m, in \u001b[0;36m_process_class\u001b[1;34m(cls, init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, slots, weakref_slot)\u001b[0m\n\u001b[0;32m 959\u001b[0m all_frozen_bases \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 960\u001b[0m has_dataclass_bases \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m--> 961\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m b \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28;43mcls\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[38;5;18;43m__mro__\u001b[39;49m[\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m:\u001b[38;5;241m0\u001b[39m:\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m]:\n\u001b[0;32m 962\u001b[0m \u001b[38;5;66;03m# Only process classes that have been processed by our\u001b[39;00m\n\u001b[0;32m 963\u001b[0m \u001b[38;5;66;03m# decorator. That is, they have a _FIELDS attribute.\u001b[39;00m\n\u001b[0;32m 964\u001b[0m base_fields \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mgetattr\u001b[39m(b, _FIELDS, \u001b[38;5;28;01mNone\u001b[39;00m)\n\u001b[0;32m 965\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m base_fields \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "\u001b[1;31mAttributeError\u001b[0m: 'function' object has no attribute '__mro__'" + ] + } + ], + "source": [ + "from dataclasses import dataclass\n", + "class Vector2D:\n", + " __abscissa : float = 0.0\n", + " __ordinate : float = 0.0\n", + " @dataclass\n", + " def __init__(self, abscissa: float, ordinate: float):\n", + " self.__abscissa = abscissa\n", + " self.__ordinate = ordinate\n", + " def get_x(self):\n", + " return self.__abscissa\n", + " def get_y(self):\n", + " return self.__ordinate\n", + " property(get_x)\n", + " property(get_y)\n", + " def conj(self) -> \"Vector2D\":\n", + " # ваш код\n", + " return Vector2D()\n", + " def __repr__(self):\n", + " return f\"Vector2D(__abscissa={self.__abscissa}, __ordinate={self.__ordinate})\"\n", + " def __eq__(self, other: \"Vector2D\"):\n", + " return self.__abscissa == other.__abscissa and self.__ordinate == other.__ordinate\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return self.__abscissa != other.__abscissa or self.__ordinate != other.__ordinate\n", + " def __lt__(self, other: \"Vector2D\"):\n", + " return self.__abscissa < other.__abscissa or (self.__ordinate < other.__ordinate and self.__abscissa == other.__abscissa)\n", + " def __gt__(self, other: \"Vector2D\"):\n", + " return self.__abscissa > other.__abscissa or (self.__ordinate > other.__ordinate and self.__abscissa == other.__abscissa)\n", + " def __le__(self, other: \"Vector2D\"):\n", + " return not (self > other)\n", + " def __ge__(self, other: \"Vector2D\"):\n", + " return not (self < other)\n", + " def __abs__(self):\n", + " return (self.__abscissa**2 + self.__ordinate**2) ** 0.5\n", + " def __bool__(self):\n", + " return abs(self) != 0\n", + " \n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " # ваш код\n", + " return 0\n", + "\n", + " # ваш код\n", + "vec = Vector2D()\n", + "print(vec)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "27573b31", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector2D(abscissa=5, ordinate=25)\n" + ] + } + ], + "source": [ + "from dataclasses import dataclass\n", + "@dataclass\n", + "class Vector2D:\n", + " abscissa : float = 0.0\n", + " ordinate : float = 0.0\n", + " # def __init__(self, abscissa: float, ordinate: float):\n", + " # self.abscissa = abscissa\n", + " # self.ordinate = ordinate\n", + " # def __repr__(self):\n", + " # return f\"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})\"\n", + " # def __eq__(self, other: \"Vector2D\"):\n", + " # return self.abscissa == other.abscissa and self.ordinate == other.ordinate\n", + " def get_x(self):\n", + " return self.abscissa\n", + " def get_y(self):\n", + " return self.ordinate\n", + " def conj(self) -> \"Vector2D\":\n", + " return Vector2D()\n", + "\n", + " property(get_x)\n", + " property(get_y)\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return self.abscissa != other.abscissa or self.ordinate != other.ordinate\n", + " def __lt__(self, other: \"Vector2D\"):\n", + " return self.abscissa < other.abscissa or (self.ordinate < other.ordinate and self.abscissa == other.abscissa)\n", + " def __gt__(self, other: \"Vector2D\"):\n", + " return self.abscissa > other.abscissa or (self.ordinate > other.ordinate and self.abscissa == other.abscissa)\n", + " def __le__(self, other: \"Vector2D\"):\n", + " return not (self > other)\n", + " def __ge__(self, other: \"Vector2D\"):\n", + " return not (self < other)\n", + " def __abs__(self):\n", + " return (self.abscissa**2 + self.ordinate**2) ** 0.5\n", + " def __bool__(self):\n", + " return abs(self) != 0\n", + " def __mul__ (self, mult_amount):\n", + " return Vector2D(self.abscissa * mult_amount, self.ordinate * mult_amount)\n", + " def __rmul__(self, mult_amount):\n", + " return self * mult_amount\n", + " \n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " # ваш код\n", + " return 0\n", + " # ваш код\n", + "print( Vector2D(1,5) * 5)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "ca8141e7", + "metadata": {}, + "outputs": [ + { + "ename": "FrozenInstanceError", + "evalue": "cannot assign to field 'abscissa'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mFrozenInstanceError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[42], line 74\u001b[0m\n\u001b[0;32m 72\u001b[0m \u001b[38;5;66;03m# ваш код\u001b[39;00m\n\u001b[0;32m 73\u001b[0m vec \u001b[38;5;241m=\u001b[39m Vector2D(\u001b[38;5;241m1\u001b[39m,\u001b[38;5;241m2\u001b[39m)\n\u001b[1;32m---> 74\u001b[0m \u001b[43mvec\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mabscissa\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m2\u001b[39m\n", + "File \u001b[1;32m:11\u001b[0m, in \u001b[0;36m__setattr__\u001b[1;34m(self, name, value)\u001b[0m\n", + "\u001b[1;31mFrozenInstanceError\u001b[0m: cannot assign to field 'abscissa'" + ] + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from math import isclose, acos, degrees\n", + "@dataclass(eq = False, frozen = True)\n", + "class Vector2D:\n", + " abscissa : float = 0.0\n", + " ordinate : float = 0.0\n", + " # def __init__(self, abscissa: float, ordinate: float):\n", + " # self.abscissa = abscissa\n", + " # self.ordinate = ordinate\n", + " # def __repr__(self):\n", + " # return f\"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})\"\n", + " def __post_init__(self):\n", + " object.__setattr__(self, \"abscissa\", float(self.abscissa))\n", + " object.__setattr__(self, \"ordinate\", float(self.ordinate))\n", + " @property\n", + " def get_x(self):\n", + " return self.abscissa\n", + " @property\n", + " def get_y(self):\n", + " return self.ordinate\n", + " def __eq__(self, other: \"Vector2D\"):\n", + " \"\"\"We use float numbers so we want to check equality by using isclose() while dataclass is using ==\"\"\"\n", + " return isclose(self.abscissa, other.abscissa) and isclose(self.ordinate, other.ordinate)\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return not (self == other)\n", + " def __lt__(self, other: \"Vector2D\"):\n", + " return self.abscissa < other.abscissa or (self.ordinate < other.ordinate and self.abscissa == other.abscissa)\n", + " def __gt__(self, other: \"Vector2D\"):\n", + " return self.abscissa > other.abscissa or (self.ordinate > other.ordinate and self.abscissa == other.abscissa)\n", + " def __le__(self, other: \"Vector2D\"):\n", + " return not (self > other)\n", + " def __ge__(self, other: \"Vector2D\"):\n", + " return not (self < other)\n", + " def __abs__(self):\n", + " return (self.abscissa**2 + self.ordinate**2) ** 0.5\n", + " def __bool__(self):\n", + " return abs(self) != 0\n", + " def __mul__ (self: \"Vector2D\", mult_amount):\n", + " return Vector2D(self.abscissa * mult_amount, self.ordinate * mult_amount)\n", + " def __rmul__ (self, mult_amount):\n", + " return self * mult_amount\n", + " def __truediv__ (self, mult_amount):\n", + " return Vector2D(self.abscissa / mult_amount, self.ordinate / mult_amount)\n", + " def __add__(self, other: \"Vector2D\"):\n", + " return Vector2D(self.abscissa + other.abscissa, self.ordinate + other.ordinate) \n", + " def __add__(self, add_amount):\n", + " return Vector2D(self.abscissa + add_amount, self.ordinate + add_amount)\n", + " def __radd__(self, add_amount):\n", + " return self + add_amount\n", + " def __sub__(self, sub_amount):\n", + " return Vector2D(self.abscissa - sub_amount, self.ordinate - sub_amount)\n", + " def __sub__(self, other: \"Vector2D\"):\n", + " return Vector2D(self.abscissa - other.abscissa, self.ordinate - other.ordinate)\n", + " def __neg__(self):\n", + " return self * -1\n", + " def __float__(self):\n", + " return abs(self)\n", + " def __int__(self):\n", + " return int(abs(self))\n", + " def __complex__(self):\n", + " return complex(self.abscissa, self.ordinate)\n", + " def __matmul__(self, other: \"Vector2D\"): #@ skalar mult\n", + " return self.abscissa * other.abscissa + self.ordinate * other.ordinate\n", + "\n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " if isclose(abs(other), 0):\n", + " raise ValueError(\"You cannot get angle in between given vector and a zero vector\")\n", + " return degrees(acos((self @ other) / (abs(self) * abs(other))))\n", + " def conj(self):\n", + " return Vector2D(self.abscissa, self.ordinate * -1)\n", + "\n", + " # ваш код\n", + "vec = Vector2D(1,2)\n", + "vec.abscissa = 2" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "134eb0ef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector2D(abscissa=0.0, ordinate=0.0)\n" + ] + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from math import isclose, acos, degrees\n", + "@dataclass(eq = False, frozen = True)\n", + "class Vector2D:\n", + " abscissa : float = 0.\n", + " ordinate : float = 0.\n", + " # def __init__(self, abscissa: float, ordinate: float):\n", + " # self.abscissa = abscissa\n", + " # self.ordinate = ordinate\n", + " # def __repr__(self):\n", + " # return f\"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})\"\n", + " def __post_init__(self):\n", + " object.__setattr__(self, \"abscissa\", float(self.abscissa))\n", + " object.__setattr__(self, \"ordinate\", float(self.ordinate))\n", + " @property\n", + " def get_x(self):\n", + " return self.abscissa\n", + " @property\n", + " def get_y(self):\n", + " return self.ordinate\n", + " def __eq__(self, other: \"Vector2D\"):\n", + " \"\"\"We use float numbers so we want to check equality by using isclose() while dataclass is using ==\"\"\"\n", + " return isclose(self.abscissa, other.abscissa) and isclose(self.ordinate, other.ordinate)\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return not (self == other)\n", + " def __lt__(self, other: \"Vector2D\"):\n", + " return self.abscissa < other.abscissa or (self.ordinate < other.ordinate and self.abscissa == other.abscissa)\n", + " def __gt__(self, other: \"Vector2D\"):\n", + " return self.abscissa > other.abscissa or (self.ordinate > other.ordinate and self.abscissa == other.abscissa)\n", + " def __le__(self, other: \"Vector2D\"):\n", + " return not (self > other)\n", + " def __ge__(self, other: \"Vector2D\"):\n", + " return not (self < other)\n", + " def __abs__(self):\n", + " return (self.abscissa**2 + self.ordinate**2) ** 0.5\n", + " def __bool__(self):\n", + " return abs(self) != 0\n", + " def __mul__ (self: \"Vector2D\", mult_amount):\n", + " return Vector2D(self.abscissa * mult_amount, self.ordinate * mult_amount)\n", + " def __rmul__ (self, mult_amount):\n", + " return self * mult_amount\n", + " def __truediv__ (self, mult_amount):\n", + " return Vector2D(self.abscissa / mult_amount, self.ordinate / mult_amount)\n", + " def __add__(self, other: \"Vector2D\"):\n", + " return Vector2D(self.abscissa + other.abscissa, self.ordinate + other.ordinate) \n", + " def __add__(self, add_amount):\n", + " return Vector2D(self.abscissa + add_amount, self.ordinate + add_amount)\n", + " def __radd__(self, add_amount):\n", + " return self + add_amount\n", + " def __sub__(self, sub_amount):\n", + " return Vector2D(self.abscissa - sub_amount, self.ordinate - sub_amount)\n", + " def __sub__(self, other: \"Vector2D\"):\n", + " return Vector2D(self.abscissa - other.abscissa, self.ordinate - other.ordinate)\n", + " def __neg__(self):\n", + " return self * -1\n", + " def __float__(self):\n", + " return abs(self)\n", + " def __int__(self):\n", + " return int(abs(self))\n", + " def __complex__(self):\n", + " return complex(self.abscissa, self.ordinate)\n", + " def __matmul__(self, other: \"Vector2D\"): #@ skalar mult\n", + " return self.abscissa * other.abscissa + self.ordinate * other.ordinate\n", + "\n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " if isclose(abs(other), 0):\n", + " raise ValueError(\"You cannot get angle in between given vector and a zero vector\")\n", + " return degrees(acos((self @ other) / (abs(self) * abs(other))))\n", + " def conj(self):\n", + " return Vector2D(self.abscissa, self.ordinate * -1)\n", + "\n", + " # ваш код\n", + "print(Vector2D())" + ] } ], "metadata": { diff --git a/conditions/lesson11/task.md b/conditions/lesson11/task.md index e9cb758a..c8234ed9 100644 --- a/conditions/lesson11/task.md +++ b/conditions/lesson11/task.md @@ -21,7 +21,7 @@ ``` - При использовании объекта типа `Vector2D` в качестве аргумента функции `print()` в консоль должна печататься строка, представляющая собой команду создания данного объекта. Пример: - ```python + ```pythonV vector = Vector2D(1, 3) print(vector) # Vector2D(abscissa=1, ordinate=3) diff --git a/solutions/lesson11/task1.py b/solutions/lesson11/task1.py index 6a15f31f..37c67e6a 100644 --- a/solutions/lesson11/task1.py +++ b/solutions/lesson11/task1.py @@ -1,10 +1,109 @@ +from dataclasses import dataclass +from math import isclose, acos, degrees +@dataclass(eq = False, frozen = True) class Vector2D: - def conj(self) -> "Vector2D": - # ваш код - return Vector2D() + abscissa : float = 0. + ordinate : float = 0. + # def __init__(self, abscissa: float, ordinate: float): + # self.abscissa = abscissa + # self.ordinate = ordinate + # def __repr__(self): + # return f"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})" + def __post_init__(self): + object.__setattr__(self, "abscissa", float(self.abscissa)) + object.__setattr__(self, "ordinate", float(self.ordinate)) + @property + def get_x(self): + return self.abscissa + @property + def get_y(self): + return self.ordinate + + + def __ne__(self, other: "Vector2D"): + return not (self == other) + + + + def __eq__(self, other): + if not isinstance(other, Vector2D): + return NotImplemented + return isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) and isclose(self.ordinate, other.ordinate, rel_tol=1e-9, abs_tol=1e-10) + def __lt__(self, other): + if not isinstance(other, Vector2D): + return NotImplemented + return (((self.abscissa < other.abscissa and not(isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10))) + or (isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) + and self.ordinate < other.ordinate and not(isclose(self.ordinate, other.ordinate))) + )) + def __le__(self, other): + return self == other or self < other + def __gt__(self, other): + return not (self <= other) + def __ge__(self, other): + return not (self < other) + + + + def __abs__(self): + return (self.abscissa**2 + self.ordinate**2) ** 0.5 + + + def __bool__(self): + return not(isclose(abs(self), 0, abs_tol= 1e-12)) + + + def __mul__(self, other): + if isinstance(other, (int, float)): + return Vector2D(self.abscissa * other, self.ordinate * other) + return NotImplemented + def __rmul__(self, other): + return self * other + + + + def __truediv__(self, other): + if isinstance(other, (int, float)): + return Vector2D(self.abscissa / other, self.ordinate / other) + return NotImplemented + + def __add__(self, other): + if isinstance(other, Vector2D): + return Vector2D(self.abscissa + other.abscissa, + self.ordinate + other.ordinate) + if isinstance(other, (int, float)): + return Vector2D(self.abscissa + other, self.ordinate + other) + return NotImplemented + + def __radd__(self, other): + return self + other + def __sub__(self, other): + if isinstance(other, Vector2D): + return Vector2D(self.abscissa - other.abscissa, + self.ordinate - other.ordinate) + if isinstance(other, (int, float)): + return Vector2D(self.abscissa - other, self.ordinate - other) + return NotImplemented + def __neg__(self): + return self * -1 + def __float__(self): + return abs(self) + def __int__(self): + return int(abs(self)) + def __complex__(self): + return complex(self.abscissa, self.ordinate) + def __matmul__(self, other: "Vector2D"): + if not isinstance(other, Vector2D): + return NotImplemented + return self.abscissa * other.abscissa + self.ordinate * other.ordinate def get_angle(self, other: "Vector2D") -> float: - # ваш код - return 0 + if not isinstance(other, Vector2D): + raise TypeError("other must be Vector2D") + if isclose(abs(other), 0) or isclose(abs(self), 0): + raise ValueError("You cannot get angle in between given vector and a zero vector") + return (acos((self @ other) / (abs(self) * abs(other)))) + def conj(self): + return Vector2D(self.abscissa, self.ordinate * -1) # ваш код diff --git a/tests_hw/test_hw1.py b/tests_hw/test_hw1.py new file mode 100644 index 00000000..3f598bdb --- /dev/null +++ b/tests_hw/test_hw1.py @@ -0,0 +1,54 @@ +from typing import Any + +import pytest + +# Импортируем тестируемую функцию и константу +from homeworks.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation + +#сюда тупа скопировать свой файл с 1 заданием +#и потом запустить счерез pytest + + + +def test_aggregate_segmentation_with_invalid_type_after_valid_detailed(): + """Тест с дополнительными проверками состояния до и после невалидного сегмента""" + segmentation_data = [ + { + "audio_id": "audio_1", + "segment_id": "segment_1", + "segment_start": 0.0, + "segment_end": 1.0, + "type": "voice_human" + }, + { + "audio_id": "audio_2", # Другой валидный audio_id + "segment_id": "segment_3", + "segment_start": 0.0, + "segment_end": 1.0, + "type": "voice_bot" + }, + { + "audio_id": "audio_1", # Невалидный сегмент + "segment_id": "segment_2", + "segment_start": 1.0, + "segment_end": 2.0, + "type": "invalid_type" + } + ] + + valid_result, invalid_result = aggregate_segmentation(segmentation_data) + + # audio_1 должен быть в невалидных + assert "audio_1" in invalid_result + # audio_1 не должно быть в валидных + assert "audio_1" not in valid_result + # audio_2 должен остаться валидным + assert "audio_2" in valid_result + # В audio_2 должен быть один сегмент + assert len(valid_result["audio_2"]) == 1 + # Проверяем содержимое сегмента audio_2 + assert valid_result["audio_2"]["segment_3"] == { + "start": 0.0, + "end": 1.0, + "type": "voice_bot" + } From 3a812f438e12333860a5e146d4f147de413b6701 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 6 Dec 2025 10:48:03 +0300 Subject: [PATCH 15/25] Vec2D Done --- solutions/lesson11/task1.py | 60 ++++++++++++++++++++++--------------- tests_hw/test_hw1.py | 29 ++++++++---------- 2 files changed, 48 insertions(+), 41 deletions(-) diff --git a/solutions/lesson11/task1.py b/solutions/lesson11/task1.py index 37c67e6a..ee196f66 100644 --- a/solutions/lesson11/task1.py +++ b/solutions/lesson11/task1.py @@ -1,9 +1,12 @@ from dataclasses import dataclass -from math import isclose, acos, degrees -@dataclass(eq = False, frozen = True) +from math import acos, isclose + + +@dataclass(eq=False, frozen=True) class Vector2D: - abscissa : float = 0. - ordinate : float = 0. + abscissa: float = 0.0 + ordinate: float = 0.0 + # def __init__(self, abscissa: float, ordinate: float): # self.abscissa = abscissa # self.ordinate = ordinate @@ -12,55 +15,59 @@ class Vector2D: def __post_init__(self): object.__setattr__(self, "abscissa", float(self.abscissa)) object.__setattr__(self, "ordinate", float(self.ordinate)) + @property def get_x(self): return self.abscissa + @property def get_y(self): return self.ordinate - def __ne__(self, other: "Vector2D"): return not (self == other) - - def __eq__(self, other): if not isinstance(other, Vector2D): return NotImplemented - return isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) and isclose(self.ordinate, other.ordinate, rel_tol=1e-9, abs_tol=1e-10) + return isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) and isclose( + self.ordinate, other.ordinate, rel_tol=1e-9, abs_tol=1e-10 + ) + def __lt__(self, other): if not isinstance(other, Vector2D): return NotImplemented - return (((self.abscissa < other.abscissa and not(isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10))) - or (isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) - and self.ordinate < other.ordinate and not(isclose(self.ordinate, other.ordinate))) - )) + return ( + self.abscissa < other.abscissa + and not (isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10)) + ) or ( + isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) + and self.ordinate < other.ordinate + and not (isclose(self.ordinate, other.ordinate)) + ) + def __le__(self, other): return self == other or self < other + def __gt__(self, other): return not (self <= other) + def __ge__(self, other): return not (self < other) - - def __abs__(self): return (self.abscissa**2 + self.ordinate**2) ** 0.5 - def __bool__(self): - return not(isclose(abs(self), 0, abs_tol= 1e-12)) - + return not (isclose(abs(self), 0, abs_tol=1e-12)) def __mul__(self, other): if isinstance(other, (int, float)): return Vector2D(self.abscissa * other, self.ordinate * other) return NotImplemented + def __rmul__(self, other): return self * other - - def __truediv__(self, other): if isinstance(other, (int, float)): @@ -69,29 +76,33 @@ def __truediv__(self, other): def __add__(self, other): if isinstance(other, Vector2D): - return Vector2D(self.abscissa + other.abscissa, - self.ordinate + other.ordinate) + return Vector2D(self.abscissa + other.abscissa, self.ordinate + other.ordinate) if isinstance(other, (int, float)): return Vector2D(self.abscissa + other, self.ordinate + other) return NotImplemented def __radd__(self, other): return self + other + def __sub__(self, other): if isinstance(other, Vector2D): - return Vector2D(self.abscissa - other.abscissa, - self.ordinate - other.ordinate) + return Vector2D(self.abscissa - other.abscissa, self.ordinate - other.ordinate) if isinstance(other, (int, float)): return Vector2D(self.abscissa - other, self.ordinate - other) return NotImplemented + def __neg__(self): return self * -1 + def __float__(self): return abs(self) + def __int__(self): return int(abs(self)) + def __complex__(self): return complex(self.abscissa, self.ordinate) + def __matmul__(self, other: "Vector2D"): if not isinstance(other, Vector2D): return NotImplemented @@ -102,7 +113,8 @@ def get_angle(self, other: "Vector2D") -> float: raise TypeError("other must be Vector2D") if isclose(abs(other), 0) or isclose(abs(self), 0): raise ValueError("You cannot get angle in between given vector and a zero vector") - return (acos((self @ other) / (abs(self) * abs(other)))) + return acos((self @ other) / (abs(self) * abs(other))) + def conj(self): return Vector2D(self.abscissa, self.ordinate * -1) diff --git a/tests_hw/test_hw1.py b/tests_hw/test_hw1.py index 3f598bdb..9819410b 100644 --- a/tests_hw/test_hw1.py +++ b/tests_hw/test_hw1.py @@ -5,9 +5,8 @@ # Импортируем тестируемую функцию и константу from homeworks.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation -#сюда тупа скопировать свой файл с 1 заданием -#и потом запустить счерез pytest - +# сюда тупа скопировать свой файл с 1 заданием +# и потом запустить счерез pytest def test_aggregate_segmentation_with_invalid_type_after_valid_detailed(): @@ -15,29 +14,29 @@ def test_aggregate_segmentation_with_invalid_type_after_valid_detailed(): segmentation_data = [ { "audio_id": "audio_1", - "segment_id": "segment_1", + "segment_id": "segment_1", "segment_start": 0.0, "segment_end": 1.0, - "type": "voice_human" + "type": "voice_human", }, { "audio_id": "audio_2", # Другой валидный audio_id "segment_id": "segment_3", - "segment_start": 0.0, + "segment_start": 0.0, "segment_end": 1.0, - "type": "voice_bot" + "type": "voice_bot", }, { "audio_id": "audio_1", # Невалидный сегмент "segment_id": "segment_2", "segment_start": 1.0, - "segment_end": 2.0, - "type": "invalid_type" - } + "segment_end": 2.0, + "type": "invalid_type", + }, ] - + valid_result, invalid_result = aggregate_segmentation(segmentation_data) - + # audio_1 должен быть в невалидных assert "audio_1" in invalid_result # audio_1 не должно быть в валидных @@ -47,8 +46,4 @@ def test_aggregate_segmentation_with_invalid_type_after_valid_detailed(): # В audio_2 должен быть один сегмент assert len(valid_result["audio_2"]) == 1 # Проверяем содержимое сегмента audio_2 - assert valid_result["audio_2"]["segment_3"] == { - "start": 0.0, - "end": 1.0, - "type": "voice_bot" - } + assert valid_result["audio_2"]["segment_3"] == {"start": 0.0, "end": 1.0, "type": "voice_bot"} From 850a20491da832517c8e90aabd30457147ac643a Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 6 Dec 2025 10:52:03 +0300 Subject: [PATCH 16/25] Vec2D Done --- .coverage | Bin 53248 -> 53248 bytes tests_hw/test_hw1_task1.py | 44 ------------------------------------- 2 files changed, 44 deletions(-) diff --git a/.coverage b/.coverage index 1d495755a6c4cfd6adb3ba8c9e97674175bd1d51..b6aa25fce47c566141655d76b400482ca7023e32 100644 GIT binary patch delta 608 zcmZozz}&Eac>{}s02}XB2L4t20emm`Ch}SEUfnDxaFCZRk&T6+(S7nnPiu{g{M^*? z{G#mQn2d76nB@GtvecrI_|%Hz)Pj=C{5-va%5YYoVq=hEIaI|-iOJddX=y+up)5cp z`XD7Ts7jI(lQU9*3euQ?3Iah2bWs%~rl%LBrYDx9#uuli=ceYBBtmVCU;^qe1?k{L z(J^_Rzpo}jBtAYfFS8^*9%u&_0|Nsa?==Sg)%<~cFZnF_Ch=YadiM}7OA-svvw>kY z>Ii+fJj)ir0`x0Ttt_fqgkM?1Sb$yys*pxi0rM(LIt$RJKzSWhd7M6Fj${FP6sQ5+ zqw&5RFe5g9ju%uAVB_D!!2gr~BmW)#v;6z{HvxS&kKdk~jfIg@f{m@1;lq9|9#$Yn ziiJ;&;lu6!+Po}4o(!wp8F>bU|FW#Y%s{RNGZUl3$=r2QnHlbV|5si=`D4E@(BvBo n{J;6X@W181&;N}72GHaq{30NO8QIvt6bmbuV%_{^zK8(;e9z5- literal 53248 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 diff --git a/tests_hw/test_hw1_task1.py b/tests_hw/test_hw1_task1.py index 03cbe9b8..e3a4f348 100644 --- a/tests_hw/test_hw1_task1.py +++ b/tests_hw/test_hw1_task1.py @@ -131,28 +131,6 @@ def test_duplicate_segments_same_data(self): assert len(invalid) == 0 assert len(valid["audio-123"]) == 1 - def test_missing_segment_id(self): - """Тест отсутствующего segment_id.""" - data = [ - { - "audio_id": "audio-123", - "segment_id": "", # Пустой ID - "segment_start": 0.5, - "segment_end": 2.5, - "type": "voice_human", - }, - { - "audio_id": "audio-123", - "segment_id": "seg-2", - "segment_start": 1.0, - "segment_end": 3.0, - "type": "voice_bot", - }, - ] - valid, invalid = aggregate_segmentation(data) - - assert "audio-123" in invalid - assert len(valid.get("audio-123", {})) == 0 @pytest.mark.parametrize( "field,value", @@ -276,28 +254,6 @@ def test_empty_input(self): assert len(valid) == 0 assert len(invalid) == 0 - def test_audio_id_with_mixed_valid_invalid_segments(self): - """Тест audio_id с валидными и невалидными сегментами (весь audio_id невалиден).""" - data = [ - { - "audio_id": "audio-123", - "segment_id": "seg-1", - "segment_start": 0.5, - "segment_end": 2.5, - "type": "voice_human", - }, - { - "audio_id": "audio-123", - "segment_id": "", # Невалидный сегмент - "segment_start": 1.0, - "segment_end": 3.0, - "type": "voice_bot", - }, - ] - valid, invalid = aggregate_segmentation(data) - - assert "audio-123" in invalid - assert len(valid) == 0 def test_multiple_invalid_segments_same_audio_id(self): """Тест нескольких невалидных сегментов для одного audio_id (в списке должен быть один раз).""" From 5cebb1c4418f36a28d2fcf7a74a72e2c4c24e565 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 13 Dec 2025 02:13:34 +0300 Subject: [PATCH 17/25] Hw done --- .coverage | Bin 53248 -> 53248 bytes solutions/lesson12/task1.py | 13 ++++++++++--- solutions/lesson12/task2.py | 10 +++++++--- solutions/lesson12/task3.py | 18 ++++++++++++++---- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/.coverage b/.coverage index b6aa25fce47c566141655d76b400482ca7023e32..ac72a85bb09e992bdf7e7960330a053097a67797 100644 GIT binary patch literal 53248 zcmeI4OKcm*8OOO?a+k};4)vrcO5*BmHIic4u^p$V71uFPBMxjgLLZJAdPS`y#w1tf zF8u)Nq)Gx9sM{Xe6h)Du&86rgMGk!=hb9it98&bs`cRWlj%$#<_|_QBH*P3eu2ns55}*%G9L9xsbQql9{zi)yLSLPw(A5x7V24{mK1%jcT5;DQv(sHD&BJ zO7;n3)hZcFxx8iMii^3d>E?>dhPz_bYnL5s5jQ%>gRKrLPV@Gd%H$TIm1`}7#A+#5 zFiWS5qt>ZOUZM7Cu65F_W`Ky5TP|V^V^gj9gi*4VtddpCT29rAO}WL1@Wd03h1Eny zhjhl`eKJc~_=&Xi1~M95*|!8jS-Z4|-Y=BQVs^!HCJl4Fhglo^Jn2?@Te1sxy0($e z6?r#{PR`BQMZ-F2Wy`L$c*71(BDDGi@S1xAT1T)$N2)Druhm4S(f9@}Yt?Iwm%zEA zUB}vJh%XI|xzELNVF8doQZB6G;6RJ&3%mA4EjMcG+O^K!^w`{BEF1l88O}O#9MNj4 zaxr(TY^@Leq|shtXFZ{{1kHN(2BCxH8^;aKlGY%>S(MKk_7a~m*xINL4n%YF zoVl|O!z|%MGwfPL@bm+XHgpzzFc$LmLTjw?h;b~KAg~tHt1Xp_S)3{k_91H@w@T)+ zHC-#ME(;%YAVKKNXi8#ldZW-;4}r|gdNRD0RMbQ&B|Wb3FshAK&8RIQR3FJ3ETi?J zf@M18QhgzLzeTd%qOnTW=SZytx-+FJiQTDKu{nW-g0YidAEbahE<)fs{768;8T zI)OQjDR9k3aP#i3n(m5mz3-R}j*kbG#K*ffavWXA%;2N)#6y1W@|h&3Uw}m)f8`dJ zF{k13{nn|e<7OUKlT|ZUf+-7YaCHYTu;QW#mCXga?DF`IR^LP`dw+Zq2ZiD zjpgIx+^72Hg3J6IOuG8~f<&QU78h>h^-RapCRa^mY$7ZGS^jKidKV2WO&Sr@I9 z;)+$*pI!wfs9G==tJ56^rAhI`igle_bvL@tbS*bmunerio#BCX!gP#m$$~=IODD$V z_X`6Yt;`HQ;wd?VZ{^m~lA5^V4r#4in~c?UCgZ@SUNGx!*Pi-Yg5;XQM!Xz=1sC4! zG{*_DYr=s~tFM5ITXh5%sub$AYVUQz6L`Rv*cBiA&_x7@01+SpM1Tko0U|&IhyW2F z0z`laeDny&l3!Bs{O@Of^|61#16@Rb2oM1xKm>>Y5g-CYfCvx)B0vO)z=x7R)Gzn5 z`d@f#j!SYXS^FD+9n&+Lr?-cpSAKTM$1bsVK9n6Kr-=X&AOb{y2oM1xKm>>Y5g-CY zfCvzQbplbjU#i~*@Q3A;R=W#;pZ|~f5BS)R+5P&P`Zx3)u|LHgiw#F#jUJ7%$j>65 z4POa=FT6*4Lp!TYhJF`vLp|z?>Jc@d{7~5+d@J}=a9iND!0Et%{3|&ttNv#}jIP_2 zz~QlgZ*AwEd*&b5cW^Fquw*Zrg#vt&HRHJD#hm5LA3Wu**u_lNE|(l@eigpq>*li7 z)QVlOPS~ZR^NyV_^Y7)&=Pk#vi!)p1UDG+b6~5YYcvOt`7StXQqdf!KUl<1MS#OzS zGPz>T&1BH^A+KGZ;mEn`gI>EnGvDmG;j!zR@8+)a5lxHH=2!+i#sYpfW9j$W_3a$X zT^KEWVzl`LO^MOw!`UlF8;A37(rfK)ocDIr?h&KC4YdvuK6uuY6M55u?^Z61b9j5ZHLU5qvl!!I`_3J>%!b5h^X)+uqs`}~B1W6f%b*x-9)kff+B^njG1`24 z@PoFe+ryUT?O{#=`6*A15l!zk@$>&N|3M#nmhIQy(x1}rj{Q0IrPxUH*U@}59{G9X zbK&>G&xG&OE@@xUrb6dKqgWCgt3_Kc0%dg0ba>)M^5Ton1 zCGb#SZKvc(kY0EG9}}dF>%yoYZCn>d9uBO{dd#G01`m7gdgJ^*XXXEl3;Vq)U)C#!08=)4t{W-{HCIjq`uIAZ-l8HbL4LhH*jK7=}!cHin@tNE^d2 z=DBv`{2vvhjZ7kfw2?{JbM40YUlXK_=l_r(ZJd{?AZ?tNiXd&Amq9_=7=r;p+8Bef z=i5We`QJYmSew#3Ia)QH9_au7-L^+{>NpV~0z`la5CI}U1c(3;AOb{y2;3e7WJ&hL z@biC(z2}1;x`+S~AOb{y2oM1xKm>>Y5g-CYfCvzQk01eAj%ob+|N7HD_Aa~3{?7i! z{=k0AUS;RlFW^4{Jj1@lzQ&$pUt|t@gqiH~>=64DyO-@?lWd4}u_*KF@9LNJOZs2* z-|H{yFX+$d-v=?ehyW2F0z`la5CI}U1c(3;AOb{y2;4k@&{#my+Q*juH>-|9Dn9yO zRUN^^$lGaU7!vxh)Hww9V?(d6J^k7nC!R0ZZ@zHx`Ijy#gIGE!X$II%WBTgLel-or zc>1U3lmRRokko$6=zlvM>VrgkU*!r@Q<#;id?L~diJsod)r%K?DTk6+o~#^&EJ$_s zKz2k;VAaHJ=!bcuieU;mGfU19%X|6s4Pi*N_vJo^oM zg}n&(0G?w%fV%+SW?yG#;Xc4=cAVL41?~hKW)HIcY%kjd_X0Mv3Ah`OVx26;6#XiQ z(M1G^01+SpM1Tko0U|&IhyW2F0z}{zA%N#(X>5#}(NS(jMz|Rs=4NP!o54YD41=3= znwxFeVrmExwimz!jgo1PwS5(#d)ySeG=;-<5cn~n}{+S|ElYvU#!=Y}zE zbe)@6jGJhbn+P23p%39OH=4#xD8!A*?*mAR!k-0$+ynyL$TBy6e*kU(@X!B!@mpk2 z$PFSu1c(3;AOb{y2oM1xKm>>Y5g-CYKnMZ+{GZPMLeP>Z5g-CYfCvx)B0vO)01+Sp uM1Tkofm?>Y5g-CYfCvx)B0vO$5cogfkvTm8 delta 608 zcmZozz}&Eac>{}s02}XB2L4t20emm`Ch}SEUfnDxaFCZRk&T6+(S7npCu@z2{M^*? z{G#mQn2d76nB@GtvecrI_|%Hz)Pj=C{5-va%5YYoVq=hEIaI|-iOJddX=y+up)5cp z`XD7Ts7jI(lQU9*3euQ?3Iah2bWs%~rl%LBrYDx9#uuli=ceYBBtmVCU;^qe1?k{L z(J^_Rzpo}jBtAYfFS8^*9%u&_0|Nsa?==Sg)%<~cFZnF_Ch=YadiM}7OA-svvw>kY z>Ii+fJj)ir0`x0Ttt_fqgkM?1Sb$yys*pxi0rM(LIt$RJKzSWhd7M6Fj${FP6sQ5+ zqw&5RFe5g9ju%uAVB_D!!2gr~BmW)#v;6z{HvxS&kKdk~jfIg@f{m@1;lq9|9#$Yn ziiJ;&;lu6!+Po}4o(!wp8F>bU|FW#Y%s{RNGZUl3$=r2QnHlbV|5si=`D4E@(BvBo n{J;6X@W181&;N}72GHaq{30NO8QIvt6bmbuV%_{^zK8(;Ypu=y diff --git a/solutions/lesson12/task1.py b/solutions/lesson12/task1.py index d1bb828c..35a26c0c 100644 --- a/solutions/lesson12/task1.py +++ b/solutions/lesson12/task1.py @@ -1,6 +1,13 @@ from typing import Any, Generator, Iterable -def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: - # ваш код - ... +def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: + chunk = [] + for i in iterable: + chunk.append(i) + if len(chunk) == size: + yield tuple(chunk) + chunk = [] + if len(chunk) != 0: + yield tuple(chunk) + diff --git a/solutions/lesson12/task2.py b/solutions/lesson12/task2.py index 3ad802ee..ac54f260 100644 --- a/solutions/lesson12/task2.py +++ b/solutions/lesson12/task2.py @@ -1,6 +1,10 @@ from typing import Any, Generator, Iterable - def circle(iterable: Iterable) -> Generator[Any, None, None]: - # ваш код - ... + saved = [] + for item in iterable: + yield item + saved.append(item) + while saved: + for item in saved: + yield item diff --git a/solutions/lesson12/task3.py b/solutions/lesson12/task3.py index 64c112cc..941ddbc9 100644 --- a/solutions/lesson12/task3.py +++ b/solutions/lesson12/task3.py @@ -1,12 +1,22 @@ import sys +from typing import Any class FileOut: + old_std_out: str='' + def __init__( self, path_to_file: str, - ) -> None: - # ваш код - ... - + ) ->None: + self.path_to_file = path_to_file + def __enter__(self): + self.old_std_out = sys.stdout + self.file = open(self.path_to_file, 'w') + sys.stdout = self.file + return self + def __exit__(self, *_: Any): + sys.stdout = self.old_std_out + self.file.close() + return False # ваш код From 5452ce5235259138ca4bd1522bd7a75606e3a878 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Sat, 13 Dec 2025 02:16:35 +0300 Subject: [PATCH 18/25] forgor ruff check --- solutions/lesson12/task1.py | 3 +-- solutions/lesson12/task2.py | 1 + solutions/lesson12/task3.py | 11 +++++++---- tests/test_lesson11_tasks.py | 5 ++--- tests_hw/test_hw1_task1.py | 2 -- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/solutions/lesson12/task1.py b/solutions/lesson12/task1.py index 35a26c0c..15c0f22f 100644 --- a/solutions/lesson12/task1.py +++ b/solutions/lesson12/task1.py @@ -1,7 +1,7 @@ from typing import Any, Generator, Iterable -def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: +def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: chunk = [] for i in iterable: chunk.append(i) @@ -10,4 +10,3 @@ def chunked(iterable: Iterable, size: int) -> Generator[tuple[Any], None, None]: chunk = [] if len(chunk) != 0: yield tuple(chunk) - diff --git a/solutions/lesson12/task2.py b/solutions/lesson12/task2.py index ac54f260..d9977ad5 100644 --- a/solutions/lesson12/task2.py +++ b/solutions/lesson12/task2.py @@ -1,5 +1,6 @@ from typing import Any, Generator, Iterable + def circle(iterable: Iterable) -> Generator[Any, None, None]: saved = [] for item in iterable: diff --git a/solutions/lesson12/task3.py b/solutions/lesson12/task3.py index 941ddbc9..b3bf3342 100644 --- a/solutions/lesson12/task3.py +++ b/solutions/lesson12/task3.py @@ -3,20 +3,23 @@ class FileOut: - old_std_out: str='' - + old_std_out: str = "" + def __init__( self, path_to_file: str, - ) ->None: + ) -> None: self.path_to_file = path_to_file + def __enter__(self): self.old_std_out = sys.stdout - self.file = open(self.path_to_file, 'w') + self.file = open(self.path_to_file, "w") sys.stdout = self.file return self + def __exit__(self, *_: Any): sys.stdout = self.old_std_out self.file.close() return False + # ваш код diff --git a/tests/test_lesson11_tasks.py b/tests/test_lesson11_tasks.py index 88608f89..ea2f3382 100644 --- a/tests/test_lesson11_tasks.py +++ b/tests/test_lesson11_tasks.py @@ -84,13 +84,12 @@ def test_print(): sys.stdout = old_stdout output = captured_output.getvalue() assert ( - output == "Vector2D(abscissa=1, ordinate=-2)" + output == "Vector2D(abscissa=1, ordinate=-2)" or output == "Vector2D(abscissa=1., ordinate=-2.)" - or output == "Vector2D(abscissa=1.0, ordinate=-2.0)" + or output == "Vector2D(abscissa=1.0, ordinate=-2.0)" ) - @pytest.mark.parametrize( "abscissa1, ordinate1, abscissa2, ordinate2, expected", [ diff --git a/tests_hw/test_hw1_task1.py b/tests_hw/test_hw1_task1.py index e3a4f348..348ec6f8 100644 --- a/tests_hw/test_hw1_task1.py +++ b/tests_hw/test_hw1_task1.py @@ -131,7 +131,6 @@ def test_duplicate_segments_same_data(self): assert len(invalid) == 0 assert len(valid["audio-123"]) == 1 - @pytest.mark.parametrize( "field,value", [ @@ -254,7 +253,6 @@ def test_empty_input(self): assert len(valid) == 0 assert len(invalid) == 0 - def test_multiple_invalid_segments_same_audio_id(self): """Тест нескольких невалидных сегментов для одного audio_id (в списке должен быть один раз).""" data = [ From 8999f5912fde26e0c21c7660f4a95973fd167d81 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Wed, 4 Mar 2026 09:19:32 +0300 Subject: [PATCH 19/25] somekindofbs --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 503 ++++++++++++++++++++++++++++++++- solutions/lesson11/task1.py | 47 ++- 3 files changed, 523 insertions(+), 27 deletions(-) diff --git a/.coverage b/.coverage index ac72a85bb09e992bdf7e7960330a053097a67797..48183f367a68d8ff7191d481590d4fa14dffc037 100644 GIT binary patch delta 45 zcmV+|0Mh?}paX!Q1F)lx3*GkzB?JKp5GC>tldg^#4$kiO{chX#_I~?q+xrc()s8Dc D(n}Vc delta 45 zcmV+|0Mh?}paX!Q1F)lx3;jO_B?JKp5GDEtldg^#4uPB5=XbVkZ|}F?w!Po8)s8Dc DsOS|F diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index 765066a4..c0637997 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -880,7 +880,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 13, "id": "134eb0ef", "metadata": {}, "outputs": [ @@ -967,6 +967,507 @@ " # ваш код\n", "print(Vector2D())" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50ebd9a2", + "metadata": {}, + "outputs": [ + { + "ename": "RuntimeError", + "evalue": "'widget is not a recognised GUI loop or backend name", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\matplotlib\\backends\\registry.py:407\u001b[0m, in \u001b[0;36mBackendRegistry.resolve_gui_or_backend\u001b[1;34m(self, gui_or_backend)\u001b[0m\n\u001b[0;32m 406\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 407\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresolve_backend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgui_or_backend\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 408\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m: \u001b[38;5;66;03m# KeyError ?\u001b[39;00m\n", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\matplotlib\\backends\\registry.py:369\u001b[0m, in \u001b[0;36mBackendRegistry.resolve_backend\u001b[1;34m(self, backend)\u001b[0m\n\u001b[0;32m 368\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gui \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m--> 369\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mbackend\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m is not a recognised backend name\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m 371\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m backend, gui \u001b[38;5;28;01mif\u001b[39;00m gui \u001b[38;5;241m!=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mheadless\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n", + "\u001b[1;31mRuntimeError\u001b[0m: 'widget' is not a recognised backend name", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[1;31mRuntimeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[21], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mget_ipython\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_line_magic\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mmatplotlib\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mwidget\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python313\\site-packages\\IPython\\core\\interactiveshell.py:2482\u001b[0m, in \u001b[0;36mInteractiveShell.run_line_magic\u001b[1;34m(self, magic_name, line, _stack_depth)\u001b[0m\n\u001b[0;32m 2480\u001b[0m kwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mlocal_ns\u001b[39m\u001b[38;5;124m'\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mget_local_scope(stack_depth)\n\u001b[0;32m 2481\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mbuiltin_trap:\n\u001b[1;32m-> 2482\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[43mfn\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 2484\u001b[0m \u001b[38;5;66;03m# The code below prevents the output from being displayed\u001b[39;00m\n\u001b[0;32m 2485\u001b[0m \u001b[38;5;66;03m# when using magics with decorator @output_can_be_silenced\u001b[39;00m\n\u001b[0;32m 2486\u001b[0m \u001b[38;5;66;03m# when the last Python token in the expression is a ';'.\u001b[39;00m\n\u001b[0;32m 2487\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mgetattr\u001b[39m(fn, magic\u001b[38;5;241m.\u001b[39mMAGIC_OUTPUT_CAN_BE_SILENCED, \u001b[38;5;28;01mFalse\u001b[39;00m):\n", + "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python313\\site-packages\\IPython\\core\\magics\\pylab.py:103\u001b[0m, in \u001b[0;36mPylabMagics.matplotlib\u001b[1;34m(self, line)\u001b[0m\n\u001b[0;32m 98\u001b[0m \u001b[38;5;28mprint\u001b[39m(\n\u001b[0;32m 99\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAvailable matplotlib backends: \u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 100\u001b[0m \u001b[38;5;241m%\u001b[39m _list_matplotlib_backends_and_gui_loops()\n\u001b[0;32m 101\u001b[0m )\n\u001b[0;32m 102\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 103\u001b[0m gui, backend \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mshell\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43menable_matplotlib\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mgui\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 104\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_show_matplotlib_backend(args\u001b[38;5;241m.\u001b[39mgui, backend)\n", + "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python313\\site-packages\\IPython\\core\\interactiveshell.py:3667\u001b[0m, in \u001b[0;36mInteractiveShell.enable_matplotlib\u001b[1;34m(self, gui)\u001b[0m\n\u001b[0;32m 3664\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mmatplotlib_inline\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mbackend_inline\u001b[39;00m\n\u001b[0;32m 3666\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mIPython\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mcore\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m pylabtools \u001b[38;5;28;01mas\u001b[39;00m pt\n\u001b[1;32m-> 3667\u001b[0m gui, backend \u001b[38;5;241m=\u001b[39m \u001b[43mpt\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfind_gui_and_backend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgui\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mpylab_gui_select\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 3669\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gui \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 3670\u001b[0m \u001b[38;5;66;03m# If we have our first gui selection, store it\u001b[39;00m\n\u001b[0;32m 3671\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mpylab_gui_select \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "File \u001b[1;32m~\\AppData\\Roaming\\Python\\Python313\\site-packages\\IPython\\core\\pylabtools.py:349\u001b[0m, in \u001b[0;36mfind_gui_and_backend\u001b[1;34m(gui, gui_select)\u001b[0m\n\u001b[0;32m 347\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 348\u001b[0m gui \u001b[38;5;241m=\u001b[39m _convert_gui_to_matplotlib(gui)\n\u001b[1;32m--> 349\u001b[0m backend, gui \u001b[38;5;241m=\u001b[39m \u001b[43mbackend_registry\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresolve_gui_or_backend\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgui\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 351\u001b[0m gui \u001b[38;5;241m=\u001b[39m _convert_gui_from_matplotlib(gui)\n\u001b[0;32m 352\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m gui, backend\n", + "File \u001b[1;32mc:\\Users\\Shanechka\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\matplotlib\\backends\\registry.py:409\u001b[0m, in \u001b[0;36mBackendRegistry.resolve_gui_or_backend\u001b[1;34m(self, gui_or_backend)\u001b[0m\n\u001b[0;32m 407\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mresolve_backend(gui_or_backend)\n\u001b[0;32m 408\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m: \u001b[38;5;66;03m# KeyError ?\u001b[39;00m\n\u001b[1;32m--> 409\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[0;32m 410\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mgui_or_backend\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m is not a recognised GUI loop or backend name\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "\u001b[1;31mRuntimeError\u001b[0m: 'widget is not a recognised GUI loop or backend name" + ] + } + ], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "37da7d5e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Y8uvjxkzJh1yyCHltQnZVVddlUaMGJFuueWWNHr06HTfffell156Kd11112VHioAUM1oyFMoP/jggzR58uTyYsY8dXLWrFlNFzsuX768nFHRaPjw4enee+9NP//5z9PPfvazdOSRR6aHH344HXPMMZUeKgBQzfdp2Nu8TwMAtML3aQAA2g7RAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQPWj4aOPPkoXXnhhqqurS127dk2XXHJJ2rBhw073OeWUU1JNTU2z5fLLL6/kMAGAgA6pgnIwrFy5Ms2ePTv973//S2PHjk3jxo1L99577073u+yyy9L111/fdLtz586VHCYAUM1oeP3119OsWbPSiy++mIYMGVKuu+OOO9IZZ5yRbr755tSrV68d7psjob6+vlJDAwBa0ssTc+fOLV+SaAyGbOTIkaldu3Zp3rx5O933nnvuSd26dUvHHHNMmjRpUvr44493uO2mTZvSunXrmi0AQCs609DQ0JAOPvjg5t+sQ4d00EEHlV/bkQsuuCAddthh5ZmIV155JV177bVpyZIlaebMmdvdfurUqem6667b4+MHAP4/o2HixInpN7/5zS5fmviy8jUPjY499tjUs2fPdNppp6W33norHXHEEV/YPp+JmDBhQtPtfKahd+/eX/r7AwB7KBquvvrqdPHFF+90m759+5bXJKxevbrZ+s8++6ycUbE71ysMHTq0/Lh06dLtRkNtbW25AAAtLBq6d+9eLrsybNiwtGbNmrRgwYI0ePDgct1TTz2Vtm7d2hQCEYsWLSo/5jMOAEAbvBDy6KOPTqeffno5fXL+/Pnp+eefT1deeWU677zzmmZOvPfee6l///7l17P8EsQvf/nLMjT+/e9/p0cffTSNGTMmnXzyyem4446r1FABgGq/uVOeBZGjIF+TkKdannTSSemuu+5q+np+74Z8kWPj7IiOHTumf/zjH+nb3/52uV9+KeS73/1u+tvf/lbJYQIAATVFURSpDckXQnbp0iWtXbu2fCdKAGDP/N70b08AACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwBQ3Wi44YYb0vDhw1Pnzp1T165dQ/sURZEmT56cevbsmfbff/80cuTI9Oabb1ZqiABAS4iGzZs3p3POOSddccUV4X1uvPHGdPvtt6fp06enefPmpQMOOCCNGjUqffrpp5UaJgAQVFPkv95X0IwZM9L48ePTmjVrdrpdHkavXr3S1VdfnX7yk5+U69auXZt69OhR/j/OO++80Pdbt25d6tKlS7lvXV3dHrkPANBW7c7vzRZzTcOyZctSQ0ND+ZJEo3wnhg4dmubOnbvD/TZt2lTe4W0XAGDPazHRkIMhy2cWtpVvN35te6ZOnVrGRePSu3fvio8VAPZFuxUNEydOTDU1NTtd3njjjbQ3TZo0qTyl0risWLFir35/ANhXdNidjfP1BhdffPFOt+nbt++XGkh9fX35cdWqVeXsiUb59sCBA3e4X21tbbkAAC0oGrp3714uldCnT58yHObMmdMUCfn6hDyLYndmYAAAreyahuXLl6dFixaVH7ds2VJ+npcNGzY0bdO/f//00EMPlZ/nlzbyLItf/epX6dFHH02LFy9OY8aMKWdUnH322ZUaJgBQiTMNuyO/SdOf/vSnptuDBg0qPz799NPplFNOKT9fsmRJeR1Co2uuuSZt3LgxjRs3rpyiedJJJ6VZs2alTp06VWqYAEBLeZ+Gvc37NABAG3+fBgCgZRMNAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACGiAQAIEQ0AQIhoAABCRAMAECIaAIAQ0QAAhIgGACBENAAAIaIBAAgRDQBAiGgAAEJEAwAQIhoAgBDRAACEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAIR0SG1MURTlx3Xr1lV7KADQ4jX+vmz8/blPRcP69evLj7179672UACgVf3+7NKly063qSkiadGKbN26Nb3//vvpwAMPTDU1Naml1VyOmRUrVqS6urpqD2ef4/hXl+NffR6D6lrXQo9/zoAcDL169Urt2rXbt8405Dt86KGHppYsP1la0hNmX+P4V5fjX30eg+qqa4HHf1dnGBq5EBIACBENAECIaNiLamtr05QpU8qP7H2Of3U5/tXnMaiu2jZw/NvchZAAQGU40wAAhIgGACBENAAAIaIBAAgRDQBAiGiosBtuuCENHz48de7cOXXt2jW0T57QMnny5NSzZ8+0//77p5EjR6Y333yz4mNtiz766KN04YUXlu++lo//JZdckjZs2LDTfU455ZTyLci3XS6//PK9NubWbNq0aenwww9PnTp1SkOHDk3z58/f6fYPPvhg6t+/f7n9sccem5544om9Nta2ancegxkzZnzhuZ73Y/c9++yz6cwzzyzfijkfx4cffniX+zzzzDPp+OOPL6dg9uvXr3w8WjrRUGGbN29O55xzTrriiivC+9x4443p9ttvT9OnT0/z5s1LBxxwQBo1alT69NNPKzrWtigHw2uvvZZmz56dHnvssfIP9rhx43a532WXXZZWrlzZtOTHhJ27//7704QJE8p56AsXLkwDBgwon7erV6/e7vYvvPBCOv/888uQe/nll9PZZ59dLq+++upeH/u++hhkOai3fa6/8847e3XMbcXGjRvL452jLWLZsmVp9OjR6dRTT02LFi1K48ePT5deeml68sknU4uW36eByvvjH/9YdOnSZZfbbd26taivry9uuummpnVr1qwpamtri7/+9a8VHmXb8q9//Su/B0nx4osvNq37+9//XtTU1BTvvffeDvcbMWJEcdVVV+2lUbYdJ5xwQvHDH/6w6faWLVuKXr16FVOnTt3u9t/73veK0aNHN1s3dOjQ4gc/+EHFx9pW7e5jEP25xO5JKRUPPfTQTre55pprim9+85vN1p177rnFqFGjipbMmYYWJtdnQ0ND+ZLEtv+QSD7NOHfu3KqOrbXJxyu/JDFkyJCmdfm45n/ULJ/B2Zl77rkndevWLR1zzDFp0qRJ6eOPP94LI27dZ9QWLFjQ7Hmbj3O+vaPnbV6/7fZZ/lux5/neewyy/HLdYYcdVv7ri2eddVZ5Zo7Km9tKn/9t7l+5bO1yMGQ9evRotj7fbvwaMfl4HXzwwc3WdejQIR100EE7PZYXXHBB+UM0vzb5yiuvpGuvvTYtWbIkzZw5cy+MunX68MMP05YtW7b7vH3jjTe2u09+DDzPq/sYHHXUUenuu+9Oxx13XFq7dm26+eaby2uwcji09H8tuLVr2MHzP//z2Z988kl5PVtL5EzDlzBx4sQvXDz0+WVHf0hp+cc/X/OQiz9fmJevifjzn/+cHnroofTWW2/t0fsB1TZs2LA0ZsyYNHDgwDRixIgyjLt3757uvPPOag+NFsqZhi/h6quvThdffPFOt+nbt++X+n/X19eXH1etWlXOnmiUb+c/2MSPfz6Wn78A7LPPPitnVDQe54j80lC2dOnSdMQRR3zJUbdt+aWc9u3bl8/TbeXbOzrWef3ubM+efww+b7/99kuDBg0qn+tUVv0Onv/5wtSWepYhEw1fQi7xvFRCnz59yifTnDlzmiIhn67Kr8HvzgyMtix6/PPfotasWVO+zjt48OBy3VNPPZW2bt3aFAIR+crmbNuIo7mOHTuWxzg/b/MMiCwf53z7yiuv3OHjk7+erxpvlGe55PXsncfg8/LLG4sXL05nnHFGhUfLsGHDvjDFuFU8/6t9JWZb98477xQvv/xycd111xVf+cpXys/zsn79+qZtjjrqqGLmzJlNt3/9618XXbt2LR555JHilVdeKc4666yiT58+xSeffFKle9F6nX766cWgQYOKefPmFc8991xx5JFHFueff37T1999993y+OevZ0uXLi2uv/764qWXXiqWLVtWPgZ9+/YtTj755Crei9bhvvvuK2f5zJgxo5y5Mm7cuPJ53NDQUH79+9//fjFx4sSm7Z9//vmiQ4cOxc0331y8/vrrxZQpU4r99tuvWLx4cRXvxb71GOSfS08++WTx1ltvFQsWLCjOO++8olOnTsVrr71WxXvROq1fv77p53v+1XrrrbeWn+ffAVk+7vn4N3r77beLzp07Fz/96U/L5/+0adOK9u3bF7NmzSpaMtFQYRdddFH5BPr88vTTTzdtk2/nqU/bTrv8xS9+UfTo0aP8AXDaaacVS5YsqdI9aN3+85//lJGQg62urq4YO3Zss2DLYbDt47F8+fIyEA466KDy2Pfr16/8Q7127doq3ovW44477ii+9rWvFR07diyn//3zn/9sNpU1/3nY1gMPPFB8/etfL7fP088ef/zxKox6330Mxo8f37Rt/nlzxhlnFAsXLqzSyFu3p59+ers/6xuPd/6Yj//n9xk4cGB5/PNfTrb9PdBS1eT/VPtsBwDQ8pk9AQCEiAYAIEQ0AAAhogEACBENAECIaAAAQkQDABAiGgCAENEAAISIBgAgRDQAACni/wG0ETR2GfNTBgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from matplotlib.animation import FuncAnimation\n", + "\n", + "L = 10.0\n", + "g = 9.81\n", + "Omega = 7.292115e-5\n", + "phi = np.deg2rad(55)\n", + "\n", + "omega0 = np.sqrt(g / L)\n", + "omega_p = Omega * np.sin(phi)\n", + "\n", + "dt = 0.05\n", + "t = np.arange(0, 500, dt)\n", + "\n", + "A = 1.0\n", + "x = A * np.cos(omega0 * t) * np.cos(omega_p * t)\n", + "y = A * np.cos(omega0 * t) * np.sin(omega_p * t)\n", + "\n", + "fig, ax = plt.subplots(figsize=(6, 6))\n", + "ax.set_aspect('equal')\n", + "ax.set_xlim(-1.2*A, 1.2*A)\n", + "ax.set_ylim(-1.2*A, 1.2*A)\n", + "\n", + "line, = ax.plot([], [], lw=2)\n", + "point, = ax.plot([], [], 'ro')\n", + "\n", + "trail_x, trail_y = [], []\n", + "\n", + "def update(frame):\n", + " trail_x.append(x[frame])\n", + " trail_y.append(y[frame])\n", + " line.set_data(trail_x, trail_y)\n", + " point.set_data([x[frame]], [y[frame]])\n", + " return line, point\n", + "\n", + "ani = FuncAnimation(fig, update, frames=len(t), interval=30)\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "b5f95296", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

ТЕСТ

" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from IPython.display import HTML\n", + "HTML(\"

ТЕСТ

\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ea929ebd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Vector2D(abscissa=2, ordinate=1)\n" + ] + }, + { + "ename": "TypeError", + "evalue": "__str__ returned non-string (type NoneType)", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[1], line 118\u001b[0m\n\u001b[0;32m 115\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21mconj\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m 116\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m Vector2D(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mabscissa, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mordinate \u001b[38;5;241m*\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\n\u001b[1;32m--> 118\u001b[0m \u001b[38;5;28;43mprint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mVector2D\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[1;31mTypeError\u001b[0m: __str__ returned non-string (type NoneType)" + ] + } + ], + "source": [ + "from dataclasses import dataclass\n", + "from math import acos, isclose\n", + "\n", + "\n", + "@dataclass(eq=False, init = False)\n", + "class Vector2D:\n", + " _abscissa: float\n", + " _ordinate: float \n", + "\n", + " def __init__(self, abscissa: float = 0., ordinate: float= 0.):\n", + " self._abscissa = abscissa\n", + " self._ordinate = ordinate\n", + " \n", + " \n", + " def __repr__(self):\n", + " return (f\"Vector2D(abscissa={self._abscissa}, ordinate={self._ordinate})\")\n", + "\n", + " @property\n", + " def abscissa(self):\n", + " return self._abscissa\n", + "\n", + " @property\n", + " def ordinate(self):\n", + " return self._ordinate\n", + "\n", + " def __ne__(self, other: \"Vector2D\"):\n", + " return not (self == other)\n", + "\n", + " def __eq__(self, other):\n", + " if not isinstance(other, Vector2D):\n", + " return NotImplemented\n", + " return isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) and isclose(\n", + " self.ordinate, other.ordinate, rel_tol=1e-9, abs_tol=1e-10)\n", + "\n", + " def __lt__(self, other):\n", + " if not isinstance(other, Vector2D):\n", + " return NotImplemented\n", + " return (\n", + " self.abscissa < other.abscissa\n", + " and not (isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10))\n", + " ) or (\n", + " isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10)\n", + " and self.ordinate < other.ordinate\n", + " and not (isclose(self.ordinate, other.ordinate)))\n", + "\n", + " def __le__(self, other):\n", + " return self == other or self < other\n", + "\n", + " def __gt__(self, other):\n", + " return not (self <= other)\n", + "\n", + " def __ge__(self, other):\n", + " return not (self < other)\n", + "\n", + " def __abs__(self):\n", + " return (self.abscissa**2 + self.ordinate**2) ** 0.5\n", + "\n", + " def __bool__(self):\n", + " return not (isclose(abs(self), 0, abs_tol=1e-12))\n", + "\n", + " def __mul__(self, other):\n", + " if isinstance(other, (int, float)):\n", + " return Vector2D(self.abscissa * other, self.ordinate * other)\n", + " return NotImplemented\n", + "\n", + " def __rmul__(self, other):\n", + " return self * other\n", + "\n", + " def __truediv__(self, other):\n", + " if isinstance(other, (int, float)):\n", + " return Vector2D(self.abscissa / other, self.ordinate / other)\n", + " return NotImplemented\n", + "\n", + " def __add__(self, other):\n", + " if isinstance(other, Vector2D):\n", + " return Vector2D(self.abscissa + other.abscissa, self.ordinate + other.ordinate)\n", + " if isinstance(other, (int, float)):\n", + " return Vector2D(self.abscissa + other, self.ordinate + other)\n", + " return NotImplemented\n", + "\n", + " def __radd__(self, other):\n", + " return self + other\n", + "\n", + " def __sub__(self, other):\n", + " if isinstance(other, Vector2D):\n", + " return Vector2D(self.abscissa - other.abscissa, self.ordinate - other.ordinate)\n", + " if isinstance(other, (int, float)):\n", + " return Vector2D(self.abscissa - other, self.ordinate - other)\n", + " return NotImplemented\n", + "\n", + " def __neg__(self):\n", + " return self * -1\n", + "\n", + " def __float__(self):\n", + " return abs(self)\n", + "\n", + " def __int__(self):\n", + " return int(abs(self))\n", + "\n", + " def __complex__(self):\n", + " return complex(self.abscissa, self.ordinate)\n", + "\n", + " def __matmul__(self, other: \"Vector2D\"):\n", + " if not isinstance(other, Vector2D):\n", + " return NotImplemented\n", + " return self.abscissa * other.abscissa + self.ordinate * other.ordinate\n", + "\n", + " def get_angle(self, other: \"Vector2D\") -> float:\n", + " if not isinstance(other, Vector2D):\n", + " raise TypeError(\"Other must be Vector2D\")\n", + " if isclose(abs(other), 0) or isclose(abs(self), 0):\n", + " raise ValueError(\"You cannot get angle in between given vector and a zero vector\")\n", + " return acos((self @ other) / (abs(self) * abs(other)))\n", + "\n", + " def conj(self):\n", + " return Vector2D(self.abscissa, self.ordinate * -1)\n", + "\n", + "print(Vector2D(2,1))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "393c431a", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "unsupported operand type(s) for /: 'str' and 'int'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[2], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mabcabcabc\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m/\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\n", + "\u001b[1;31mTypeError\u001b[0m: unsupported operand type(s) for /: 'str' and 'int'" + ] + } + ], + "source": [ + "\"abcabcabc\" / 3" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "e10ab248", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'abc'" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "class Strdel(str):\n", + "\n", + " def __truediv__(self, other):\n", + " if not (isinstance(other, int)):\n", + " return NotImplemented\n", + " \n", + " if other <=0:\n", + " raise ValueError\n", + " \n", + " if not ((len(self)/ other).is_integer) or (len(self) // other) == 0:\n", + " return NotImplemented\n", + " \n", + " part = self[:len(self) // other]\n", + " if part * other == self:\n", + " return Strdel(part)\n", + " \n", + " return NotImplemented\n", + "stroka = Strdel(\"abcabc\")\n", + "stroka / 2" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "6f5aede6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'abcabcabcabc'" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "stroka * 2" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "044780de", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 1\n", + "1 2\n", + "2 3\n", + "3 4\n" + ] + } + ], + "source": [ + "def ar(arrr):\n", + " arrr.append(4)\n", + "arr = [1,2,3]\n", + "ar(arr)\n", + "for i, j in enumerate(arr):\n", + " print(i,j)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f4e7c72e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "10\n" + ] + } + ], + "source": [ + "x = 10\n", + "\n", + "def func():\n", + " global x\n", + "\n", + " print(x) # Казалось бы, должна взяться глобальная x\n", + " x = 5 # НО: наличие этой строки делает x локальной во всей функции\n", + "func()\n", + "# Ошибка: UnboundLocalError: local variable 'x' referenced before assignment" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3e7f0e78", + "metadata": {}, + "outputs": [], + "source": [ + "enumerate" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "e5ee788d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4: Алексей\n", + "5: Мария\n", + "6: Иван\n" + ] + } + ], + "source": [ + "class MyEnumerate:\n", + " def __init__(self, iterable, start=0):\n", + " # Сохраняем итератор из переданного объекта\n", + " self.iterator = iter(iterable)\n", + " self.count = start\n", + "\n", + " def __iter__(self):\n", + " # Итератор должен возвращать самого себя\n", + " return self\n", + "\n", + " def __next__(self):\n", + " # 1. Получаем следующее значение из вложенного итератора\n", + " # Если элементы закончатся, iter() сам выбросит StopIteration,\n", + " # и наш генератор тоже остановится.\n", + " value = next(self.iterator)\n", + " \n", + " # 2. Формируем текущий результат\n", + " result = (self.count, value)\n", + " \n", + " # 3. Увеличиваем счетчик для следующего шага\n", + " self.count += 1\n", + " \n", + " return result\n", + "\n", + "# Проверка:\n", + "names = [\"Алексей\", \"Мария\", \"Иван\"]\n", + "for idx, name in MyEnumerate(names, start=4):\n", + " print(f\"{idx}: {name}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7d8d1a8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "def giga_any(iterable):\n", + " iterable = iter(iterable)\n", + " count = 0\n", + " for item in iterable:\n", + " count+=1\n", + " if item:\n", + " return item\n", + " if count == 0:\n", + " return False\n", + " return item\n", + "print(giga_any([0,1]))\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "8e5dbb01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "range(6)[1]" + ] } ], "metadata": { diff --git a/solutions/lesson11/task1.py b/solutions/lesson11/task1.py index ee196f66..bdbb9a9f 100644 --- a/solutions/lesson11/task1.py +++ b/solutions/lesson11/task1.py @@ -1,28 +1,25 @@ -from dataclasses import dataclass from math import acos, isclose - -@dataclass(eq=False, frozen=True) class Vector2D: - abscissa: float = 0.0 - ordinate: float = 0.0 - - # def __init__(self, abscissa: float, ordinate: float): - # self.abscissa = abscissa - # self.ordinate = ordinate - # def __repr__(self): - # return f"Vector2D(abscissa={self.abscissa}, ordinate={self.ordinate})" - def __post_init__(self): - object.__setattr__(self, "abscissa", float(self.abscissa)) - object.__setattr__(self, "ordinate", float(self.ordinate)) + _abscissa: float + _ordinate: float + + + def __init__(self, abscissa: float = 0., ordinate: float= 0.): + self._abscissa = abscissa + self._ordinate = ordinate + + + def __repr__(self): + return (f"Vector2D(abscissa={self._abscissa}, ordinate={self._ordinate})") @property - def get_x(self): - return self.abscissa + def abscissa(self): + return self._abscissa @property - def get_y(self): - return self.ordinate + def ordinate(self): + return self._ordinate def __ne__(self, other: "Vector2D"): return not (self == other) @@ -30,21 +27,19 @@ def __ne__(self, other: "Vector2D"): def __eq__(self, other): if not isinstance(other, Vector2D): return NotImplemented - return isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) and isclose( - self.ordinate, other.ordinate, rel_tol=1e-9, abs_tol=1e-10 - ) + return isclose(self.abscissa, other.abscissa) and isclose( + self.ordinate, other.ordinate) def __lt__(self, other): if not isinstance(other, Vector2D): return NotImplemented return ( self.abscissa < other.abscissa - and not (isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10)) + and not (isclose(self.abscissa, other.abscissa)) ) or ( - isclose(self.abscissa, other.abscissa, rel_tol=1e-9, abs_tol=1e-10) + isclose(self.abscissa, other.abscissa) and self.ordinate < other.ordinate - and not (isclose(self.ordinate, other.ordinate)) - ) + and not (isclose(self.ordinate, other.ordinate))) def __le__(self, other): return self == other or self < other @@ -110,7 +105,7 @@ def __matmul__(self, other: "Vector2D"): def get_angle(self, other: "Vector2D") -> float: if not isinstance(other, Vector2D): - raise TypeError("other must be Vector2D") + raise TypeError("Other must be Vector2D") if isclose(abs(other), 0) or isclose(abs(self), 0): raise ValueError("You cannot get angle in between given vector and a zero vector") return acos((self @ other) / (abs(self) * abs(other))) From 07b68a4df4c5e072d57114312b6967217ead370c Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 6 Mar 2026 04:11:12 +0300 Subject: [PATCH 20/25] Homework for 06.03.2026 --- .coverage | Bin 53248 -> 53248 bytes .../sem01/tests/test_lesson04_tasks.py | 34 ++++++++++-------- .../sem01/tests_hw/test_hw1_task1.py | 1 + solutions/sem01/lesson11/task1.py | 20 +++++------ solutions/sem02/lesson03/task1.py | 14 ++++++-- solutions/sem02/lesson03/task2.py | 18 ++++++++-- solutions/sem02/lesson03/task3.py | 13 ++++++- 7 files changed, 68 insertions(+), 32 deletions(-) diff --git a/.coverage b/.coverage index 48183f367a68d8ff7191d481590d4fa14dffc037..07c4e81787a109c3bd12194ca96f461a6174d412 100644 GIT binary patch delta 2113 zcmaKs%WD%+6voreoyj~hbMt;QkG4DANHR6*vXn+>1xvxIlt5Z)p|v&&>BfaN-72k= zaapjd7Ib3>D7x@3u$Z-*h(g`iMe75^MZD+MAv19}yTJU;ch33l`BL8%>bt^AGCGTcB#cGU|hH8te`9k^5Vy$+!S}@C%%5rsitx`F6@BUntK_!5)J~Q3|e6QrMc{`jV^!@5sWAT5ypt~K!h-YKS&TG_`(z;_`(Jw{=bd9`!;lM8Iv!3 z5rz$51h+)P2yTgr5!@05Be*3pMsQ2~Hj#4PIa}C1u35>N9-ByMObu*bkJnFWQDSC@bns z<(KkQG3AzAl|K3R{Us?SJtSM=YjK8rr&Gp}@z^-q*q$Wh7GY03ef{#Fb7Ob>bMu6y zpD~#p#!i1017Nib2Eb-%?=f7_AMh6GCO>71bb~J@J&PQ4&wLc;*CLd5{ELcsvA zLbidV^P(L&_glv^lkwA!j+je!9Zy0}LZP#W0nnM)0PozR0FNL*XNY)nr?NC;h_vOS zzv(eOq#tRk84A9m!?H;Fl(2Zx=~D^&6(?rrYo}ik*zc%dt{9(E6vsRB-n{$X%zO9D_xJZ#qCwIEZQ0n8DB?#+Yg~v8I5wy$77IzMiHWo| zafi|1V2pu;E;K3_B39!vR2YrUp z3U{V;?02T+9WL5;QX09)Im)ebDwl1RtGDk7y9YDQ^e#%@fo{?=r>0Xfr_Uv2j-MM$ zIC&@4??N7zc_;Fi%*jtw=Hw?LbMg~LzCMKZweM2ybJ6 zRAo*+739tFtofnwxSQkA)^?&$Jfaz@5DnzW3u)s&2})ua=*);J+!{F%gMjK7kYjL}I{#^@x1xV5Ft zM-Ml9UVrq>3mkIRYVB`O#^^I3WAy2lG5Q1oF1j77Xo+*(^h z#<;b*jIm25W9(9sF?Q*ZF&b16H>c5BSfx3Q^?jW=T^r61RQahkxCKk_JNybi!gp{5 zX5lmO&pZup!|U)WybNRT91Ot|@F+Y8yPzAkKoKHff}7vsOZ+;&%75UO_~-mQe~+J< z3HbN$NxiDNj9~TItN*f!i2RjhU}X%-%Q>wi0$y^3i$d=%UiyE5W;v&)MG#Ee4PYrmDf?buQUsInq`kVh z@P(o$@It~KaT#$Dh2sMISPbvRCKN3y0v@$l1VLm4v@n7&*FvZv7YpJ+(C%dc3<7pH z^JCz*6%FtT^ypk@klU+0%)%qf&M|WzMy8$9eR$}bon0`zA_{x$e^!ARcxs4yItIGk zt1%I9w!kz5nmF-b;92l#DxRperl2Z4^`$pLVq`blpaFlwA8-v8#n9&98@LP?#n{fn z`(kkKz?*PfjP4k`2uEO83~vw~gFe^|Jz{+AaF-Zh3c_H4##i}GF|v1geesNEvRXw4 zu1bZZTqY@%NQy<0LV+ZoC&}eVvRRT$h9sRPNu@}VNs>f@BpxS;#Ym!2l1PLkd@xKJ v3XueZB!K{l-%kQS!a0d$k(eflPdt}!@AZ-x28phdFnU{;rp+v#DYX9&I%gVt diff --git a/deprecated_tests/sem01/tests/test_lesson04_tasks.py b/deprecated_tests/sem01/tests/test_lesson04_tasks.py index 694276d4..c98b6f22 100644 --- a/deprecated_tests/sem01/tests/test_lesson04_tasks.py +++ b/deprecated_tests/sem01/tests/test_lesson04_tasks.py @@ -9,21 +9,25 @@ from solutions.sem01.lesson04.task5 import find_row_with_most_ones from solutions.sem01.lesson04.task6 import count_cycles -@pytest.mark.parametrize("lst, expected", [ - pytest.param([], True, id="empty_list"), - pytest.param([5], True, id="single_element"), - pytest.param([1, 3], True, id="two_elements"), - pytest.param([3, 1], True, id="two_elements_unsorted"), - pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), - pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), - pytest.param([1, 2, 4], False, id="not_ap"), - pytest.param([10, 5, 0, -5], True, id="negative_difference"), - pytest.param([1, 1, 1, 1], True, id="constant_sequence"), - pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), - pytest.param([0, 0, 1], False, id="two_same_one_different"), - pytest.param([10**5 + i*10**2 for i in range(1000)], True, id="long_list_true"), - pytest.param([10**5 + i*10**2 for i in range(999)] + [1], False, id="long_list_false"), -]) + +@pytest.mark.parametrize( + "lst, expected", + [ + pytest.param([], True, id="empty_list"), + pytest.param([5], True, id="single_element"), + pytest.param([1, 3], True, id="two_elements"), + pytest.param([3, 1], True, id="two_elements_unsorted"), + pytest.param([1, 3, 5, 7], True, id="already_sorted_ap"), + pytest.param([3, 1, 5, 7], True, id="unsorted_ap"), + pytest.param([1, 2, 4], False, id="not_ap"), + pytest.param([10, 5, 0, -5], True, id="negative_difference"), + pytest.param([1, 1, 1, 1], True, id="constant_sequence"), + pytest.param([1, 2, 3, 5], False, id="almost_ap_but_not"), + pytest.param([0, 0, 1], False, id="two_same_one_different"), + pytest.param([10**5 + i * 10**2 for i in range(1000)], True, id="long_list_true"), + pytest.param([10**5 + i * 10**2 for i in range(999)] + [1], False, id="long_list_false"), + ], +) def test_is_arithmetic_progression_parametrized(lst, expected): if len(lst) > 500: random.shuffle(lst) diff --git a/deprecated_tests/sem01/tests_hw/test_hw1_task1.py b/deprecated_tests/sem01/tests_hw/test_hw1_task1.py index e157bb19..348ec6f8 100644 --- a/deprecated_tests/sem01/tests_hw/test_hw1_task1.py +++ b/deprecated_tests/sem01/tests_hw/test_hw1_task1.py @@ -5,6 +5,7 @@ # Импортируем тестируемую функцию и константу from homeworks.hw1.aggregate_segmentation import ALLOWED_TYPES, aggregate_segmentation + class TestAggregateSegmentation: """Тесты для функции агрегации сегментации аудио данных.""" diff --git a/solutions/sem01/lesson11/task1.py b/solutions/sem01/lesson11/task1.py index bdbb9a9f..a59737a3 100644 --- a/solutions/sem01/lesson11/task1.py +++ b/solutions/sem01/lesson11/task1.py @@ -1,17 +1,16 @@ from math import acos, isclose + class Vector2D: _abscissa: float - _ordinate: float - + _ordinate: float - def __init__(self, abscissa: float = 0., ordinate: float= 0.): + def __init__(self, abscissa: float = 0.0, ordinate: float = 0.0): self._abscissa = abscissa self._ordinate = ordinate - - + def __repr__(self): - return (f"Vector2D(abscissa={self._abscissa}, ordinate={self._ordinate})") + return f"Vector2D(abscissa={self._abscissa}, ordinate={self._ordinate})" @property def abscissa(self): @@ -27,19 +26,18 @@ def __ne__(self, other: "Vector2D"): def __eq__(self, other): if not isinstance(other, Vector2D): return NotImplemented - return isclose(self.abscissa, other.abscissa) and isclose( - self.ordinate, other.ordinate) + return isclose(self.abscissa, other.abscissa) and isclose(self.ordinate, other.ordinate) def __lt__(self, other): if not isinstance(other, Vector2D): return NotImplemented return ( - self.abscissa < other.abscissa - and not (isclose(self.abscissa, other.abscissa)) + self.abscissa < other.abscissa and not (isclose(self.abscissa, other.abscissa)) ) or ( isclose(self.abscissa, other.abscissa) and self.ordinate < other.ordinate - and not (isclose(self.ordinate, other.ordinate))) + and not (isclose(self.ordinate, other.ordinate)) + ) def __le__(self, other): return self == other or self < other diff --git a/solutions/sem02/lesson03/task1.py b/solutions/sem02/lesson03/task1.py index 2c3fc0b5..0c6d3738 100644 --- a/solutions/sem02/lesson03/task1.py +++ b/solutions/sem02/lesson03/task1.py @@ -8,13 +8,21 @@ class ShapeMismatchError(Exception): def sum_arrays_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if np.size(lhs) != np.size(rhs): + raise ShapeMismatchError + return lhs + rhs -def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: ... +def compute_poly_vectorized(abscissa: np.ndarray) -> np.ndarray: + return 3 * (abscissa**2) + 2 * abscissa + 1 def get_mutual_l2_distances_vectorized( lhs: np.ndarray, rhs: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if np.size(lhs, 1) != np.size(rhs, 1): + raise ShapeMismatchError + dist = lhs[:, np.newaxis, :] - rhs[np.newaxis, :, :] + return np.sqrt(np.sum(dist**2, 2)) diff --git a/solutions/sem02/lesson03/task2.py b/solutions/sem02/lesson03/task2.py index fc823c1d..f55c10d9 100644 --- a/solutions/sem02/lesson03/task2.py +++ b/solutions/sem02/lesson03/task2.py @@ -9,11 +9,25 @@ def convert_from_sphere( distances: np.ndarray, azimuth: np.ndarray, inclination: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + if not (np.shape(distances) == np.shape(azimuth) == np.shape(inclination)): + raise ShapeMismatchError + x = distances * np.cos(azimuth) * np.sin(inclination) + y = distances * np.sin(azimuth) * np.sin(inclination) + z = distances * np.cos(inclination) + return x, y, z def convert_to_sphere( abscissa: np.ndarray, ordinates: np.ndarray, applicates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + if not (np.shape(abscissa) == np.shape(ordinates) == np.shape(applicates)): + raise ShapeMismatchError + distance = np.sqrt(abscissa**2 + ordinates**2 + applicates**2) + azimuth = np.arctan2(ordinates, abscissa) + inciclination = np.arccos( + np.divide(applicates, distance), where=distance != 0 + ) # https://ru.stackoverflow.com/questions/1162129/Оставить-нули-при-делении-на-ноль-вместо-inf-в-python-через-numpy + return distance, azimuth, inciclination diff --git a/solutions/sem02/lesson03/task3.py b/solutions/sem02/lesson03/task3.py index 477acd0c..82ac6fbd 100644 --- a/solutions/sem02/lesson03/task3.py +++ b/solutions/sem02/lesson03/task3.py @@ -3,4 +3,15 @@ def get_extremum_indices( ordinates: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: ... +) -> tuple[np.ndarray, np.ndarray]: + if (np.size(ordinates)) < 3: + raise ValueError + moved_left = ordinates[:-2] + moved_right = ordinates[2:] + check_area = ordinates[1:-1] + max_mask = (check_area > moved_left) & (check_area > moved_right) + min_mask = (check_area < moved_left) & (check_area < moved_right) + return ( + np.where(min_mask)[0] + 1, + np.where(max_mask)[0] + 1, + ) # where return tuple of arrays of indexes for each axis, sooo need[0] to work, source: google From 6d258f468e863571b815e84a71df5438af7ec543 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Tue, 10 Mar 2026 23:31:43 +0300 Subject: [PATCH 21/25] fixed solutions to not include functions that were not explained --- .coverage | Bin 53248 -> 53248 bytes solutions/sem02/lesson03/task2.py | 7 ++++--- solutions/sem02/lesson03/task3.py | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.coverage b/.coverage index 07c4e81787a109c3bd12194ca96f461a6174d412..a7505b558c2b288025940aa92dd15c3d8c3a2e0f 100644 GIT binary patch delta 29 lcmZozz}&EadBcr tuple[np.ndarray, np.ndarray]: if (np.size(ordinates)) < 3: raise ValueError + + all_numbers = np.arange(1, len(ordinates) - 1) + moved_left = ordinates[:-2] moved_right = ordinates[2:] check_area = ordinates[1:-1] max_mask = (check_area > moved_left) & (check_area > moved_right) min_mask = (check_area < moved_left) & (check_area < moved_right) - return ( - np.where(min_mask)[0] + 1, - np.where(max_mask)[0] + 1, - ) # where return tuple of arrays of indexes for each axis, sooo need[0] to work, source: google + return (all_numbers[min_mask], all_numbers[max_mask]) From 136ceb43c32696860099413dab8c9ab672c7a3a1 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 13 Mar 2026 02:58:55 +0300 Subject: [PATCH 22/25] Homework for 14.03.2026 --- .coverage | Bin 53248 -> 53248 bytes solutions/sem02/lesson04/task1.py | 32 ++++++++++++++++++--- solutions/sem02/lesson04/task2.py | 24 ++++++++++++++-- solutions/sem02/lesson04/utils/__init__.py | 0 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 solutions/sem02/lesson04/utils/__init__.py diff --git a/.coverage b/.coverage index a7505b558c2b288025940aa92dd15c3d8c3a2e0f..46d5da857434baa3b852d1c8151cc23a3b1c3c25 100644 GIT binary patch delta 596 zcmZozz}&Eac>{}sj|!6x1OF<1KfafI?R=`dCwbF(S$P)oIC9_TuH}~I+Qt>a`H6D^ zryj={jw}vV_QmXuYb%7 delta 318 zcmZozz}&Eac>{}sgB(jQ1OF<1KfX77U3?n6XLz%CIe3=wxNtw>Zs3;V+Q}8p`IU1r zry<9Aj$957_GRoYZ1>q}*(6xEv4*gGV(HyD(UXNKS#C0;v+v{uuH2iSxXk37+~C5- zW~^6GnZPpnql@okKWFaA3NDlV>@PEB9mW zMs9fyPOb>fZ=6#&jW{lF<)q0%?h%cb@IeWKd99lj4Vhzpw*kb<993YTwvh;$^Vi68UHQ*3!4Q6_VFunu(B|6 k3bB6r%Fo3DWQnmD{eS*njFTD27GXB3e8V?VzG00zZh_y7O^ diff --git a/solutions/sem02/lesson04/task1.py b/solutions/sem02/lesson04/task1.py index 1b5526c1..06a1f3cd 100644 --- a/solutions/sem02/lesson04/task1.py +++ b/solutions/sem02/lesson04/task1.py @@ -2,16 +2,40 @@ def pad_image(image: np.ndarray, pad_size: int) -> np.ndarray: - # ваш код - return image + if pad_size < 1: + raise ValueError + if np.ndim(image) == 2: + hieght, length = image.shape + new_height, new_length = hieght + pad_size * 2, length + pad_size * 2 + padded_image = np.zeros((new_height, new_length), dtype=image.dtype) + padded_image[pad_size : pad_size + hieght, pad_size : pad_size + length] = image + else: + hieght, length, depth = image.shape + new_height, new_length = hieght + pad_size * 2, length + pad_size * 2 + padded_image = np.zeros((new_height, new_length, depth), dtype=image.dtype) + padded_image[pad_size : pad_size + hieght, pad_size : pad_size + length, ...] = image + return padded_image def blur_image( image: np.ndarray, kernel_size: int, ) -> np.ndarray: - # ваш код - return image + if kernel_size < 1 or kernel_size % 2 == 0: + raise ValueError + if kernel_size == 1: + return image + pad = kernel_size // 2 + padded_image = pad_image(image, pad) + shifted = [ + padded_image[i : i + image.shape[0], j : j + image.shape[1], ...] + for i in range(kernel_size) + for j in range(kernel_size) + ] + # сверху сам массив а в глубину уходят элементы вокруг и потом мы просто ищем среднее вглубь + blurred = np.mean(shifted, axis=0) + + return blurred.astype(image.dtype) if __name__ == "__main__": diff --git a/solutions/sem02/lesson04/task2.py b/solutions/sem02/lesson04/task2.py index be9a2288..6e5599dd 100644 --- a/solutions/sem02/lesson04/task2.py +++ b/solutions/sem02/lesson04/task2.py @@ -5,6 +5,26 @@ def get_dominant_color_info( image: np.ndarray[np.uint8], threshold: int = 5, ) -> tuple[np.uint8, float]: - # ваш код + if threshold < 1: + raise ValueError("threshold must be positive") - return 0, 0 + pixels = image.flatten() + counts = np.zeros(256, dtype=np.int64) + for color in range(256): + mask = pixels == color + counts[color] = np.sum(mask) + + max_pixels = 0 + best_color = 0 + for current_color in range(256): + if counts[current_color] != 0: + low = max(0, current_color - (threshold - 1)) + high = min(255, current_color + (threshold - 1)) + current_sum = np.sum(counts[low : high + 1]) + + if current_sum > max_pixels: + max_pixels = current_sum + best_color = current_color + + percentage = (max_pixels / pixels.size) * 100 + return np.uint8(best_color), float(percentage) diff --git a/solutions/sem02/lesson04/utils/__init__.py b/solutions/sem02/lesson04/utils/__init__.py new file mode 100644 index 00000000..e69de29b From d6979207793a70ed4ffb54f16090b5c5b442e859 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 20 Mar 2026 00:27:35 +0300 Subject: [PATCH 23/25] Homework for 21.03.2026 --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 52 ++++++++++++++++++ .../sem02/tests/task4/test_lesson04_tasks.py | 8 +-- solutions/sem02/lesson05/task1.py | 7 ++- solutions/sem02/lesson05/task2.py | 16 +++++- solutions/sem02/lesson05/task3.py | 10 +++- tests/test_lesson05_tasks.py | 8 +-- 7 files changed, 86 insertions(+), 15 deletions(-) diff --git a/.coverage b/.coverage index 46d5da857434baa3b852d1c8151cc23a3b1c3c25..02659de6be996080141ea47f9b53ae255af3bdd2 100644 GIT binary patch delta 502 zcmZozz}&Eac>{|>kUAq91OF<1KfX77U3?n6XLz%CIe3=wxNtw>Zs3;V+Q}8p`IU1r zry<9Aj$957_GRoYZ1>q}*(6xEv4*gGV(DekWp5kys*w60(03v^y@c;k- delta 389 zcmZozz}&Eac>{}sj|!6x1OF<1KfafI?R=`dCwbF(S$P)oIC9_TuH}~I+Qt>a`H6D^ zryj={jw}vV_QmXuY%8^BxDE_ zN@kw?&2Vs67cfuXD**ku>0?$PT{-69G z`Cszi;lH?9P+&j50w*gABc}-Kr{DkbxLJTq36@XqKmQMAVq*q!_?Q_tf9&UX004eF BdENj3 diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index c0637997..da0f6280 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -1468,6 +1468,58 @@ "source": [ "range(6)[1]" ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "487e59ab", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "5\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[[0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.]],\n", + "\n", + " [[0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.]],\n", + "\n", + " [[0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.],\n", + " [0., 0., 0., 0., 0.]]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Создаем матрицу 3x5 (3 строки, 5 столбцов)\n", + "matrix = np.zeros((3, 5,5))\n", + "\n", + "print(matrix.shape[0]) # Выведет: 3 (строки)\n", + "print(matrix.shape[1]) \n", + "matrix # Выведет: 5 (столбцы)" + ] } ], "metadata": { diff --git a/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py b/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py index 37d249b7..81312dea 100644 --- a/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py +++ b/deprecated_tests/sem02/tests/task4/test_lesson04_tasks.py @@ -333,12 +333,8 @@ class TestTask2: ), ], ) - def test_get_dominant_color_info( - self, image, threshold, expected_color, expected_ratio - ): - color, ratio_percent = get_dominant_color_info( - image.astype(np.uint8), threshold - ) + def test_get_dominant_color_info(self, image, threshold, expected_color, expected_ratio): + color, ratio_percent = get_dominant_color_info(image.astype(np.uint8), threshold) assert color in expected_color assert (abs(ratio_percent - expected_ratio * 100) < 1e-6) or ( diff --git a/solutions/sem02/lesson05/task1.py b/solutions/sem02/lesson05/task1.py index e9c7c3c5..413da76b 100644 --- a/solutions/sem02/lesson05/task1.py +++ b/solutions/sem02/lesson05/task1.py @@ -9,4 +9,9 @@ def can_satisfy_demand( costs: np.ndarray, resource_amounts: np.ndarray, demand_expected: np.ndarray, -) -> bool: ... +) -> bool: + if resource_amounts.shape[0] != costs.shape[0] or demand_expected.shape[0] != costs.shape[1]: + raise ShapeMismatchError + total = costs @ demand_expected + check = total <= resource_amounts + return all(check) diff --git a/solutions/sem02/lesson05/task2.py b/solutions/sem02/lesson05/task2.py index be1fb9d2..cedbded6 100644 --- a/solutions/sem02/lesson05/task2.py +++ b/solutions/sem02/lesson05/task2.py @@ -8,4 +8,18 @@ class ShapeMismatchError(Exception): def get_projections_components( matrix: np.ndarray, vector: np.ndarray, -) -> tuple[np.ndarray | None, np.ndarray | None]: ... +) -> tuple[np.ndarray | None, np.ndarray | None]: + if matrix.shape[0] != matrix.shape[1] or matrix.shape[1] != vector.size: + raise ShapeMismatchError + if np.linalg.det(matrix) == 0: + return (None, None) + + scalar_mult = matrix @ vector + + length = np.sum(matrix**2, axis=1) + + keff = (scalar_mult / length)[:, np.newaxis] + + projections = keff * matrix + + return (projections, vector - projections) diff --git a/solutions/sem02/lesson05/task3.py b/solutions/sem02/lesson05/task3.py index 0c66906c..8ee68bf5 100644 --- a/solutions/sem02/lesson05/task3.py +++ b/solutions/sem02/lesson05/task3.py @@ -9,4 +9,12 @@ def adaptive_filter( Vs: np.ndarray, Vj: np.ndarray, diag_A: np.ndarray, -) -> np.ndarray: ... +) -> np.ndarray: + if Vs.shape[0] != Vj.shape[0] or Vj.shape[1] != diag_A.size: + raise ShapeMismatchError + + Vj_h = Vj.real - 1j * Vj.imag + Vj_h = np.moveaxis(Vj_h, 0, 1) + A = np.diag(diag_A) + R = Vs - Vj @ np.linalg.inv(np.eye(Vj.shape[1]) + Vj_h @ Vj @ A) @ (Vj_h @ Vs) + return R diff --git a/tests/test_lesson05_tasks.py b/tests/test_lesson05_tasks.py index aeb37ebc..4cc9188d 100644 --- a/tests/test_lesson05_tasks.py +++ b/tests/test_lesson05_tasks.py @@ -89,9 +89,7 @@ class TestTask1: ), ], ) - def test_can_satisfy_demand( - self, costs, resource_amounts, demand_expected, expected - ): + def test_can_satisfy_demand(self, costs, resource_amounts, demand_expected, expected): assert can_satisfy_demand(costs, resource_amounts, demand_expected) == expected def test_can_satisfy_demand_validate(self): @@ -172,9 +170,7 @@ class TestTask2: ), ], ) - def test_get_projections_components( - self, matrix, vector, proj_expected, orth_expected - ): + def test_get_projections_components(self, matrix, vector, proj_expected, orth_expected): projections, orthogonals = get_projections_components(matrix, vector) if proj_expected is None: From 52c37f5b43fca53e19744e3107dbbb0acc475065 Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 10 Apr 2026 04:12:00 +0300 Subject: [PATCH 24/25] Hw for 11.04.2026 --- .coverage | Bin 53248 -> 53248 bytes .github/workflows/trying.ipynb | 70 ++++++++++++++++++++++++++++++ .gitignore | 1 + result.png | Bin 0 -> 22437 bytes solutions/sem02/lesson07/task1.py | 52 +++++++++++++++++++++- solutions/sem02/lesson07/task2.py | 43 ++++++++++++++++++ 6 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 result.png diff --git a/.coverage b/.coverage index 02659de6be996080141ea47f9b53ae255af3bdd2..d7376cf5457ea18623a14b780a7ed01891ea8d40 100644 GIT binary patch delta 1022 zcmY+BOH30{6o%){<4$L$GgA!Uqed`x0SS+^;w!ok3*{lSw3RltNU4;VssWVZV_^at z77#V$EHt_)E^#5G>~RHK-Dor#HF2i`YSQd zjWgFuWuDI_tTk4m-sH^9QrYMiGp!0<<;KwUvHZxe&T*WNuj9;lGi-b{ri~hXMbGKH zyXZctz0q!J6>3S1D(lL$QsY{2W#ujTk$jxKq6KP7B`GSdi+9BuVMWOCTl@pQZgKV< zp?ROkO(wzL5`+Hca_JDN=#Fwp5QUyriR{5ey`YC=9Bgh&VdW;3tPw^+CaGYl?)O_av#wE&Sq@&bC*gM+JFKB2 zjg>~q&`-cXyFolS(?^f8-!QSv5`!6Vze6HAtH@!vb2bgnI@$OKtI~+d#?3%qCX*k| z7cv<(tzb8lP*VEyJcI#=`M*-ThS)gE7=mJ!f}}BP;iY#1jp@+#K9ZZVLp=T z^6~YY`P7WKPZ{5g8KYKT)rWM!J*UiQZ?#)mrMjfXlnvKKrPj47KX!4{BcGtJ>A18c z)=DvPL%b(&!m2RD*Ygh-XWtW$qn9P$B@sz~?~fyQhjh#$9nWchNgsr{Se8`7$HX?l zR8S{X=p~V>3O~+8Y?6#?uftVhEcQbrlO|rAO;}VDHdT@Ag~@cnrkZ3_HL{yEB2@$V zV(R{fY6V6)QjWHR^*qC>l3kc25vz`XS&Gyo_WT}49YJL}ioL#JD@Ho)khUt7Xl$eL zA88c_E=UZrf>oBdm`1E3k*$O+rIt5^54ufLC8FhYW_CzsQYEqYtMSP~HGY2pmBv%+ delta 1264 zcmY*ZOKe+36n*#2$8X+m>?BPSm87XC4G1W))4+~TW2Z?!Hgy}vO5NH|b=x?F0C7NK z0dlq=KIyOprCqcVEC`BJJ}b5$s-lVws8S-3po@MKpooe};m-3*(rjk#nRDjOojF(2 zm?w>SvOs$*Q!X3wwp?CJ#D9{$YmzQG5!B1U^vQaky8To;3T_zkhP;b{>vGB^U9iTR zHrXcLI5l0by)xw)hS3Y$ke|w|f75@*f7JWIoAfOAqMLSpbsA2${k1(N?g}N6)>Z3e z%Zgo$rTG$ni+8Zg>?r+{zDFM?-;p!KH0RA{;b(Xa_}turS?g81_fE21)R;Z2UN|_S z<}*Uw*_(hz6bx2jr&>uDRCk*5)b3Z`tUMe+=iK0!YNUO2YH-Z$FUL$aKVTKOO zFsi;9matW=j1*L3B&wx7f(~ral6c=hsa&p2)n>|NT}m6$0kq;+QlIZ<(4r&0M^gzM zVktLKkW$(GL9T%!ho917WH$x1L1AM2PP4f$rW+qC%M!4UWgg;Gw+K1jd#`)?j<+l z{N~I$+bv-i#XsUBk+OcYrucH~QY^ze`P=k;_9Yvof5{vn^a=7knSfu+^X799gK7Vk z|E~Yo+}s!H$^0biJwws+Wd4|VeWvzO9dEGf+>t68f4-Cr+W>86x~iQItk`NbUqtIY zRXtL4Hmu(fX=J#gCqk{*3-Lk~x)B$&<+=ez*Bcqs|nB0{!0Jbr@d{*%q@ zo0`H$RRA82$FU74xH@7wx1%HNKJw^3j_CgJAj}j~x$-gKs0d$I`+p+z-`Vh=KEP(m zVGEQ`FVgru=@G-T@fQGj-;nng6O}u%6Coj<(4Pa7*9>`2uE;;+b$JaxlW*no7Na0q zSZe8CK{0Bhsnw&xB6^XeRtYyT@=ahsh44WqXL8Aq|H;4QE&01#!Yo(h4vs2dESx9} QCqlx>G%4;B7b<=K1M>uc>;M1& diff --git a/.github/workflows/trying.ipynb b/.github/workflows/trying.ipynb index da0f6280..908a1b10 100644 --- a/.github/workflows/trying.ipynb +++ b/.github/workflows/trying.ipynb @@ -1520,6 +1520,76 @@ "print(matrix.shape[1]) \n", "matrix # Выведет: 5 (столбцы)" ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "241b61a0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "np.True_" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "diagram_type = np.array([\"hist\",\"violin\",\"box\"])\n", + "arr2 = arr.copy()\n", + "diagram_type.sort()\n", + "(diagram_type == [\"hist\", \"box\", \"violin\"]).any()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "22c6a436", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4beb7c3d", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc903181", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a4cdde85", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/.gitignore b/.gitignore index cececc15..bd573544 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ # Python Files *solved.ipynb +.github\workflows\trying.ipynb.github\workflows\trying.ipynb \ No newline at end of file diff --git a/result.png b/result.png new file mode 100644 index 0000000000000000000000000000000000000000..556c6f78a4111cb0dfe8418aaf89e9d9906bc74e GIT binary patch literal 22437 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 E58DmLi2wiq literal 0 HcmV?d00001 diff --git a/solutions/sem02/lesson07/task1.py b/solutions/sem02/lesson07/task1.py index 3a505d89..1d68b3d1 100644 --- a/solutions/sem02/lesson07/task1.py +++ b/solutions/sem02/lesson07/task1.py @@ -13,8 +13,56 @@ def visualize_diagrams( ordinates: np.ndarray, diagram_type: Any, ) -> None: - # ваш код - pass + if abscissa.shape[0] != ordinates.shape[0] or diagram_type not in ["hist", "box", "violin"]: + raise ShapeMismatchError + figure = plt.figure(figsize=(8, 8)) + grid = plt.GridSpec(4, 4, wspace=0.2, hspace=0.2) + scat_graph = figure.add_subplot(grid[0:-1, 1:]) + ord_vert_graph = figure.add_subplot(grid[0:-1, 0], sharey=scat_graph) + abs_hor_graph = figure.add_subplot(grid[-1, 1:], sharex=scat_graph) + scat_graph.scatter(abscissa, ordinates, color="red", alpha=0.5) + if diagram_type == "box": + ord_vert_graph.boxplot( + ordinates, + patch_artist=True, + boxprops={"facecolor": "red", "linewidth": 2}, + medianprops={"color": "black"}, + ) + abs_hor_graph.boxplot( + abscissa, + vert=False, + patch_artist=True, + boxprops={"facecolor": "red", "linewidth": 2}, + medianprops={"color": "black"}, + ) + if diagram_type == "hist": + ord_vert_graph.hist( + ordinates, orientation="horizontal", bins=50, color="red", alpha=0.5, edgecolor="black" + ) + abs_hor_graph.hist(abscissa, bins=50, color="red", alpha=0.5, edgecolor="black") + ord_vert_graph.invert_xaxis() + abs_hor_graph.invert_yaxis() + + if diagram_type == "violin": + viol_vert = ord_vert_graph.violinplot( + ordinates, + showmedians=True, + ) + for body in viol_vert["bodies"]: + body.set_facecolor("red") + body.set_edgecolor("black") + for parts in viol_vert: + if parts == "bodies": + continue + viol_vert[parts].set_edgecolor("black") + viol_hor = abs_hor_graph.violinplot(abscissa, vert=False, showmedians=True) + for body in viol_hor["bodies"]: + body.set_facecolor("red") + body.set_edgecolor("black") + for parts in viol_hor: + if parts == "bodies": + continue + viol_hor[parts].set_edgecolor("black") if __name__ == "__main__": diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index decd607e..3a067696 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -1 +1,44 @@ # ваш код (используйте функции или классы для решения данной задачи) +import json + +import matplotlib.pyplot as plt +import numpy as np + + +def get_counts(tier_list, stages_order): + arr = np.array(tier_list) + return np.array([np.sum(arr == stage) for stage in stages_order]) + + +def solution(): + plt.style.use("ggplot") + with open("solutions\sem02\lesson07\data\medic_data.json") as f: + data = json.load(f) + + stages_order = ["I", "II", "III", "IV"] + + before_count = get_counts(data["before"], stages_order) + after_count = get_counts(data["after"], stages_order) + + fig, ax = plt.subplots(figsize=(12, 7)) + x = np.arange(len(stages_order)) + width = 0.35 + + ax.bar( + x - width / 2, before_count, width, label="До установки", color="red", edgecolor="black" + ) + ax.bar( + x + width / 2, after_count, width, label="После установки", color="green", edgecolor="black" + ) + ax.set_title("Стадии митральной недостаточности") + ax.set_ylabel("Кол-во людей") + ax.set_xticks(x) + ax.set_xticklabels(stages_order) + ax.legend() + fig.tight_layout() + + plt.savefig("result.png") + plt.show() + +if __name__ == "__main__": + solution() From cc4534c4c81edbcb43012764b9b82714994ececf Mon Sep 17 00:00:00 2001 From: Shanechka Date: Fri, 10 Apr 2026 04:14:24 +0300 Subject: [PATCH 25/25] Forgor formating --- solutions/sem02/lesson07/task2.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/solutions/sem02/lesson07/task2.py b/solutions/sem02/lesson07/task2.py index 3a067696..9c29919a 100644 --- a/solutions/sem02/lesson07/task2.py +++ b/solutions/sem02/lesson07/task2.py @@ -24,9 +24,7 @@ def solution(): x = np.arange(len(stages_order)) width = 0.35 - ax.bar( - x - width / 2, before_count, width, label="До установки", color="red", edgecolor="black" - ) + ax.bar(x - width / 2, before_count, width, label="До установки", color="red", edgecolor="black") ax.bar( x + width / 2, after_count, width, label="После установки", color="green", edgecolor="black" ) @@ -40,5 +38,6 @@ def solution(): plt.savefig("result.png") plt.show() + if __name__ == "__main__": solution()