$30 off During Our Annual Pro Sale. View Details »

Python: Decorating Your Code

Python: Decorating Your Code

Deriving decorators, presented at a Pymug Meetup

Abdur-Rahmaan Janhangeer

March 31, 2019
Tweet

More Decks by Abdur-Rahmaan Janhangeer

Other Decks in Programming

Transcript

  1. Decorating Your Code
    by Abdur-Rahmaan Janhangeer @appinv

    View Slide

  2. pymug
    Python Mauritius User-Group
    pymug.com
    [email protected]
    github.com/pymug

    View Slide

  3. View Slide

  4. @decorators: the feared simpleton

    View Slide

  5. decorator example
    @app.route('/home')
    def index_page():
    pass

    View Slide

  6. Some points to note:
    1) Functions can be nested

    View Slide

  7. Functions can be nested [1]
    functions can be defined within functions
    def calc(a, b):
    def add(a, b):
    return a + b
    return add(a, b) * 2
    print(calc(2, 2))

    View Slide

  8. Functions can be nested [2]
    functions can be defined within functions multiple times
    def calc(a, b):
    def add(a, b):
    return a + b
    def minus(a, b):
    return a - b
    return add(a, b) * 2
    print(calc(2, 2))
    #8

    View Slide

  9. Functions can be nested [3]
    functions can be defined within functions at multiple levels
    def do_this():
    def calc(a, b):
    def add(a, b):
    return a+b
    return add(a, b) * 2
    return calc(1, 3)
    print(do_this())
    # 8

    View Slide

  10. Some points to note:
    2) Functions can take functions as
    arguments

    View Slide

  11. Functions can take functions as arguments [1]
    a normal function
    def print_these():
    print('----')
    print('....')
    print('----')

    View Slide

  12. Functions can take functions as arguments [2]
    def print_these():
    print('----')
    print('....')
    print('----')
    calling / executing it
    print_these()
    the ( ) calls the function

    View Slide

  13. Functions can take functions as arguments [3]
    implementing a fuction to execute other functions. it actually calls
    the function we pass in as argument
    def execute(f):
    f()
    applying
    def print_these():
    print('----')
    print('....')
    print('----')
    execute(print_these) # same as print_these()
    # ----
    # ....
    # ----

    View Slide

  14. Functions can take functions as arguments [4]
    we can also retrieve values
    def name():
    return 'moris'
    def view_value(v):
    print('the value is', v())
    view_value(name)
    # the value is moris

    View Slide

  15. Some points to note:
    3) Functions can return functions

    View Slide

  16. Functions can return functions [1]
    returning a fuction:
    def x():
    def y():
    print(3)
    return y
    calling
    x()()
    # 3

    View Slide

  17. Functions can return functions [2]
    to avoid this (bit ugly)
    x()()
    we do
    func = x()
    func()

    View Slide

  18. Functions can return functions [3]
    example of use
    def welcome_message():
    def first_part():
    return '------'
    def last_part():
    return '******'
    def body():
    return 'welcome to our program'
    def main():
    print(first_part())
    print(body())
    print(last_part())
    return main
    w = welcome_message()
    w()

    View Slide

  19. Functions can return functions [4]
    prints out
    # ------
    # welcome to our program
    # ******

    View Slide

  20. Some points to note:
    4) Functions can take functions as
    arguments

    View Slide

  21. Getting arguments passed: positional [1]
    we can get all arguments passed using *args
    def s(*args):
    return args
    print(s(1, 2, 3))
    print(s(1, 2))
    print(s(1))
    # (1, 2, 3)
    # (1, 2)
    # (1,)

    View Slide

  22. Getting arguments passed: positional [2]
    but we can change *args to anything like *canne
    def s(*canne):
    return canne
    print(s(1, 2, 3))
    print(s(1, 2))
    print(s(1))
    # (1, 2, 3)
    # (1, 2)
    # (1,)

    View Slide

  23. Getting arguments passed: positional [3]
    can be useful in the case of
    def add(*nums):
    return sum(nums)
    print(add(1, 2, 3, 4, 5))
    print(add(100, 400, 1000))
    # 15
    # 1500

    View Slide

  24. Getting arguments passed: keyword [1]
    kwargs allows us to get all keyword arguments passed
    def s(**kwargs):
    return kwargs
    print(s(name='me', age=5, country='mauritius'))
    # {'name': 'me', 'age': 5, 'country': 'mauritius'}

    View Slide

  25. Getting arguments passed: keyword [2]
    as with *args we can change the name to **keyword_arguments
    def s(**keyword_arguments):
    return keyword_arguments
    print(s(name='me', age=5, country='mauritius'))
    # {'name': 'me', 'age': 5, 'country': 'mauritius'}

    View Slide

  26. We can also mix them
    def view_args(*args, **keyw_args):
    print(args)
    print(keyw_args)
    view_args(1, 2, 3, name='me', town='pl')
    # (1, 2, 3)
    # {'name': 'me', 'town': 'pl'}

    View Slide

  27. Some points to note:
    4) Functions can be reassigned names

    View Slide

  28. Functions can be reassigned names [1]
    we can change function names by reassignment
    def add(x, y):
    return x + y
    addition = add
    print(addition(2, 3))
    # 5

    View Slide

  29. Functions can be reassigned names [2]
    still works if we delete original
    def add(x, y):
    return x + y
    addition = add
    del add
    print(addition(2, 3))
    # 5

    View Slide

  30. In enters the skeleton

    View Slide

  31. In enters the skeleton
    let us take this piece of code
    def quote(text):
    return '<<{}>>'.format(text)
    def indent(text):
    return '> {}'.format(text)
    print(indent(quote('abc')))
    # > <>
    #
    # quote('abc') -> '<>'
    # indent(quote('abc')) -> '> <>'

    View Slide

  32. In enters the skeleton
    we can also write it as
    def quote(text):
    return '<<{}>>'.format(text)
    def indent(q):
    def dummy(text):
    return '> {}'.format(
    q(text)
    )
    return dummy
    print(
    indent(quote)('i am here')
    )
    # > <>

    View Slide

  33. In enters the skeleton
    which is equivalent to:
    def indent(q):
    def dummy(text):
    return '> {}'.format(
    q(text)
    )
    return dummy
    @indent
    def quote(text):
    return '<<{}>>'.format(text)
    print(quote('the sun is rising'))
    neater

    View Slide

  34. Chaining decorators

    View Slide

  35. Chaining operators [1]
    let's say we want to get
    # ----
    # > <>
    # ----
    we just add another function
    def enclose(f):
    def dummy(text):
    return '----\n{}\n----'.format(
    f(text)
    )
    return dummy
    and just call
    @enclose

    View Slide

  36. Chaining operators [2]
    def enclose(f):
    def dummy(text):
    return '----\n{}\n----'.format(
    f(text)
    )
    return dummy
    def indent(q):
    def dummy(text):
    return '> {}'.format(
    q(text)
    )
    return dummy
    @enclose
    @indent
    def quote(text):
    return '<<{}>>'.format(text)
    print(quote('the sun is rising'))

    View Slide

  37. Chaining operators [3]
    ----
    > <>
    ----

    View Slide

  38. Adding arguments to decorators
    def awesome_f(dec_param):
    def awesome_f_decorator(f):
    # wrap here
    def awesome_f_wrapper(p):
    # dec_param f(p)
    return awesome_f_wrapper
    return awesome_f_decorator
    @awesome_f('abcd')
    def some_func():
    # ...

    View Slide

  39. bulletproofing our decorators

    View Slide

  40. bulletproofing our decorators [1]
    accept all arguments
    def indent(q):
    def dummy(*args, **kwargs):
    return '> {}'.format(
    q(*args, **kwargs)
    )
    return dummy

    View Slide

  41. bulletproofing our decorators [1]
    preserve info
    from functools import wraps
    # ...
    def indent(q):
    @wraps(func)
    def dummy(*args, **kwargs):
    return '> {}'.format(
    q(*args, **kwargs)
    )
    return dummy

    View Slide

  42. where decorators are used

    View Slide

  43. @staticmethod

    View Slide

  44. where decorators are used: @staticmethod [1]
    let's take a simple class
    import math
    class Calcs:
    def add(self, x, y):
    return x + y
    def hypotenuse(self, x, y):
    return math.sqrt((x**2) + (y**2))
    c = Calcs()
    print(c.hypotenuse(3, 4))
    # 5.0
    functions not related together

    View Slide

  45. where decorators are used: @staticmethod [2]
    adding @staticmethod
    import math
    class Calcs:
    @staticmethod
    def add(x, y):
    return x + y
    def hypotenuse(self, x, y):
    return math.sqrt((x**2) + (y**2))
    c = Calcs()
    print(c.add(1, 2))
    # 3
    isolated from class, using another method/var results in error

    View Slide

  46. where decorators are used: @staticmethod [3]
    use of @staticmethod
    isolate function
    group related functions under a name space
    visually telling purpose of function

    View Slide

  47. @classmethod

    View Slide

  48. where decorators are used: @classmethod [1]
    syntax
    @classmethod
    def method_name(var_holding_classs, argument ...

    View Slide

  49. where decorators are used: @classmethod [2]
    demo
    import math
    class Person:
    @classmethod
    def say_hi(cls, name):
    return 'hi ' + name
    print(Person.say_hi('doe'))
    # hi doe

    View Slide

  50. @property

    View Slide

  51. where decorators are used: @property [1]
    another way of customising getters, setters and deleters
    class Car:
    def __init__(self):
    self._wheel = None
    @property
    def wheel(self):
    return self._wheel
    @wheel.setter
    def wheel(self, number):
    self._wheel = number
    @wheel.getter
    def wheel(self):
    return self._wheel
    @wheel.deleter
    def wheel(self):
    del self._wheel

    View Slide

  52. nissan = Car()
    nissan.wheel = 4
    print(nissan.wheel)
    #4
    but if getter changed
    @wheel.getter
    def wheel(self):
    return self._wheel + 1
    and printed
    nissan = Car()
    nissan.wheel = 4
    print(nissan.wheel)
    we'd get 5

    View Slide

  53. where decorators are used: @property [2]
    same as __set__ , __get__ and __del__

    View Slide

  54. yeah, they are all functions

    View Slide

  55. yeah, they are all functions
    @property , @staticmethod , @classmethod are all functions, in-built
    ones. can be used as property() , staticmethod() and
    classmethod .
    try help on them
    print(help(property))

    View Slide

  56. the end

    View Slide