Георги Кунчев
01.11.2024 19:08
Здравейте,
Както стана ясно вчера, породено от анкетата за обратна връзка, споделяме набор кратки задачи, с които можете да тренирате.
Задачите няма да се оценяват. Няма да ги проверяваме. Не е нужно да споделяте решение.
Ако решите да споделите и да потърсите обратна връзка, ще дадем.
Ако имате въпроси, питайте и ще кажем.
Ако сме допуснали грешка, казвайте - ще я оправим.
Ако вие имате задача, която бихте споделили, заповядайте долу в коментарите.
Като цяло това е списък от задачи, измислени и решени "на крак", така че не сме се стремили да дефинираме корнър кейсове или стриктно поведение при неочакван вход.
Идеята е да ви дадем занимавка, храна за размисъл, нещо, с което да започнете.
Чувствайте се свободни да надграждате инструкциите и да модифицирате, ако го намерите за интересно и вълнуващо.
Задачите са в следните категории:
* Типове данни;
* Функции;
* Декоратори;
* ООП;
* Context managers and exceptions.
Всяка категория има по три задачи.
Във всяка задача има примерно решение, което сме замаскирали, за да не ви дразни периферното зрение.
Замаскирането е направно чрез Base64 encoding.
Ето примерен код, който показва как можете да енкоуднете, или декоуднете нещо.
```
import base64
def to_base64(text):
"""Convert text to Base64."""
text_bytes = text.encode('utf-8')
b64_bytes = base64.b64encode(text_bytes)
return b64_bytes.decode('utf-8')
def from_base64(b64_text):
"""Convert text from Base64."""
b64_bytes = b64_text.encode('utf-8')
text_bytes = base64.b64decode(b64_bytes)
return text_bytes.decode('utf-8')
# Можете да дефинирате стрингове с много редове, ограждайки ги в тройни кавички.
CODE = """
def fun():
return "Примерна функция."
"""
# Това е енкоуднатата версия на горното, за да визуализирам и обратния процес.
ENCODED = "CmRlZiBmdW4oKToKICAgIHJldHVybiAi0J/RgNC40LzQtdGA0L3QsCDRhNGD0L3QutGG0LjRjy4iCg=="
print(to_base64(CODE))
print(from_base64(ENCODED))
```
С две думи - взимате енкоуднатата версия от тук, слагате я в `ENCODED` константата по-горе и рънвате.
Можете да си закоментирате реда, който принти `to_base64(CODE)`, тъй като той е оставен само за да ви покаже как сме енкоуднали суровия код.
Това го споделяме, защото е на Python. Ако не ви се занимава с това, можете да използвате и [това](https://www.base64decode.org/).
Надявам се задачите да са полезни и интересни...
Ако не са - [Always Look On The Bright Side Of Life](https://www.youtube.com/watch?v=L2Wx230gYJw)
## Типове данни
### Задача 1
Напиши програма, която евалюира потребителски инпут и създава обекти от конкретен тип и конкретна стойност. Принтира репрезентацията на създадения обект.
```
python program.py
<<< Въведи тип:
<<< int
>>> Въведи аргументи:
<<< 5
>>> 5
python program.py
>>> Въведи тип:
<<< str
>>> Въведи аргументи:
<<< something
>>> 'something'
python program.py
>>> Въведи тип:
<<< list[int]
>>> Въведи аргументи:
<<< 1, 2, 3
>>> [1, 2, 3]
```
Програмата може да използва функцията `input`, за да поиска вход от потребителя.
Опитайте наистина да създадете обект и да му принтирате стойността чрез `repr()`, а не просто да сглабяте репрезентацията му ръчно с конкатенация. По-лесно и по-интересно е.
Ако искате, се заиграйте с типовете данни и вход, които да поддържате, но ето примерен вход, който да поддържате.
```
int - Цяло число. Така, както е подадено.
float - Дробно число. Така, както е подадено.
str - Символен низ. Така, както е подаден.
list[str] - Списък от стрингове. Очаква вход от елементи, разделени със запетая. Всеки елемент ще стане отделен обект в списъка. Махнете интервалите в началото и в края на стринга, така че вход "1, 2, 3" резултира в "['1', '2', '3']"
list[int] - Списък от цели числа. Очаква вход от елементи, разделени със запетая. По-различно от горното, защото трябва да "кастнете" елементите от списъка към цяло число.
dict[str] - Речник с ключ(стринг) и стойност(стринг). Очаква вход от елементи, разделени със запетая. Ключът е отделен от стойността с ":". При вход "a:1,b:2" -> "{'a': '1', 'b': '2'}".
```
Примерно решение:
```
CnR5cGVzID0gewogICAgJ2ludCc6IChpbnQsIGxhbWJkYSBhcmc6IGFyZyksCiAgICAnZmxvYXQnOiAoZmxvYXQsIGxhbWJkYSBhcmc6IGFyZyksCiAgICAnc3RyJzogKHN0ciwgbGFtYmRhIGFyZzogYXJnKSwKICAgICdsaXN0W3N0cl0nOiAobGlzdCwgbGFtYmRhIGFyZzogbWFwKHN0ci5zdHJpcCwgYXJnLnNwbGl0KCcsJykpKSwKICAgICdsaXN0W2ludF0nOiAobGlzdCwgbGFtYmRhIGFyZzogbWFwKGludCwgbWFwKHN0ci5zdHJpcCwgYXJnLnNwbGl0KCcsJykpKSksCiAgICAnZGljdFtzdHJdJzogKGRpY3QsIGxhbWJkYSBhcmc6IG1hcChsYW1iZGEgZWw6IHR1cGxlKG1hcChzdHIuc3RyaXAsIGVsLnNwbGl0KCc6JykpKSwgYXJnLnNwbGl0KCcsJykpKQp9CgppbnB1dF90eXBlID0gaW5wdXQoJ9CS0YrQstC10LTQuCDRgtC40L86ICcpCmlucHV0X2FyZ3MgPSBpbnB1dCgn0JLRitCy0LXQtNC4INCw0YDQs9GD0LzQtdC90YLQuDogJykKCnR5cGVfLCBhcmdzID0gdHlwZXNbaW5wdXRfdHlwZV0KcHJpbnQocmVwcih0eXBlXyhhcmdzKGlucHV0X2FyZ3MpKSkpCg==
```
### Задача 2
Напиши програма, която очаква потребителски вход на дробно число и извежда репрезентация на речник, който дефинира отделно цялата и дробната му част.
```
python program.py
>>> Въведи число:
<<< 3.14159
>>> {'whole_part': 3, 'dec_part': 14159}
```
Програмата може да използва функцията `input`, за да поиска вход от потребителя.
Опитайте наистина да създадете обект и да му принтирате стойността чрез `repr()`, а не просто да сглабяте репрезентацията му ръчно с конкатенация. По-лесно и по-интересно е.
Примерно решение:
```
CmlucHV0X251bWJlciA9IGlucHV0KCfQktGK0LLQtdC00Lgg0YfQuNGB0LvQvjogJykKd2hvbGVfcGFydCwgZGVjX3BhcnQgPSBtYXAoaW50LCBzdHIoaW5wdXRfbnVtYmVyKS5zcGxpdCgnLicpKQpwcmludChkaWN0KHdob2xlX3BhcnQ9d2hvbGVfcGFydCwgZGVjX3BhcnQ9ZGVjX3BhcnQpKQo=
```
### Задача 3
Напиши програма, която очаква потребителски вход на текст на кирилица и извежда текста преведен на шльокавица.
```
python program.py
>>> Въведи текст:
<<< Шльокавица. Едно две три.
>>> Shljokavitsa. Edno dve tri.
```
Програмата може да използва функцията `input`, за да поиска вход от потребителя.
Няма значение как ще си дефинирате превода на самите букви. Идеята е да тренирате Питон, а не да се чудите как е по-правилно да се преведе "ь" например. Опитайте да дефинирате превода само веднъж и само за малки букви, а да напишете логика, която се справя с главните. Не закачайте препинателните знаци.
Примерно решение:
```
Cm1hcHBpbmcgPSB7CiAgICAn0LAnOiAnYScsCiAgICAn0LEnOiAnYicsCiAgICAn0LInOiAndicsCiAgICAn0LMnOiAnZycsCiAgICAn0LQnOiAnZCcsCiAgICAn0LUnOiAnZScsCiAgICAn0LYnOiAnemgnLAogICAgJ9C3JzogJ3onLAogICAgJ9C4JzogJ2knLAogICAgJ9C5JzogJ2onLAogICAgJ9C6JzogJ2snLAogICAgJ9C7JzogJ2wnLAogICAgJ9C8JzogJ20nLAogICAgJ9C9JzogJ24nLAogICAgJ9C+JzogJ28nLAogICAgJ9C/JzogJ3AnLAogICAgJ9GAJzogJ3InLAogICAgJ9GBJzogJ3MnLAogICAgJ9GCJzogJ3QnLAogICAgJ9GDJzogJ3UnLAogICAgJ9GEJzogJ2YnLAogICAgJ9GFJzogJ2gnLAogICAgJ9GGJzogJ3RzJywKICAgICfRhyc6ICdjaCcsCiAgICAn0YgnOiAnc2gnLAogICAgJ9GJJzogJ3NodCcsCiAgICAn0YonOiAneScsCiAgICAn0YwnOiAnaicsCiAgICAn0Y4nOiAnaXUnLAogICAgJ9GPJzogJ2lhJwp9CgppbnB1dF90ZXh0ID0gaW5wdXQoJ9CS0YrQstC10LTQuCDRgtC10LrRgdGCOiAnKQoKdHJhbnNsYXRlZF90ZXh0ID0gW10KZm9yIGNoYXIgaW4gaW5wdXRfdGV4dDoKICAgIGlmIGNoYXIgaW4gbWFwcGluZzoKICAgICAgICB0cmFuc2xhdGVkX3RleHQuYXBwZW5kKG1hcHBpbmdbY2hhcl0pCiAgICBlbGlmIGNoYXIubG93ZXIoKSBpbiBtYXBwaW5nOgogICAgICAgIHRyYW5zbGF0ZWRfdGV4dC5hcHBlbmQobWFwcGluZ1tjaGFyLmxvd2VyKCldLmNhcGl0YWxpemUoKSkKICAgIGVsc2U6CiAgICAgICAgdHJhbnNsYXRlZF90ZXh0LmFwcGVuZChjaGFyKQoKcHJpbnQoJycuam9pbih0cmFuc2xhdGVkX3RleHQpKQo=
```
## Функции
### Задача 1
Напишете функция `spam`, която приема произволен брой позиционни и именувани аргументи, чийто брой обаче съвпада, т.е. ако имате три позиционни, трябва да имате три именувани. Функцията връща речник, чиито ключове са позиционните аргументи, а стойности са имената, използвани за подаване на именувани аргументи. Стойностите зад тези имена се игнорират.
```
print(spam(1, 2, 3, name1=4, name2=5, name3=6))
>>> {1: 'name1', 2: 'name2', 3: 'name3'}
```
Не се притеснявайте от факта, че речникът по принцип е неподреден. От Python 3.6 насам kwargs е подреден речник и редът е гарантиран.
Примерно решение:
```
CmRlZiBzcGFtKCphcmdzLCAqKmt3YXJncyk6CiAgICByZXR1cm4gZGljdCh6aXAoYXJncywga3dhcmdzLmtleXMoKSkpCg==
```
### Задача 2
Напишете функция `spam`, която приема два позиционни параметъра. Един `tuple` от специални стрингове и един `int`. Функцията връща `True`, ако числото, подадено като втори параметър, се намира в някой от затворените интервали, дефинирани от първия.
Интервалите смятам за очевидни, но ако не е ясно - питайте.
```
print(spam(('1|3', '2|9', '11|15'), 1)) # True
print(spam(('1|3', '2|9', '11|15'), 12)) # True
print(spam(('1|3', '2|9', '11|15'), 55)) # False
```
Примерно решение:
```
CmRlZiBzcGFtKGl0ZW1zLCBzdW1fKToKICAgIGZvciBpdGVtIGluIGl0ZW1zOgogICAgICAgIG1pbl8sIG1heF8gPSBpdGVtLnNwbGl0KCd8JykKICAgICAgICBpZiBtaW5fIDw9IHN1bV8gPD0gbWF4XzoKICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgIHJldHVybiBGYWxzZQo=
```
### Задача 3
Напишете функция `spam`, която приема един позиционен аргумент от тип `list` от `str` и произволен брой именувани аргументи от тип `str`. Функцията връща `True`, ако в списъка, подаден като първи аргумент, има елемент, който съдържа в себе си стринга, подаден като втори аргумент (името на параметъра или стойността му). В противен случай връща `False`.
```
# Няма именувани параметри, така че проверката е ясна.
print(spam(['asd', 'qwerty'])) # False
# Името "sd" се съдържа в първия елемент от списъка.
print(spam(['asd', 'qwerty'], sd=1)) # True
# Името "qwerty" се съдържа във втория елемент от списъка. Реално съвпада с него.
print(spam(['asd', 'qwerty'], qwerty=1)) # True
# Стойността "wer" се съдържа във втория елемент от списъка.
print(spam(['asd', 'qwerty'], something='wer')) # True
```
Примерно решение:
```
CmRlZiBzcGFtKGl0ZW1zLCAqKmt3YXJncyk6CiAgICBmb3IgaXRlbSBpbiBpdGVtczoKICAgICAgICBmb3Iga2V5LCB2YWx1ZSBpbiBrd2FyZ3MuaXRlbXMoKToKICAgICAgICAgICAgaWYga2V5IGluIGl0ZW06CiAgICAgICAgICAgICAgICByZXR1cm4gVHJ1ZQogICAgICAgICAgICBpZiBpc2luc3RhbmNlKHZhbHVlLCBzdHIpIGFuZCB2YWx1ZSBpbiBpdGVtOgogICAgICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgIHJldHVybiBGYWxzZQo=
```
## Декоратори
### Задача 1
Напишете декоратор `spam`, който може да декорира произволна функция, очакваща само именувани аргументи.
Декораторът трябва да разменя местата на име/стойност на всички подадени именувани аргументи.
За визуализация използвам функцията `helper_fun`.
```
@spam
def helper_fun(**kwargs):
for key, value in kwargs.items():
print(f"'{key}' passed with a value of '{value}'")
helper_fun(to_be_value='to_be_key')
```
Очевидно се очаква стойностите на именуваните аргументи да са стрингове, защото след размяната те ще станат имена, а ако не са, ще се породи грешка, но това е ок. Нека работим само със стрингове.
Примерно решение:
```
CmRlZiBzcGFtKGZ1bik6CiAgICBkZWYgZGVjb3JhdGVkKCoqa3dhcmdzKToKICAgICAgICByZXR1cm4gZnVuKCoqZGljdChtYXAocmV2ZXJzZWQsIGt3YXJncy5pdGVtcygpKSkpCiAgICByZXR1cm4gZGVjb3JhdGVkCg==
```
### Задача 2
Напишете декоратор `spam`, който може да декорира произволна функция. Декораторът очаква като аргумент функция, която да извика непосредствено преди изпълнение на декорираната функция, подавайки пълният набор аргументи, които декорираната функция е получила. Позиционните аргументи се подават директно, а от именуваните използваме само стойностите им и ги подаваме като позиционни.
За визуализация използвам функцията `helper_fun`.
```
@spam(print)
def helper_fun(*args, **kwargs):
pass
helper_fun(1, 2, 3, asd=123)
>>> 1 2 3 123
>>> helper_fun was called with (1, 2, 3) and {'asd': 123}
# Функцията helper_fun не прави нищо, освен да принтира вторият ред от примера по-горе.
# Преди това, обаче, декораторът spam е извикал функцията print,
# подавайки 4 позиционни параметъра: 1, 2, 3 и 123.
# Първият ред от резултата е резултата от извикването на този print.
```
Забележете, че в примера по-горе, от именувания (asd=123) сме използвали само неговата стойност (123) и сме го подали на `print`. Подавайки именуван на `print`, ще получите грешка.
Примерно решение:
```
CmRlZiBzcGFtKGluaXRfZnVuKToKICAgIGRlZiBkZWNvcmF0b3IoZnVuKToKICAgICAgICBkZWYgZGVjb3JhdGVkKCphcmdzLCAqKmt3YXJncyk6CiAgICAgICAgICAgIGluaXRfZnVuKCooYXJncyArIHR1cGxlKGt3YXJncy52YWx1ZXMoKSkpKQogICAgICAgICAgICByZXR1cm4gZnVuKCphcmdzLCAqKmt3YXJncykKICAgICAgICByZXR1cm4gZGVjb3JhdGVkCiAgICByZXR1cm4gZGVjb3JhdG9yCg==
```
### Задача 3
Напишете декоратор `spam`, който може да се прилага върху декоратори. Целта му е при инициализиране на кода (т.е. по време на прилагане на декоратора) да се принтира текст, показваш коя функция с кой декоратор е била декорирана.
За визуализация използвам функцията `helper_fun` и декоратора `plus_5_decorator`.
Целта на функцията е просто да върне числото 8, а декораторът `plus_5_decorator` добавя 8 към резултата от произволна функция, която е декорирал.
```
@spam
def plus_5_decorator(fun):
def decorated(*args, **kwargs):
return fun(*args, **kwargs) + 5
return decorated
@plus_5_decorator
def helper_fun(*args, **kwargs):
return 8
# При изпълнение на този файл (дори да не извикате helper_fun), се очаква следния изход.
# Тъй като декораторът plus_5_decorator е декориран със spam, всеки път,
# когато приложите plus_5_decorator някъде, в конзолата ще виждате коя функция
# е била декорирана с него.
>>> helper_fun has been decorated with plus_5_decorator
```
Примерно решение:
```
CmRlZiBzcGFtKGZ1bik6CiAgICBkZWYgZGVjb3JhdGVkKHRhcmdldCk6CiAgICAgICAgcHJpbnQoZiJ7dGFyZ2V0Ll9fbmFtZV9ffSBoYXMgYmVlbiBkZWNvcmF0ZWQgd2l0aCB7ZnVuLl9fbmFtZV9ffSIpCiAgICAgICAgcmV0dXJuIGZ1bih0YXJnZXQpCiAgICByZXR1cm4gZGVjb3JhdGVkCg==
```
## ООП
### Задача 1
Напишете миксин `GangstaMixIn`, който може да се прилага на произволен клас, променяйки стринговата репрезентазия на неговите инстанции, като я "украсява" във формат "This is <normal representation>, yo!"
```
class VerboseInt(GangstaMixIn, int):
pass
class VerboseStr(GangstaMixIn, str):
pass
print(VerboseInt(10)) # This is 10, yo!
print(VerboseStr('Dr. Dre')) # This is Dr. Dre, yo!
```
Примерно решение:
```
CmNsYXNzIEdhbmdzdGFNaXhJbjoKCiAgICBkZWYgX19zdHJfXyhzZWxmKToKICAgICAgICByZXR1cm4gZiJUaGlzIGlzIHtzdXBlcigpLl9fc3RyX18oKX0sIHlvISI==
```
### Задача 2
Напишете клас `Weird`, чиито инстанции не очакват никакви аргументи при създаване. Инстанциите от класа могат да се извикат, резултат от което е нова инстанция на същия клас.
```
weird = Weird()
print(weird) # <__main__.Weird object at 0x000001E1E6938210>
print(weird()) # <__main__.Weird object at 0x000001E1E6938250>
print(weird()) # <__main__.Weird object at 0x0000021F40BF82D0>
```
Примерно решение:
```
CmNsYXNzIFdlaXJkOgoKICAgIEBjbGFzc21ldGhvZAogICAgZGVmIF9fY2FsbF9fKGNscyk6CiAgICAgICAgcmV0dXJuIGNscygpCg==
```
### Задача 3
Напишете клас `Storage`, който приема един позиционен параметър от тип `type` и произволен брой именувани параметри. При инициализация класът създава по една инстанция на типа, подаден като позиционен аргумент, за всеки именуван аргумент, подавайки този аргумент на класа подаден като `type`. Инстанции на класа `Storage` трябва да могат да се подават на функцията `len`, а очакваният резултат е броя инстанции, които са създадени при инициализирането (с други думи броя позиционни параметри). Инстанции на класа `Storage` трябва да могат да бъдат индексирани с квадратни скоби (само ще четем, без да пишем с квадратни скоби). Ако в квадратните скоби е подадено име, съвпадащи с името на някой именуван параметър, който е подаден при създаване на инстанцията, връща се създадената инстанция на класа, подаден като позиционен аргумент. Ако не - някаква грешка.
Използвам класа `Example` като примерен тип, който `Storage` може да приеме.
```
class Example:
def __init__(self, **kwargs):
print(f'New example instance created from {kwargs}')
self._kwargs = kwargs
def __str__(self):
return f"Example instance created form {self._kwargs}"
storage = Storage(Example, name1=1, name2=2)
# New example instance created from {'name1': 1}
# New example instance created from {'name2': 2}
print(len(storage))
# 2
print(storage['name1'])
# Example instance created form {'name1': 1}
```
Примерно решение:
```
CmNsYXNzIFN0b3JhZ2U6CgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGNscywgKiprd2FyZ3MpOgogICAgICAgIHNlbGYuX2luc3RhbmNlcyA9IHt9CiAgICAgICAgZm9yIGtleSwgdmFsdWUgaW4ga3dhcmdzLml0ZW1zKCk6CiAgICAgICAgICAgIHNlbGYuX2luc3RhbmNlc1trZXldID0gY2xzKCoqZGljdChbKGtleSwgdmFsdWUpXSkpCiAgICAKICAgIGRlZiBfX2xlbl9fKHNlbGYpOgogICAgICAgIHJldHVybiBsZW4oc2VsZi5faW5zdGFuY2VzKQogICAgCiAgICBkZWYgX19nZXRpdGVtX18oc2VsZiwgbmFtZSk6CiAgICAgICAgcmV0dXJuIHNlbGYuX2luc3RhbmNlc1tuYW1lXQo=
```
## Context managers and exceptions
### Задача 1
Напишете клас `ReversedPrint`, чиито инстанции могат да се ползват с `with`. В тялото на `with` с инстанции на този клас очакваме `print` функцията да обръща текст, подаден като аргумент. Извън тялото очакваме функцията да работи нормално.
```
print('Normal print.') # Normal print.
with ReversedPrint():
print('.tnirp desreveR') # Reversed print.
print('Normal print again.') # Normal print again.
```
За да модифицирате поведението на функцията, можете да използвате `__builtins__.print`, което ви дава достъп да я презапишете. Много лоша практика, но за този дребен пример става, а и всичко ще е само в контекста на нашият мениджър. Разбира се, можете да използвате и друг подход, ако измислите такъв.
Примерно решение:
```
CmNsYXNzIFJldmVyc2VkUHJpbnQ6CgogICAgZGVmIF9fZW50ZXJfXyhzZWxmKToKICAgICAgICBzZWxmLl9wcmludCA9IF9fYnVpbHRpbnNfXy5wcmludAogICAgICAgIF9fYnVpbHRpbnNfXy5wcmludCA9IGxhbWJkYSB4OiBzZWxmLl9wcmludCgnJy5qb2luKHJldmVyc2VkKHgpKSkKICAgIAogICAgZGVmIF9fZXhpdF9fKHNlbGYsICpfKToKICAgICAgICBfX2J1aWx0aW5zX18ucHJpbnQgPSBzZWxmLl9wcmludAo=
```
### Задача 2
Напишете клас `InfiniteContextManager`, чиито инстанции могат да се ползват с `with`. Инстанции на мениджъра ви трябва да могат да се извикват, връщайки обект, който също да поддържа контекст мениджър протокола, и чиито инстанции също да могат да бъдат извиквани, което прави същото, и така до безкрай...
При влизане в блока вашата инстанция трябва да връща обект, който да можете да хванете с `with ... as ...:`.
Обектът трябва да може да се подава на функцията `len`, като очакваният резултат е броят извиквания, през които сте минали, за да инициализирате контекстния си мениджър.
```
with InfiniteContextManager() as cm:
print(len(cm)) # 1
with InfiniteContextManager()()()()() as cm:
print(len(cm)) # 5
```
Примерно решение:
```
CmNsYXNzIEluZmluaXRlQ29udGV4dE1hbmFnZXI6CgogICAgZGVmIF9faW5pdF9fKHNlbGYpOgogICAgICAgIHNlbGYuX2NvdW50ID0gMQoKICAgIGRlZiBfX2NhbGxfXyhzZWxmKToKICAgICAgICBzZWxmLl9jb3VudCArPSAxCiAgICAgICAgcmV0dXJuIHNlbGYKICAgIAogICAgZGVmIF9fbGVuX18oc2VsZik6CiAgICAgICAgcmV0dXJuIHNlbGYuX2NvdW50CgogICAgZGVmIF9fZW50ZXJfXyhzZWxmKToKICAgICAgICByZXR1cm4gc2VsZgogICAgCiAgICBkZWYgX19leGl0X18oc2VsZiwgKl8pOgogICAgICAgIHBhc3MK
```
### Задача 3
Напишете функция `smart_getattr`, която приема два позиционни аргумента. Един произволен обект и един стринг - име на атрибут, който да търсим в обекта. Функцията се опитва да върне от обекта стойност с подаденото име чрез индексация - `object[name]`. Ако това не стане (има грешка), опитва да върне атрибут с това име - `object.name`. Ако и това не става, хвърля `MegaError`, който вие сами трябва да дефинирате.
```
# Създаване на нов клас, наследяващ речник, и директна инициализация на
# инстанция от този клас. Това е само за да е възможно сетването на атрибути
# към иснтанцията, което е невъзможно с обикновен речник.
mega_dict = type('', (dict, ), {})({'name1': 'val1'})
mega_dict.example = 123
print(smart_getattr(mega_dict, 'name1')) # val1
print(smart_getattr(mega_dict, 'example')) # 123
print(smart_getattr(mega_dict, 'invalid')) # MegaError: Value invalid not found anywhere.
```
Примерно решение:
```
CmNsYXNzIE1lZ2FFcnJvcihFeGNlcHRpb24pOgogICAgcGFzcwoKZGVmIHNtYXJ0X2dldGF0dHIob2JqLCB2YWx1ZSk6CiAgICB0cnk6CiAgICAgICAgcmV0dXJuIG9ialt2YWx1ZV0KICAgIGV4Y2VwdDoKICAgICAgICB0cnk6CiAgICAgICAgICAgIHJldHVybiBnZXRhdHRyKG9iaiwgdmFsdWUpCiAgICAgICAgZXhjZXB0OgogICAgICAgICAgICByYWlzZSBNZWdhRXJyb3IoZidWYWx1ZSB7dmFsdWV9IG5vdCBmb3VuZCBhbnl3aGVyZS4nKQo=
```