Вдъхновени от предстоящите _(поредни)_ парламентарни избори, решихме да ви помолим заедно да подготвим симулация на изборния процес.
**Бел. Ред. - Да**, знаем, че нямате търпение за картинката, която неизменно върви с домашните, обещаваме да си я получите по някое време. Но тъй като това е предизборно обещание, може да го спазим, може и да не го спазим.
Обратно към домашното!
За да _"симулираме"_ парламентарните избори ще са ни необходими няколко неща:
#### `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:58