diff --git a/decorators/class-based decorator.py b/decorators/class-based decorator.py new file mode 100644 index 0000000000000000000000000000000000000000..e0dd602883c673523f2a817088871351fb221b42 --- /dev/null +++ b/decorators/class-based decorator.py @@ -0,0 +1,33 @@ +from typing import Any, Callable +from functools import wraps + + +class callNTimes: + def __init__(self, times): + self.times = times + + def __call__(self, *args, **kwargs): + + if args and isinstance(args[0], Callable): + func = args[0] + + if not isinstance(self.times, int) or self.times < 0: + raise ValueError("Times must be positive number") + + @wraps(func) + def wrapper(*args, **kwargs): + for _ in range(self.times): + func(*args[1:], **kwargs) + + return wrapper + else: + raise TypeError("Trying to call decorator not on a function") + + +@callNTimes(3) +def say_whee(): + print("Whee!") + + +if __name__ == "__main__": + say_whee() diff --git a/decorators/decorate a class.py b/decorators/decorate a class.py new file mode 100644 index 0000000000000000000000000000000000000000..0e0adbd17e263af53d2a31aef9e317989b326db2 --- /dev/null +++ b/decorators/decorate a class.py @@ -0,0 +1,26 @@ +class capitalize: + def __init__(self, cls): + self.cls = cls + + # accept the class's __init__ method arguments + def __call__(self, name: str, surname: str): + + def capitalize_print(self): + print(f"name: {self.name.capitalize()}, surname: {self.surname.capitalize()}") + + self.cls.print_person = capitalize_print + + return self.cls(name, surname) + + +@capitalize +class Person: + def __init__(self, name, surname): + self.name = name + self.surname = surname + + def print_person(self): + pass + +person = Person("todd", "goofill") +person.print_person() diff --git a/decorators/decorator with params.py b/decorators/decorator with params.py new file mode 100644 index 0000000000000000000000000000000000000000..101f43b534c12b6b06f3b286bca71e4ab83b5840 --- /dev/null +++ b/decorators/decorator with params.py @@ -0,0 +1,29 @@ +from functools import wraps +import time +from typing import Any, Callable + + +def delay(sec: int) -> Any: + def func_decorator(func: Callable) -> Any: + @wraps(func) + def wrapper(*args: tuple, **kwargs: dict) -> Any: + print(f"Wait for {sec} sec") + time.sleep(sec) + print(f"{sec} sec delay is over") + return func(*args, **kwargs) + + return wrapper + + return func_decorator + + +# You can apply several decorators to a function by stacking them on top of each other +@delay(3) # first +@delay(2) # second +def fun() -> None: + """Some function""" + print("Some function") + + +if __name__ == "__main__": + fun() diff --git a/decorators/decorators.py b/decorators/decorators.py new file mode 100644 index 0000000000000000000000000000000000000000..0d02457287db0fb9ccbbab8955b673af64952c35 --- /dev/null +++ b/decorators/decorators.py @@ -0,0 +1,307 @@ +from typing import Callable, Any + +registry = [] + + +def decorator1(func: Callable) -> Callable: + print(f'Decorate({func})') + + def wrapper(*args, **kwargs) -> Any: # asd + print(f"Wrap {func}") + print(args, kwargs) + func() + return wrapper + +@decorator1 +def say_whee(a = 123) -> None: + print(a) + +say_whee = decorator1(say_whee) + + + +def decorator(func: Callable) -> Callable: + print(f'decorate({func})') + return func + +@decorator +def f1() -> None: + print('running f1()') + +def register(func: Callable) -> Callable: + print(f'running register({func})') + registry.append(func) + return func + + +@register +def f1() -> None: + print('running f1()') + + +@register +def f2() -> None: + print('running f2()') + + +def f3() -> None: + print('running f3()') + + +def main() -> None: + print('running main()') + print('registry ->', registry) + f1() + f2() + f3() + + +if __name__ == '__main__': + say_whee() + +def make_averager(): count = 0 +total = 0 +def averager(new_value): nonlocal count, total count += 1 +total += new_value return total / count +return averager + +To work around this, the nonlocal keyword was introduced in Python 3. It lets you declare a variable as a free variable even when it is assigned within the function. If a new value is assigned to a nonlocal variable, the binding stored in the closure is changed. + +import time +def clock(func): +def clocked(*args): +t0 = time.perf_counter() +result = func(*args) +elapsed = time.perf_counter() - t0 +name = func.__name__ +arg_str = ', '.join(repr(arg) for arg in args) print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') return result +return clocked + +it does not support keyword arguments, and it masks the __name__ and __doc__ of the deco‐ rated function. Example 9-16 uses the functools.wraps decorator to copy the rele‐ vant attributes from func to clocked. + + +def attrs(**kwds: Any) -> Callable: + def decorate(f: Callable) -> Any: + for k in kwds: + setattr(f, k, kwds[k]) + return f + return decorate + + +@attrs(versionadded="2.2", + author="Guido van Rossum") +def mymethod() -> None: + a = getattr(f, "author") + print(a) + + +def checked(cls: type) -> type: + for name, constructor in _fields(cls).items(): + setattr(cls, name, Field(name, constructor)) + +cls._fields = classmethod(_fields) # type: ignore +instance_methods = ( + __init__, + __repr__, + __setattr__, + _asdict, + __flag_unknown_attrs, +) + + +for method in instance_methods: + setattr(cls, method.__name__, method) + return cls +from functools import wraps, singledispatch + + +def decorator_with_params(param: str) -> Any: + def func_decorator(func: Callable) -> Any: + @wraps(func) + def wrapper(*args: list, **kwargs: dict) -> Any: + print(f"decorator params {param}") + return func(*args, **kwargs) + return wrapper + return func_decorator + +@decorator_with_params("params") +def fun() -> None: + """ Some function """ + print("Hello") + +@singledispatch +def dispatch_types(args: Any) -> str: + return f"args: {args}" +@dispatch_types.register +def _(text: str) -> str: + return f"str: {text}" +@dispatch_types.register +def _(val_list: list) -> str: + return f"list: {val_list}" +@dispatch_types.register +def _(integer: int) -> str: + return f"int: {integer}" +import time +import functools +def clock(func): + @functools.wraps(func) + def clocked(*args, **kwargs): + t0 = time.perf_counter() + result = func(*args, **kwargs) + elapsed = time.perf_counter() - t0 + name = func.__name__ + arg_lst = [repr(arg) for arg in args] + arg_lst.extend(f'{k}={v!r}'for k, v in kwargs.items()) + arg_str = ', '.join(arg_lst) + print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') + return result + return clocked +import functools +@functools.cache +@clock +def fibonacci(n): + if n<2: + return n + return fibonacci(n - 2) + fibonacci(n - 1) + +class decorator: + def __init__(self, param: Any) -> None: # + self.param = param + + def __call__(self, func: Callable) -> Any: + @wraps(func) + def wrapper(*_args: Any) -> Any: + print(self.param) + print("Class decorator") + return func(*_args) + return wrapper + +@decorator("params") +def foo(): + print("hello") + +class Decorator: + + def __init__(self, function: Callable) -> None: + self.function = function + + def __call__(self, *args: list, **kwargs: dict) -> Callable: + print("class decorator") + return self.function(*args, **kwargs) + +@Decorator +def get_square() -> None: + print("hello") + + +def function_details(func): + # Getting the argument names of the called function + argnames = func.__code__.co_varnames[:func.__code__.co_argcount] + # Getting the Function name of the called function + fname = func.__name__ + + def inner_func(*args, **kwargs): + print(fname, "(", end="") + + # printing the function arguments + print(', '.join('% s = % r' % entry + for entry in zip(argnames, args[:len(argnames)])), end=", ") + + # Printing the variable length Arguments + print("args =", list(args[len(argnames):]), end=", ") + + # Printing the variable length keyword + # arguments + print("kwargs =", kwargs, end="") + print(")") + + return inner_func + +@function_details +def GFG(a, b=1, *args, **kwargs): + pass + +a = GFG(11, 2, 2,3, a = 32) + + +from functools import wraps +import time +from typing import Any, Callable +def delay(sec: int) -> Any: + def func_decorator(func: Callable) -> Any: + @wraps(func) # Update a wrapper function to look like the wrapped function. More about wraps in "Decorators in the Standard Library" block + def wrapper() -> Any: + print(f"Wait for {sec} sec") + time.sleep(sec) + print(f"{sec} sec delay is over") + return func() + return wrapper + return func_decorator + +# @delay(2) +# @delay(2) # equivalent to fun = delay("params")(fun) +def fun() -> None: + """ Some function """ + print("Hello") + + +class Mydecorator: + # accept the class as argument + def __init__(self, student): + self.student = student + + # accept the class's __init__ method arguments + def __call__(self, name): + # define a new display method + def newdisplay(self): + print('Name: ', self.name) + print('Subject: Python') + + # replace display with newdisplay + self.student.display = newdisplay + + # return the instance of the class + obj = self.student(name) + return obj + + +@Mydecorator +class Student: + def __init__(self, name): + self.name = name + + def display(self): + print('Name: ', self.name) + + +obj = Student('Pencil Programmer') +obj.display() + + +class Mydecorator: + def __init__(self, cls): + self.cls = cls + + # accept the class's __init__ method arguments + def __call__(self, name: str, surname: str): + new_name = "JOHN" + + def capitalize_print(self): + print(f"name: {self.name.capitalize()}, surname: {self.surname.capitalize()}") + + self.cls.print_person = capitalize_print + + return self.cls(new_name, surname) + + +@Mydecorator +class person: + def __init__(self, name, surname): + self.name = name + self.surname = surname + + def print_person(self): + pass + +obj = person("todd", "goofill") +obj.print_person() \ No newline at end of file diff --git a/decorators/function details.py b/decorators/function details.py new file mode 100644 index 0000000000000000000000000000000000000000..f847c568f1ee2b17b9583722a8b2aa1be211d0c9 --- /dev/null +++ b/decorators/function details.py @@ -0,0 +1,66 @@ +import inspect +from functools import wraps + + +def handle_kwargs(args_dict, kwargs): + for k, v in kwargs.items(): + if k in args_dict.keys(): + args_dict[k] = v + for k in args_dict.keys(): + kwargs.pop(k, None) + + +def function_details(func): + + fname = func.__name__ + + # getting positional arguments names of the function + argnames = func.__code__.co_varnames[: func.__code__.co_argcount] + + default_args = { + k: v.default + for k, v in inspect.signature(func).parameters.items() + if v.default is not inspect.Parameter.empty + } + + @wraps(func) + def inner_func(*args: tuple, **kwargs: dict): + print(fname + "(", end="") + + # printing positional function arguments + positional_args = { + entry[0]: entry[1] for entry in zip(argnames, args[: len(argnames)]) + } + + # extra logic to handle default arguments + if len(positional_args) < len(argnames): + handle_kwargs(default_args, kwargs) + positional_args |= default_args + + # handle possible reassignment of positional arguments as kwargs + # but that is really bad practice to do such things + handle_kwargs(positional_args, kwargs) + + print( + ", ".join(f"{k} = {v}" for k, v in positional_args.items()), + end=", ", + ) + + print("args =", list(args[len(argnames) :]), end=", ") + print("kwargs =", kwargs, end="; ") + print("return =", func(*args, **kwargs), end=")\n") + + return inner_func + + +@function_details +def func(a, b=1, *args, **kwargs): + return 42 + + +if __name__ == "__main__": + func(1, 2, 3, 4, k=42) + func(1, 2, k=42) + func(1, k=42) + func(1, b=2, k=42, k1=42) + func(11, 2, 2, 3, a=32, c=231) diff --git a/decorators/sample decorator.py b/decorators/sample decorator.py new file mode 100644 index 0000000000000000000000000000000000000000..8acd286971ceda7b35cb42bb77ec6d43850b741f --- /dev/null +++ b/decorators/sample decorator.py @@ -0,0 +1,25 @@ +from functools import wraps +from typing import Callable + + +def sample_decorator(func: Callable) -> Callable: + print("This message wil appear on initialization stage.") + + @wraps(func) + def wrapper(): + """We need additional wrapper function inside + because all decorator functions are called during initialization. + So decorator is initialized, but wrapper function + will be saved and called only when 'func' is called. + """ + print(f"Before {func.__name__} called") + func() + print(f"After {func.__name__} called") + + return wrapper + + +# sample_decorator will be executed exactly after sample_func initialization. +@sample_decorator # Equal to sample_func = sample_decorator(sample_func) +def sample_func() -> None: + print("Calling sample_func()") diff --git a/generator/example b/generator/example new file mode 100644 index 0000000000000000000000000000000000000000..b2bfc32a91b327f0a874f1b454eae8a7f6b9b44d --- /dev/null +++ b/generator/example @@ -0,0 +1 @@ +asdasdjiluagfjaspasdhpwqhaqadhqdhfkadhflafhfphfdhaldjhvpaiuwhfahsdfihwkhasdhffadhfhadhffhcdhcapqdfncasdasdjiluagfjaspasdhpwqhaqadhqdhfkadhflafhfphfdhaldjhvpaiuwhfahsdfihwkhasdhffadhfhadhffhcdhcapqdfncadasd \ No newline at end of file diff --git a/generator/generator.py b/generator/generator.py new file mode 100644 index 0000000000000000000000000000000000000000..a421db9fd7e7c863d06e73912183fea4519fbef0 --- /dev/null +++ b/generator/generator.py @@ -0,0 +1,97 @@ +from typing import Generator, Any +from contextlib import contextmanager + + +def read_chunks() -> Generator: + file = "./example" + with open(file, 'rb') as f: # read chunk from file + for chunk in iter(lambda: f.read(50), b''): # Read chunk with generator + yield chunk + + +@contextmanager +def checker() -> Generator: # generator as error catcher + try: + yield + except ZeroDivisionError: + raise Exception("ad") + except TypeError: + raise Exception("qwe") + except AttributeError: + raise Exception("asdzxcz") + + +def error_raiser(err: Any) -> None: + with checker(): # error catcher + # come code that raise error + raise err + + +# cool example that shows how to display the hierarchy of all subclasses +def tree(cls: Any, level: int = 0) -> Generator: + yield cls.__name__, level # yield class name and level in hierarchy + for sub_cls in cls.__subclasses__(): # move to subclasses + yield from tree(sub_cls, level + 1) # yield from all subclasses + + +def display(cls: Any) -> None: + for cls_name, level in tree(cls): # for to initialize generator + indent = ' ' * 4 * level + print(f'{indent}{cls_name}') + + +def sub_coroutine1() -> Generator[str, Any, None]: + print("start 1 sub coroutine") + launched_times = 0 + val = "" + while val != "stop": + launched_times += 1 + val = yield f"generator 1 launched {launched_times} times " + print(f"{val} from 1 coroutine") + + +def sub_coroutine2() -> Generator[str, Any, None]: + print("start 2 sub coroutine") + launched_times = 0 + val = "" + while val != "stop": + launched_times += 1 + val = yield f"generator 2 launched {launched_times} times " + print(f"{val} from 2 coroutine") + + +def base_generator() -> Generator[str, Any, None]: + print("start base coroutine") + while True: + val = yield "Enter 1 or 2 to run 1 or 2 coroutine" + if val == 1: + yield from sub_coroutine1() + elif val == 2: + yield from sub_coroutine2() + else: + break + print("Done") + return "Generator return value as StopIteration error value" + + +if __name__ == '__main__': + a = read_chunks() + for i in a: + print(i) + error_raiser(ZeroDivisionError) + error_raiser(TypeError) + display(BaseException) + try: + gen = base_generator() + next(gen) + gen.send(1) + gen.send("run 1") + gen.send(2) + gen.send("stop") + gen.send(2) + gen.send("hello") + gen.send("run 2") + gen.send("stop") + gen.send("3") + except Exception as e: + print(e.value) \ No newline at end of file diff --git a/iterator/example b/iterator/example new file mode 100644 index 0000000000000000000000000000000000000000..b2bfc32a91b327f0a874f1b454eae8a7f6b9b44d --- /dev/null +++ b/iterator/example @@ -0,0 +1 @@ +asdasdjiluagfjaspasdhpwqhaqadhqdhfkadhflafhfphfdhaldjhvpaiuwhfahsdfihwkhasdhffadhfhadhffhcdhcapqdfncasdasdjiluagfjaspasdhpwqhaqadhqdhfkadhflafhfphfdhaldjhvpaiuwhfahsdfihwkhasdhffadhfhadhffhcdhcapqdfncadasd \ No newline at end of file diff --git a/iterator/iterator.py b/iterator/iterator.py new file mode 100644 index 0000000000000000000000000000000000000000..4131517d9433ccc50da95ad4bc59283935b6a3d3 --- /dev/null +++ b/iterator/iterator.py @@ -0,0 +1,71 @@ +from collections.abc import Iterator +from random import randint + + +class IteratorExample: + def __init__(self, words: str) -> None: + self.words = words.split(" ") + self.index = 0 + + def __next__(self) -> str: + try: + word = self.words[self.index] + except IndexError: + raise StopIteration() + self.index += 1 + return word + + def __iter__(self) -> Iterator: + return self + + +class IterableExample: + def __init__(self, words: str) -> None: + self.words = words.split(" ") + + def __iter__(self) -> Iterator[str]: + return iter(self.words) + + +class IterableExample_getitem: + def __init__(self, words: str) -> None: + self.words = words.split(" ") + + def __getitem__(self, item: int) -> str: + return self.words[item] + + +def callable_f() -> int: + value = randint(1, 6) + print(f"value: {value}") + return value + + +def iter_example() -> None: + it = iter(callable_f, 1) # transform callable to iterator + for val in it: + print(val) + + +def read_chunks() -> None: + file = "./example" + with open(file, "rb") as f: # read chunk from file + for chunk in iter( + lambda: f.read(50), b"" + ): # lambda return callable, iter create iterator and raise StopIteration if b"" (EOF) + print(chunk) + + +if __name__ == "__main__": + it = IteratorExample("Hello world! How are you?") + next(it) # in case that we use same iterator next() will skip first value + for i in it: # in python "for .. in" works like "while True: next()" + print(i) + print("Iter with callable") + iter_example() + print("Equal to iter_example but with lambda") + it = iter(lambda: randint(1, 6), 1) # lambda also could be used as callable + for i in it: + print(i) + print("Read chunks from file with iter") + read_chunks()