Домашни > Предизборно ООП


Предизборно ООП
Краен срок: 24.03.2026 23:59
Точки: 7

Вдъхновени от предстоящите _(поредни)_ парламентарни избори, решихме да ви помолим заедно да подготвим симулация на изборния процес. **Бел. Ред. - Да**, знаем, че нямате търпение за картинката, която неизменно върви с домашните, обещаваме да си я получите по някое време. Но тъй като това е предизборно обещание, може да го спазим, може и да не го спазим. Обратно към домашното! За да _"симулираме"_ парламентарните избори ще са ни необходими няколко неща: #### `Currency` Напишете клас `Currency`, който да ни помогне да дефинираме различните валути, с които партиите могат да си купвуат избиратели. Искаме класът да приема два аргумента - име и "курс" на валутата, т.е. колко от дадената валута е нужна за купуване на един глас. Пример: ``` kebapcheta = Currency("кебапчета", 15) ``` ##### Сравнение на `Currency` Искаме да можем да сравняваме валути с `==` - една единица е равна на друга, ако имената **и** "курсът" на двете са еднакви! ``` kebapcheta = Currency("кебапчета", 15) pak_kebapcheta = Currency("кебапчета", 15) gadni_kebapcheta = Currency("кебапчета", 35) evra = Currency("евро", 35) print(kebapcheta == pak_kebapcheta) # True print(kebapcheta == gadni_kebapcheta) # False print(gadni_kebapcheta == evra) # False ``` #### `PoliticalParty` Напишете клас `PoliticalParty`, който да ни помогне да дефинираме понятието "партия". Искаме класът да приема следните аргументи: - Първи позиционен аргумент - име на партията. - Втори позиционен аргумент - девиз на партията. - Опционален аргумент `members`, който е *списък* от членове на партията. - Опционален аргумент `preferred_currency`, който показва предпочитаната от потенциалните избиратели на тази партия валута. Примери: ``` rebirth = PoliticalParty("Възраждане", "Аман от тия гей паради!") BSP = PoliticalParty("БСП", "Ще вдигнем пенсиите и тази година!", members=["Крум Еди-Кой-Си", "Атанас Еди-Кой-Си", "Онзи другия"]) # preferred_currency gold_bars = Currency("мини-златни кюлчета", 0.2) GERB = PoliticalParty("ГЕРБ", "Ту-тууу!", preferred_currency=gold_bars) ``` ##### Девиз Искаме девизът да е достъпен чрез атрибута `party.motto`, но само за четене. Т.е. искаме да забраним това девизът да бъде променян чрез външен за класа достъп. ``` GERB = PoliticalParty("ГЕРБ", "Ту-тууу!") print(GERB.motto) # "Ту-тууу!" GERB.motto = "Дайте пак да се пробваме?" # ТЦ! Не може! Грешка. ``` ##### `convert_currency_to_voters` Искаме методът на `PoliticalParty` - `convert_currency_to_voters` да конвертира даден брой единици от дадена валута в брой избиратели. Ако това е предпочитаната единица на въпросната партия, ефективността ѝ се удвоява и тя може да закупи двойно повече гласове. Например: ``` leva = Currency("левчета", 50) SDS = PoliticalParty("СДС", "Ние също сме шокирани, че все още съществуваме...") greatness = PoliticalParty("Величие", "Не на еврото!", preferred_currency=leva) print(SDS.convert_currency_to_voters(100, leva)) # 2 print(greatness.convert_currency_to_voters(100, leva)) # 4 ``` Ако с дадената бройка единици от валутата не могат да се купят кръгло число гласове - купуват се колкото може и ресто не се връща _(да са си правили сметката)_: ``` kebapcheta = Currency("кебапчета", 15) DPS = PoliticalParty("ДПС", "КТБ по КТБ - знаете...", preferred_currency=kebapcheta) print(DPS.convert_currency_to_voters(50, kebapcheta)) # 6, останалите 5 кебапчета са бакшиш ``` ##### Стрингова репрезентация на `PoliticalParty` Искаме да дефинираме поведение при конвертиране към `str` за нашите партии. Страшно е просто - това са просто имената на партиите: ``` SDS = PoliticalParty("СДС", "Ние също сме шокирани, че все още съществуваме...") print(str(SDS)) # "СДС" # Бонус - print функцията прави конвертирането автоматично: print(SDS) # "СДС" ``` ##### Коалиране Както добре знаем, партиите могат да се коалират по неочаквани за нас начини. В нашето задание искаме да го дефинираме с минимален интерфейс - `+`. Когато две партии бъдат "събрани", те сформират нова инстанция на обект `Coalition`. Например: ``` GERB = PoliticalParty("ГЕРБ", "Ту-тууу!") SDS = PoliticalParty("СДС", "Ние също сме шокирани, че все още съществуваме...") GERB_SDS = GERB + SDS print(type(GERB_SDS)) # <__main__.Coalition object ...> ``` А що се отнася до `Coalition`... #### `Coalition` Напишете клас `Coalition`, който репрезентира коалиция от партии (предизборна коалиция). За начало, искаме да може да се инициализира с произволен брой `PoliticalParty` обекти: ``` yes_Bulgaria = PoliticalParty("Да, България", "Ще щурмуваме всичко на север от Одрин!") # Добре де, Христо Иванов вече го няма, но Божо има много точки в Stack Overflow и него не можем да нападаме DSB = PoliticalParty("ДСБ", "Помните ли Иван Костов?") DB = Coalition(yes_Bulgaria, DSB) ``` ##### `members` Искаме коалициите да имат атрибут `members`, който да ни дава речник състоящ се от имената на партиите, участващи в коалицията, като ключове, и списъци с техните членове като стойности: ``` yes_Bulgaria = PoliticalParty("Да, България", "Ще щурмуваме всичко на север от Одрин!", members=["Божо", "Ивайло Мирчев"]) DSB = PoliticalParty("ДСБ", "Помните ли Иван Костов?", members=["Не-Иван-Костов", "Пак не е Иван Костов"]) DB = Coalition(yes_Bulgaria, DSB) print(DB.members) # {"Да, България": ["Божо", "Ивайло Мирчев"], "ДСБ": ["Не-Иван-Костов", "Пак не е Иван Костов"]} ``` - **Уговорка**: Ако дадена партия не е създадена с `members=[...]`, т.е. списъкът с членове е празен, и тя участва в коалиция, то `Coalition.members` връща **празен списък** за въпросната партия. ##### Събиране на партии / коалиции Искаме възможността да добавяме партии или сливаме коалиции по следния начин: ``` yes_Bulgaria = PoliticalParty("Да, България", "Ще щурмуваме всичко на север от Одрин!", members=["Божо", "Ивайло Мирчев"]) DSB = PoliticalParty("ДСБ", "Помните ли Иван Костов?", members=["Не-Иван-Костов", "Пак не е Иван Костов"]) DB = Coalition(yes_Bulgaria, DSB) PP = PoliticalParty("ПП", "Не сме сигурни все още...", members=["Асенката"]) PPDB = DB + PP print(PPDB.members) # {"Да, България": ["Божо", "Ивайло Мирчев"], "ДСБ": ["Не-Иван-Костов", "Пак не е Иван Костов"], "ПП": ["Асенката"]} ``` С други думи добавянето на партия към коалиция връща нова коалиция, която съдържа информацията за партиите от коалицията + новодобавената партия. За да улесним нещата, ще имплементираме само поведението `Coalition + PoliticalParty`, но не и `PoliticalParty + Coalition` _(да, има разлика)_. Същото правило важи и за събиране на две коалиции: ``` yes_Bulgaria = PoliticalParty("Да, България", "Ще щурмуваме всичко на север от Одрин!", members=["Божо", "Ивайло Мирчев"]) DSB = PoliticalParty("ДСБ", "Помните ли Иван Костов?", members=["Не-Иван-Костов", "Пак не е Иван Костов"]) DB = Coalition(yes_Bulgaria, DSB) GERB = PoliticalParty("ГЕРБ", "Ту-тууу!", members=["Бат'", "Бойко", "и", "сам", "стига"]) SDS = PoliticalParty("СДС", "Ние също сме шокирани, че все още съществуваме...", members=["Румен, ама не Радев"]) GERB_SDS = GERB + SDS OMG = DB + GERB_SDS print(OMG.members) # {"Да, България": ["Божо", "Ивайло Мирчев"], "ДСБ": ["Не-Иван-Костов", "Пак не е Иван Костов"], "ГЕРБ": ["Бат'", "Бойко", "и", "сам", "стига"], "СДС": ["Румен, ама не Радев"]} ``` Мислехме да ви тормозим и с премахване на партии от коалиция, но решихме, че много ще ескалираме домашното ако трябва да имплементирате и сбивания в народното събрание. ##### Стрингова репрезентация на `Coalition` Искаме да дефинираме поведение при конвертиране към `str` за нашите коалиции. Резултатът от това ще е конкатенация на имената на партиите в реда на добавянето им, разделени от `"-"` _(тире)_. Пример: ``` PPDB = DB + PP print(str(PPDB)) # "Да, България-ДСБ-ПП" ``` #### `Elections` И финално, искаме клас `Elections`, който да се грижи за провеждането на изборите. Инициализацията става с един позиционен аргумент, който е година на провеждане. Отвъд това, искаме следните той да има следните методи: - `register_party_or_coalition` - приема партия или коалиция и я регистрира като участваща в тазгодишните избори - `vote` - приема партия или коалиция и добавя един глас към гласовете за нея - `rig_elections` - приема партия или коалиция, количество и валута и купува колкото е възможно повече гласове, добавяйки ги към общия резултат за тази партия или коалиция. **Забележка**: Ако става въпрос за коалиция, добре е да потърсите дали случайно това не е предпочитана единица за някоя от партиите и да "купите" чрез нейният метод за конвертиране на валута в гласоподаватели, защото това е начинът да купите "колкото е възможно повече". - `get_results` - връща резултатите от изборите във формата на речник, където ключовете са имена на партии или коалици (името на коалицията е нейната стрингова репрезентация), а стойностите - гласове за дадената партия или коалиция - `get_results_by_year` - връща резултати за изборите проведени в дадена година **Уточнение** - когато се гласува за коалиция, гласовете се броят само за тази коалиция. Опростен вариант на реалното гласуване, но не искаме да броим и двете. С други думи **няма** да тестваме с добавяне на коалиция и отделно партия, която е част от същата коалиция към `Elections`. ###### Обобщен пример за изборите ``` elections_2026 = Elections(2026) BSP = PoliticalParty("БСП", "Ще вдигнем пенсиите и тази година!", members=["Крум Еди-Кой-Си", "Атанас Еди-Кой-Си", "Онзи другия"]) elections_2026.register_party_or_coalition(BSP) gold_bars = Currency("мини-златни кюлчета", 0.2) GERB = PoliticalParty("ГЕРБ", "Ту-тууу!", members=["Бат'", "Бойко", "и", "сам", "стига"], preferred_currency=gold_bars) SDS = PoliticalParty("СДС", "Ние също сме шокирани, че все още съществуваме...", members=["Румен, ама не Радев"]) GERB_SDS = GERB + SDS elections_2026.register_party_or_coalition(GERB_SDS) elections_2026.vote(BSP) elections_2026.rig_elections(GERB_SDS, 1, gold_bars) print(elections_2026.get_results()) # {"БСП": 1, "ГЕРБ-СДС": 10} # Не сме ги дефинирали тук, но все пак print(Elections.get_results_by_year(2025)) # Някакви други резултати, защото са за 2025 ``` И финална **уговорка** - няма да правим повече от едни избори в една и съща година... Така де, поне за нашите цели - няма.
Дискусия
Илиян Гаврилов
18.03.2026 18:32

Няколко неща: 1. `elections_2026.get_results() # {"БСП": 1, "ГЕРБ-СДС": 5}` не трябва ли да е `elections_2026.get_results() # {"БСП": 1, "ГЕРБ-СДС": 10}`, тъй като на `GERB` предпочитаната валута е `gold_bars`? (1/0.2) * 2 = 10? 2. За коалицията пише, че "се инициализира с произволен брой PoliticalParty обекти", значи ли, че може с <2 партии? 3. `PPDB = DB + PP print(DB.members) # {"Да, България": ["Божо", "Ивайло Мирчев"], "ДСБ": ["Не-Иван-Костов", "Пак не е Иван Костов"], "ПП": ["Асенката"]}` не трябва ли да е `PPDB.members`, защото така се мутира `DB`, а в условието пише, че трябва да се създаде нова инстанция
Нишка
Виктор Бечев
18.03.2026 18:58

1. Прав си. 2. Според условието може. Не би следвало да има значение, въпреки че концептуално няма смисъл _(although, има някои коалиции, които се появяват "магически" като коалиции, нищо, че няма очевидни партии, които са се коалирали)_. 3. И за това си прав.
Милка Кръстева
18.03.2026 14:58

kebapcheta = Currency("кебапчета", 15) DPS = PoliticalParty("ДПС", kebapcheta) В описанието вторият позиционен аргумент не е опционален, а в примера тук липсва. :) Кое да приемем да вярно ?:) И опционалните параметри keyword ли са, или позиционни ? :) И отговора не трябва ли да е 6, защото kebapcheta са предпочитана валута на ДПС :)
Нишка
Виктор Бечев
18.03.2026 15:43

Права си, коригирахме. Що се отнася до опционалните параметри - `optional == keyword == default` параметър. Optional positional и другите езотерики няма да ви караме да ползвате.
Михаил Георгиев
18.03.2026 12:12

Може ли да попитам ако гласуваме за партия, която не е регистрирана дали трябва да има допълнителна проверка за това, също така методът get_results_by_year дали е static или class метод и откъде идват данните и coalition , мисля че се пише с едно L и дали това ще гръмне на тестовете и последно как се купуват гласове за коалиция дали трябва да изберем най-добрата партия демек тази, която дава най-много гласове
Нишка
Виктор Бечев
18.03.2026 12:28

1. Няма да тестваме гласуване с нерегистрирани партии. 2. Дали методът е статичен или класов ще решите вие. Ние дефинираме само интерфейса и поведението. 3. Данните за `get_results_by_year` са на базата на предходни инстанции на класът `Elections`. Т.е. ако имаме инстанции `Elections(2024)`, `Elections(2025)` и т.н. - функцията извикана за 2024 ще ни върне информация за изборите от тази година. 4. Абсолютно си прав за `Coalition` - оправяме го. И да, иначе ще гръмне, разбира се, защото няма да намери обект / клас с даденото име. 5. Гласовете се броят **на името на** коалиция, но за да купите възможно най-ефективно гласове - трябва да проверите дали случайно валутата не е `preferred_currency` за някоя от партиите в тази коалиция.
Михаил Георгиев
18.03.2026 12:36

Благодаря за уточненията и последно може ли да ползваме hasattr
Виктор Бечев
18.03.2026 15:32

Може, няма дерт.
Виктор Бечев
17.03.2026 22:48

Примерите са много, домашното не е сложно... Но е обемно, а ако все още не сте свикнали с ООП-то в [Python](https://www.youtube.com/watch?v=a1sn0ZSfnMo) - времеемко. Така че ви съветваме, да не го оставяте за последния момент. Оставаме насреща да ни питате ако има нещо неясно или пък сме допуснали грешка. Успех!