Dekoratory i generatory


    
    1. Dekoratory - wstęp
    def censoring_wrapper(func):
      # Funkcja do sprawdzania, czy napis zawiera literę 'k'
      def contains_k(s):
          return 'k' in s
  
      def wrapper(*args, **kwargs):
          # Przefiltruj argumenty pozycyjne, usuwając te, które zawierają 'k'
          censored_args = [arg for arg in args if not contains_k(str(arg))]
  
          # Przefiltruj argumenty nazwane, usuwając te, których nazwa zawiera 'k'
          censored_kwargs = {key: value for key, value in kwargs.items() if not contains_k(key)}
  
          # Usuń argument 'd', jeśli istnieje
          if 'd' in censored_kwargs:
              del censored_kwargs['d']
  
          return func(*censored_args, **censored_kwargs)
  
      return wrapper


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    2. Składnia dekoratora
    def power(x, y):
      result = x ** y
      print(f"Argumenty: x={x}, y={y}, Wynik: {result}")
      return result



- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    3. Dekoratory z argumentami
    def enforce_types(t1, t2):
      def decorator(func):
          def inner(*args, **kwargs):
              # Sprawdź, czy argumenty są poprawnych typów
              a1, a2 = args
              if not isinstance(a1, t1):
                  raise TypeError(f"Argument {a1} nie jest typu {t1}")
              if not isinstance(a2, t2):
                  raise TypeError(f"Argument {a2} nie jest typu {t2}")
  
              return func(*args, **kwargs)
  
          return inner
  
      return decorator
  
    @enforce_types(int, int)
    # /* read-only */
    def add_ints(x, y):
        return x + y
    # /* read-only */
    
    
    
    @enforce_types(str, str) 
    # /* read-only */
    def concat_strings(s1, s2):
        return s1 + s2
    # /* read-only */


    
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    4. @lru_cache
    from secret_base_of_horrible_baddies import get_secret_plan
    # Słownik do przechowywania wyników memoizacji
    memo = {}
    
    def steal_plans(plan_list):
        stolen_plans = []
        for plan_number in plan_list:
            # Sprawdź, czy wynik jest już w pamięci podręcznej (memoizacja)
            if plan_number in memo:
                plan = memo[plan_number]
            else:
                plan = get_secret_plan(plan_number)
                # Zapamiętaj wynik w pamięci podręcznej
                memo[plan_number] = plan
    
            if plan is not None:
                stolen_plans.append(plan)
            if len(stolen_plans) >= 20:
                break
        return stolen_plans



    
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    5. @singledispatch
    def count_elements(container):
      if isinstance(container, (list, set, tuple)):
          return len(container)
      elif isinstance(container, str):
          # Podziel napis na słowa (używając spacji jako separatora) i zwróć ilość słów
          words = container.split()
          return len(words)
      elif isinstance(container, dict):
          # Ilość elementów w słowniku to suma liczby kluczy i liczby wartości
          return len(container.keys()) + len(container.values())
      else:
          # Obsługa innych rodzajów kontenerów lub nieznanych typów
          raise ValueError("Nieobsługiwany typ kontenera")


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    6. @wraps
    import functools

    def enforce_types(type1, type2):
        def decorator(func):
            @functools.wraps(func)
            def inner(a1, a2):
                if isinstance(a1, type1) and isinstance(a2, type2):
                    return func(a1, a2)
                else:
                    raise TypeError(f"Arguments must be of types {type1} and {type2}")
            
            return inner
        
        return decorator
    
    # Udekoruj funkcje add_ints() i concat_strings() za pomocą enforce_types
    @enforce_types(int, int)
    # /* read-only */
    def add_ints(x: int, y: int):
        """Adds two integers together"""
        return x + y
    # /* read-only */
    
    
    @enforce_types(str, str)    
    # /* read-only */
    def concat_strings(s1: str, s2: str):
        """Concatenates two strings together"""
        return s1 + s2
    # /* read-only */

    
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    7. Generatory - wstęp
    def neighbours():
      # Lista zawierająca nazwy krajów sąsiadujących z Polską
      neighbours_list = ['Rosja', 'Litwa', 'Białoruś', 'Ukraina', 'Czechy', 'Słowacja', 'Niemcy']
  
      # Iterujemy przez listę i zwracamy nazwy sąsiadujących krajów
      for country in neighbours_list:
          yield country


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    8. Dlaczego używać generatorów?
    def my_range(start, stop=None, step=1):
      if stop is None:
          start, stop = 0, start
  
      current = start
      while current < stop:
          yield current
          current += step


- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    9. Nieskończone generatory
    import itertools
    def fibonacci_gen():
        a, b = 0, 1
        while True:
            yield a
            a, b = b, a + b
    
    def nth_fibonacci(n):
        if n <= 0:
            raise ValueError("n must be a positive integer")
        nth_fib = next(itertools.islice(fibonacci_gen(), n, n+1))
        return nth_fib

          
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    10. Podgeneratory
    def join_with(iterators, sep):
      # Jeśli lista iterators jest pusta, zwracamy pusty iterator
      if not iterators:
          return iter([])
  
      # Tworzymy generator, który łączy iteratory z separatorem
      for i, iterator in enumerate(iterators):
          for item in iterator:
              yield item
          if i < len(iterators) - 1:  # Jeśli nie jest to ostatni iterator
              yield from sep  # Dodajemy elementy z sep




- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    11. Wczesne zakańczanie za pomocą return
    def short_list(numbers, k):
      for num in numbers:
          if num % k == 0:
              break
          yield num

 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

    12. Wyrażenie generatora
    def first_powers(k):
      return (2 ** i for i in range(k))
    
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -