Slide 1

Slide 1 text

Decorating Your Code by Abdur-Rahmaan Janhangeer @appinv

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

@decorators: the feared simpleton

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Some points to note: 1) Functions can be nested

Slide 7

Slide 7 text

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))

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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() # ---- # .... # ----

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Some points to note: 3) Functions can return functions

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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()

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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,)

Slide 22

Slide 22 text

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,)

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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'}

Slide 25

Slide 25 text

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'}

Slide 26

Slide 26 text

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'}

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

In enters the skeleton

Slide 31

Slide 31 text

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')) -> '> <>'

Slide 32

Slide 32 text

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') ) # > <>

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Chaining decorators

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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'))

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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(): # ...

Slide 39

Slide 39 text

bulletproofing our decorators

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

where decorators are used

Slide 43

Slide 43 text

@staticmethod

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

@classmethod

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

@property

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

yeah, they are all functions

Slide 55

Slide 55 text

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))

Slide 56

Slide 56 text

the end