Примерни решения на задачите по теми 2 и 3#

Задача 1 (0.25 т.)#

По дадена дата във формат YYYY-MM-DD (тип str) да се върне наредена тройка от числа (int), съответстващи на годината, месеца и деня.

  • Hint: може да използвате str.split

def extract_date_components(date_string):
    year, month, day = date_string.split('-')
    return int(year), int(month), int(day)


# Tests:

assert extract_date_components('1970-01-01') == (1970, 1, 1)
assert extract_date_components('2023-10-22') == (2023, 10, 22)
assert extract_date_components('0000-12-25') == (0, 12, 25)
assert extract_date_components('2009-02-29') == (2009, 2, 29), "no one said it should be a valid date"

"✅ All OK! +0.25 points"
'✅ All OK! +0.25 points'

Задача 2 (0.25т.)#

Напишете функция format_date_components, която:

  • приема 3 параметъра: year, month, day (тип int)

  • приема oще 1 параметър separator (тип str), по подразбиране със стойност '-'

  • връща str с годината, месеца и деня, разделени със separator символа

  • цифрите на месеца и деня да са точно 2

Number formatting reference: https://www.pythoncheatsheet.org/cheatsheet/string-formatting#formatting-digits

def format_date_components(year, month, day, separator='-'):
    return f'{year:04}{separator}{month:02}{separator}{day:02}'

# Tests:

assert format_date_components(2023, 10, 22) == '2023-10-22'
assert format_date_components(2112, 12, 21, separator='/') == '2112/12/21'
assert format_date_components(1970, 1, 1) == '1970-01-01', "check your leading zeroes formatting!"
assert format_date_components(1, 12, 25) == '0001-12-25', "leading zeroes for the year as well!"

"✅ All OK! +0.25 points"
'✅ All OK! +0.25 points'

Задача 3 (0.25т.)#

Съединете двата dict-а в един.

def merge(d1, d2):
    return {**d1, **d2}


# Tests:

assert merge({"name": "Pesho", "year": 1999}, {"groups": ["A", "B"]}) == {"name": "Pesho", "year": 1999, "groups": ["A", "B"]}
assert merge({}, {}) == {}

"✅ All OK! +0.25 points"
'✅ All OK! +0.25 points'

Задача 4 (0.25т.)#

Намерете броя на цифрите на дадено число (в десетична бройна система).

def digits_count(n):
    return len(str(n))


# Tests:

assert digits_count(0) == 1
assert digits_count(1) == 1
assert digits_count(9) == 1
assert digits_count(10) == 2
assert digits_count(666) == 3
assert digits_count(1234) == 4
assert digits_count(9876543210) == 10
assert digits_count(31415926535897932384626433832795028841971693993751058209749445923078164062862089986280) == 86

"✅ All OK! +0.25 points"
'✅ All OK! +0.25 points'

Задача 5 (0.25т.)#

Пребройте колко различни символа има в даден низ.

def unique_chars_count(s):
    return len(set(s))

# Tests:

assert unique_chars_count("abcdef") == 6
assert unique_chars_count("aabbcc") == 3
assert unique_chars_count("abcabc") == 3
assert unique_chars_count("aaaaaa") == 1
assert unique_chars_count("") == 0

"✅ All OK! +0.25 points"
'✅ All OK! +0.25 points'

Задача 6 (0.75т.)#

Цензурирайте всички лоши думички в даден текст, заменяйки ги със звездички.

  • Лошите думички сa дадени в списъка BAD_WORDS.

  • Върнете текста, като замените всички лоши думички със звездички ("*") (толкова на брой, колкото символи има въпросната дума).

  • За простота не проверявайте за главни и малки букви, а ги заменяйте както са ви дадени.

  • Hint: за да създадете стринг с n на брой символа "x", използвайте n * "x".

  • Hint 2: ползвайте str.split и str.join

BAD_WORDS = [
    "fuck",
    "shit",
    "bullshit",
    "bastard",
    "bitch",
    "whore",
    "damn",
]


def censored(text):
    words = text.split()
    result_words = []

    for word in words:
        if word.lower() in BAD_WORDS:
            result_words.append("*" * len(word))
        else:
            result_words.append(word)

    return " ".join(result_words)


# Tests:

assert censored("this line should not be censored at all") == "this line should not be censored at all"
assert censored("fuck this bitch") == "**** this *****"
assert censored("This task is UTTER BULLshit") == "This task is UTTER ********", "be careful with the case!"
assert censored("") == ""

"✅ All OK! +0.75 points"
'✅ All OK! +0.75 points'
# Just for the sake of completeness, here's how to solve this task in a more "pythonic" way:
# (Note: learning the ways of '04 - Functional programming' is required)
def censored(text):
    return " ".join(
        "*" * len(word) if word.lower() in BAD_WORDS else word
        for word in text.split()
    )

# Tests:

assert censored("this line should not be censored at all") == "this line should not be censored at all"
assert censored("fuck this bitch") == "**** this *****"
assert censored("This task is UTTER BULLshit") == "This task is UTTER ********", "be careful with the case!"
assert censored("") == ""

"✅ All OK! +1 snek 🐍"
'✅ All OK! +1 snek 🐍'

Задача 7 (0.5т.)#

Направете стек:

  • в инициализатора си да приема неопределен брой аргументи - елементите на стека (първи-последен <-> отляво-надясно)

  • атрибут top - връща последния елемент от стека (None ако няма такъв)

  • метод push(x) - добавя x към края на стека

  • метод pop() - премахва последния елемент от стека

  • предефинирайте __len__

class Stack:
    def __init__(self, *args) -> None:
        self.__elements = list(args)

    @property
    def top(self):
        return self.__elements[-1] if len(self.__elements) > 0 else None
    
    def push(self, x):
        self.__elements.append(x)
    
    def pop(self):
        self.__elements.pop()

    def __len__(self):
        return len(self.__elements)


# Tests:
assert Stack().top is None
assert Stack("a").top == "a"
assert Stack(1, 2, 3, 4, 5).top == 5
assert Stack(*list(range(1000))).top == 999

# pop
s = Stack("a", "b", "c", "d")
s.pop()
assert s.top == "c"
s.pop()
s.pop()
assert s.top == "a"
s.pop()
assert s.top is None

# push
s.push("X")
assert s.top == "X"
s.push("X")
s.push("X")
assert s.top == "X"

# len
assert len(s) == 3
s.pop(); s.pop(); s.pop()
assert len(s) == 0

"✅ All OK! +0.5 point"
'✅ All OK! +0.5 point'

Задача 8 (0.5 т.)#

Създайте функция find_shortest_and_longest_words, която:

  • Приема 2 параметъра - списък и буква

  • Връща наредена двойка от най-късата и най-дългата дума в списъка, която започва с дадената буква

def find_shortest_and_longest_words(words, letter):
    shortest, longest = None, None

    for word in words:
        if word.startswith(letter):  # safer than `word[0] == letter` because it won't crash on empty strings
            if shortest is None or len(word) < len(shortest):
                shortest = word
            if longest is None or len(word) > len(longest):
                longest = word
    
    return shortest, longest


# Tests

assert find_shortest_and_longest_words(["abracadabra", "banana", "kugelschreiber", "ant", "almost", "thisshouldbeaverylongword"], "a") == ("ant", "abracadabra")
assert find_shortest_and_longest_words(["abracadabra", "banana", "kugelschreiber", "ant", "almost", "thisshouldbeaverylongword"], "b") == ("banana", "banana")

"✅ All OK! +0.5 points"
'✅ All OK! +0.5 points'
# Just for the sake of completeness once again, here's an alternative:
# (again, knowledge gathered in '04 - Functional programming' is required)
def find_shortest_and_longest_words(words, letter):
    filtered_words = [w for w in words if w.startswith(letter)]
    
    if not filtered_words:
        return None, None
    
    shortest = min(filtered_words, key=len)
    longest = max(filtered_words, key=len)

    return shortest, longest

# Tests

assert find_shortest_and_longest_words(["abracadabra", "banana", "kugelschreiber", "ant", "almost", "thisshouldbeaverylongword"], "a") == ("ant", "abracadabra")
assert find_shortest_and_longest_words(["abracadabra", "banana", "kugelschreiber", "ant", "almost", "thisshouldbeaverylongword"], "b") == ("banana", "banana")

"✅ All OK! +1 snek 🐍"
'✅ All OK! +1 snek 🐍'

Задача 9 (1т.)#

Да се дефинира клас Version, който моделира числата на една софтуерна версия. Нека има следните свойства:

  • да може да се инициализира по два различни начина:

    • чрез str във формат {major}.{minor}.{patch} (например "1.0.2")

    • чрез три int-a: major, minor и patch (например 1, 0, 2)

  • read-only атрибут major (тип int)

  • read-only атрибут minor (тип int)

  • read-only атрибут patch (тип int)

  • метод __str__, който конвертира обекта в str в подходящ формат

  • метод __repr__, който връща репрезентация на обекта в подходящ формат

  • метод __eq__, който сравнява две версии в няколко различни случая:

    1. ако другият операнд е от тип Version, сравнява ги с == по major, minor и patch

    2. ако другият операнд е от тип str, то първо конвертира низa към Version и след това прилага горното правило

    3. ако другият операнд е от друг тип, връща False

  • метод __ne__, който сравнява две версии по същия начин, но с != вместо ==

  • метод __lt__, който сравнява две версии по същия начин, но с < вместо ==

  • метод __gt__, който сравнява две версии по същия начин, но с > вместо ==

Този клас хешируем ли е? Защо?

class Version:
    def __init__(self, *args):
        if len(args) == 3:
            major, minor, patch = args
        elif len(args) == 1 and isinstance(args[0], str):
            major, minor, patch = args[0].split('.')
        else:
            # we won't expect this to happen
            # in the tests of this task
            # but in the real world
            # this would be one possible way
            # of handling invalid arguments
            raise ValueError("Invalid arguments: Either pass 3 integers or a single string in the format 'major.minor.patch'")
        
        self.__major = int(major)
        self.__minor = int(minor)
        self.__patch = int(patch)

    @property
    def major(self):
        return self.__major
    
    @property
    def minor(self):
        return self.__minor
    
    @property
    def patch(self):
        return self.__patch
    
    def __str__(self):
        return f"{self.major}.{self.minor}.{self.patch}"
    
    def __repr__(self):
        return f"Version({self.major}, {self.minor}, {self.patch})"
    
    def __to_tuple(self):
        """Helper method, used for comparison. Return a tuple of the version components."""
        return self.__major, self.__minor, self.__patch
    
    def __eq__(self, other):
        if isinstance(other, Version):
            return self.__to_tuple() == other.__to_tuple()
        elif isinstance(other, str):
            return self == Version(other)
        else:
            return NotImplemented
        
    def __ne__(self, other):
        return not self == other
        
    def __lt__(self, other):
        if isinstance(other, Version):
            return self.__to_tuple() < other.__to_tuple()  # works element-wise
        elif isinstance(other, str):
            return self < Version(other)
        else:
            return NotImplemented
        
    def __gt__(self, other):
        if isinstance(other, Version):
            return self.__to_tuple() > other.__to_tuple()  # analogous to __lt__
        elif isinstance(other, str):
            return self > Version(other)
        else:
            return NotImplemented
        
    # Regarding hashing:
    # ==================
    # Written this way, the class is practically immutable.
    # (It's not possible to change `__major`, `__minor` and `__patch` directly from outside
    # after the object has been created because only their read-only `@properties` are exposed
    # (except for name-mangled access, for which we can additionally guard and raise an exception
    # if someone attempts to set a value that way, e.g. via implementing `__setattr__` etc.)).
    #
    # Therefore, this class can be considered hashable
    # and a proper hashing function can be defined as follows:
    def __hash__(self):
        """Return hash((major, minor, patch))."""
        return hash(self.__to_tuple())


# Tests:

assert Version(1, 2, 3).major == 1
assert Version(1, 2, 3).minor == 2
assert Version(1, 2, 3).patch == 3
assert Version("1.2.3").major == 1
assert Version("1.2.3").minor == 2
assert Version("1.2.3").patch == 3
assert Version("10.2.304").major == 10
assert Version("10.2.304").minor == 2
assert Version("10.2.304").patch == 304
assert str(Version(1, 2, 3)) == "1.2.3"
assert repr(Version(1, 2, 3)) == "Version(1, 2, 3)"
assert Version(1, 2, 3) == Version(1, 2, 3)
assert Version(1, 2, 3) == "1.2.3"
assert Version(1, 2, 3) != Version(1, 2, 4)
assert Version(1, 2, 3) != "1.2.4"
assert Version(1, 2, 3) < Version(2, 0, 0)
assert Version(1, 2, 3) < "2.0.0"
assert Version(1, 2, 3) < Version(1, 3, 0)
assert Version(1, 2, 3) < "1.3.0"
assert Version(1, 2, 3) < Version(1, 2, 4)
assert Version(1, 2, 3) < "1.2.4"
assert Version("1.2.3") < Version("1.12.0")
assert Version("1.2.3") < "1.12.0"
assert Version(1, 2, 3) > Version(0, 9, 0)
assert Version(1, 2, 3) > "0.9.0"
assert Version(1, 2, 3) > Version(1, 1, 0)
assert Version(1, 2, 3) > "1.1.0"
assert Version(1, 2, 3) > Version(1, 2, 2)
assert Version(1, 2, 3) > "1.2.2"
assert Version("1.1.13") > Version("1.1.3")
assert Version("1.1.13") > "1.1.3"

"✅ All OK! +1 point"
'✅ All OK! +1 point'

Задача 10 (2 т.)#

Създайте клас ElectronicDevice, който има:

  • атрибут serial_number (низ, смесица от латински букви и числа)

  • атрибут brand (низ)

  • атрибут model (низ)

  • атрибут internal_memory (число)

  • инициализатор, който приема и задава стойности на дадените атрибути

  • метод show_device_info, който връща информация за устройството в следния формат: “You have electronic device {brand} {model} with {internal_memory}GB memory”

  • метод show_storage_info, който връща съобщение “Your device has {internal_memory}GB”

  • метод __str__

  • метод __repr__

Създайте клас Smartphone, който наследява класа ElectronicDevice. Нека има:

  • атрибут available_storage (число, първоначално равно на internal_memory)

  • атрибут main_camera_quality (число)

  • атрибут installed_apps (списък, по подразбиране е празен)

  • инициализатор, който приема и задава стойности на дадените атрибути

  • метод show_device_info, който връща информация за устройството в следния формат: “You have smartphone {brand} {model} with {internal_memory}GB memory and {main_camera_quality}MP camera”

  • метод show_storage_info, който връща съобщение “Your smartphone has {available_storage}GB available storage out of {internal_memory}GB”

  • метод install_app(app_name, space_needed), който:

    • Ако е възможно, добавя приложението към списъка с приложения и извежда съобщение “You successfully installed {app_name}”.

    • Ако не е възможно, връща съобщение “Seems like you don’t have enough space. Available storage space: {available_storage}GB”

  • метод __str__

  • метод __repr__

Създайте клас Laptop, който наследява класа ElectronicDevice. Нека има:

  • атрибут processor (низ)

  • атрибут processor_version (низ)

  • инициализатор, който приема и задава стойности на дадените атрибути

  • метод show_device_info, който връща информация за устройството в следния формат: “You have laptop {brand} {model} with {internal_memory}GB memory and {processor} {processor_version} processor”

  • метод show_storage_info, който връща съобщение “Your laptop has {internal_memory}GB”

  • метод connect_device(device_name), който връща следното съобщение: “You just connected {device_name} to {brand} {model}”

  • метод __str__

  • метод __repr__

class ElectronicDevice:
    def __init__(self, serial_number, brand, model, internal_memory):
        self.serial_number = serial_number
        self.brand = brand
        self.model = model
        self.internal_memory = internal_memory
    
    def show_device_info(self):
        return f"You have electronic device {self.brand} {self.model} with {self.internal_memory}GB memory"
    
    def show_storage_info(self):
        return f"Your device has {self.internal_memory}GB"
    
    def __str__(self):
        return self.show_device_info()
    
    def __repr__(self):
        return f"ElectronicDevice({self.serial_number}, {self.brand}, {self.model}, {self.internal_memory})"


class Smartphone(ElectronicDevice):
    def __init__(self, serial_number, brand, model, internal_memory, main_camera_quality):
        super().__init__(serial_number, brand, model, internal_memory)
        self.available_storage = internal_memory
        self.main_camera_quality = main_camera_quality
        self.installed_apps = []
    
    def install_app(self, app_name, app_size):
        if app_size > self.available_storage:
            return f"Seems like you don't have enough space. Available storage space: {self.available_storage}GB"
        else:
            self.available_storage -= app_size
            self.installed_apps.append(app_name)
            return f"You successfully installed {app_name}"
    
    def show_device_info(self):
        return f"You have smartphone {self.brand} {self.model} with {self.internal_memory}GB memory and {self.main_camera_quality}MP camera"
    
    def show_storage_info(self):
        return f"Your smartphone has {self.available_storage}GB available storage out of {self.internal_memory}GB"
    
    def __str__(self):
        return self.show_device_info()
    
    def __repr__(self):
        return f"Smartphone({self.serial_number}, {self.brand}, {self.model}, {self.internal_memory}, {self.main_camera_quality})"


class Laptop(ElectronicDevice):
    def __init__(self, serial_number, brand, model, internal_memory, processor, processor_version):
        super().__init__(serial_number, brand, model, internal_memory)
        self.processor = processor
        self.processor_version = processor_version
    
    def show_device_info(self):
        return f"You have laptop {self.brand} {self.model} with {self.internal_memory}GB memory and {self.processor} {self.processor_version} processor"
    
    def show_storage_info(self):
        return f"Your laptop has {self.internal_memory}GB"
    
    def connect_device(self, device):
        return f"You just connected {device} to {self.brand} {self.model}"
    
    def __str__(self):
        return self.show_device_info()
    
    def __repr__(self):
        return f"Laptop({self.serial_number}, {self.brand}, {self.model}, {self.internal_memory}, {self.processor}, {self.processor_version})"

# Tests:

# ElectronicDevice tests
test_electronic_device = ElectronicDevice("8vef15c6", "TestBrand", "TestModel", 128)
assert test_electronic_device.serial_number == "8vef15c6"
assert test_electronic_device.brand == "TestBrand"
assert test_electronic_device.model == "TestModel"
assert test_electronic_device.internal_memory == 128
assert test_electronic_device.show_device_info() == "You have electronic device TestBrand TestModel with 128GB memory"
assert test_electronic_device.show_storage_info() == "Your device has 128GB"

# Smarthphone tests
assert issubclass(Smartphone, ElectronicDevice)
test_smartphone = Smartphone("9pol23k7", "TestBrand2", "TestModel2", 256, main_camera_quality=13)
assert test_smartphone.serial_number == "9pol23k7"
assert test_smartphone.brand == "TestBrand2"
assert test_smartphone.model == "TestModel2"
assert test_smartphone.internal_memory == 256
assert test_smartphone.available_storage == 256
assert test_smartphone.main_camera_quality == 13
assert test_smartphone.installed_apps == []
assert test_smartphone.show_device_info() == "You have smartphone TestBrand2 TestModel2 with 256GB memory and 13MP camera"
res1 =  test_smartphone.install_app("TestApp", 56)
assert res1 == "You successfully installed TestApp"
assert len(test_smartphone.installed_apps) == 1
assert "TestApp" in test_smartphone.installed_apps
assert test_smartphone.show_storage_info() == "Your smartphone has 200GB available storage out of 256GB"
res2 = test_smartphone.install_app("TestApp2", 201)
assert res2 == "Seems like you don't have enough space. Available storage space: 200GB"
assert len(test_smartphone.installed_apps) == 1
assert "TestApp2" not in test_smartphone.installed_apps 

# Laptop tests
assert issubclass(Laptop, ElectronicDevice)
test_laptop = Laptop("7yeu14t3", "TestBrand3", "TestModel3", 512, "TestProcessor", "v5.2.1.13")
assert test_laptop.serial_number == "7yeu14t3"
assert test_laptop.brand == "TestBrand3"
assert test_laptop.model == "TestModel3"
assert test_laptop.internal_memory == 512
assert test_laptop.processor == "TestProcessor"
assert test_laptop.processor_version == "v5.2.1.13"
assert test_laptop.show_device_info() == "You have laptop TestBrand3 TestModel3 with 512GB memory and TestProcessor v5.2.1.13 processor"
assert test_laptop.show_storage_info() == "Your laptop has 512GB"
assert test_laptop.connect_device("TestConnectedDevice") == "You just connected TestConnectedDevice to TestBrand3 TestModel3"

"✅ All OK! +2.0 points"
'✅ All OK! +2.0 points'