WHY YOU DON'T NEED DESIGN PATTERNS IN PYTHON? EuroPython 2017 - - PowerPoint PPT Presentation

why you don t need design patterns in python
SMART_READER_LITE
LIVE PREVIEW

WHY YOU DON'T NEED DESIGN PATTERNS IN PYTHON? EuroPython 2017 - - PowerPoint PPT Presentation

WHY YOU DON'T NEED DESIGN PATTERNS IN PYTHON? EuroPython 2017 EVERYTHING STARTS WITH A STORY... STORY OF A PYTHON DEVELOPER Zen of Python! TDD FOR THE Readability WIN!!! first! Thousands+ lines of code and then, one project changed


slide-1
SLIDE 1

WHY YOU DON'T NEED DESIGN PATTERNS IN PYTHON?

EuroPython 2017

slide-2
SLIDE 2

EVERYTHING STARTS WITH A STORY...

slide-3
SLIDE 3

STORY OF A PYTHON DEVELOPER

Thousands+ lines of code

TDD FOR THE WIN!!! Readability first! Zen

  • f Python!
slide-4
SLIDE 4

and then, one project changed everything

slide-5
SLIDE 5

Weight of a project outside framework

slide-6
SLIDE 6

FRAMEWORKS ARE SETS OF BUILDING BLOCKS

slide-7
SLIDE 7

THEY WORK FINE FOR A SPECIFIC RANGE OF PROBLEMS

slide-8
SLIDE 8
slide-9
SLIDE 9
slide-10
SLIDE 10
slide-11
SLIDE 11

Design pattern ...general reusable solution to a commonly

  • ccurring problem...

...formalized best practices...

slide-12
SLIDE 12

SINGLETON

Only one! clear way to get an instance

slide-13
SLIDE 13

SINGLETON - __NEW__

class Singleton: _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance

slide-14
SLIDE 14

SINGLETON - __NEW__

class Singleton: _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance

  • ne_instance = Singleton()

another_instance = Singleton()

  • ne_instance is another_instance # True
slide-15
SLIDE 15

SINGLETON - @CLASSMETHOD

class Singleton: _instance = None @classmethod def get_instance(cls): if not cls._instance: cls._instance = cls() return cls._instance

  • ne_instance = Singleton.get_instance()

another_instance = Singleton()

  • ne_instance is another_instance # False
slide-16
SLIDE 16

THERE IS A SIMPLER WAY...

class Singleton: pass singleton = Singleton() # another modules from my_code import singleton

slide-17
SLIDE 17

SINGLETONS IN PYTHON?

MODULES!

Exactly one instance living in sys.modules Get an instance easily import module Recreate using importlib.reload(module) #Py3

slide-18
SLIDE 18

SINGLETON - CONCLUSION

Using a module may be better than creating class

slide-19
SLIDE 19

SPEAKING OF MODULES - FAÇADE

slide-20
SLIDE 20

SPEAKING OF MODULES - FAÇADE

slide-21
SLIDE 21

SPEAKING OF MODULES - FAÇADE

slide-22
SLIDE 22

FAÇADE - CLASS

class AdvertisementsFacade: @classmethod def get_advert_for_single_post(post): pass @classmethod def get_adverts_for_main_page(count): pass

slide-23
SLIDE 23

FAÇADE - MODULE

def get_advert_for_single_post(post): pass def get_adverts_for_main_page(count): pass # in another module import advertisements adverts = advertisements.get_adverts_for_main_page(count=3)

slide-24
SLIDE 24

FAÇADE - CONCLUSION

Helpful to organize code, no need for a class

slide-25
SLIDE 25

COMMAND

Object oriented callback

class Client: def foo(self): some_obj = SomeClass() command = Command(some_obj) self.menu_item.set_command(command) # later in menu_item code self.command.execute() # menu_item doesn't know anything

slide-26
SLIDE 26

COMMAND - CLASS

class Command: ... def execute(discount_rate): self.object.notify_users_about_discount(discount_rate)

slide-27
SLIDE 27

COMMAND - FUNCTION

def command(discount_rate): some_obj.notify_users_about_discount()

  • r even simpler using standard library's goodies:

import functools command = functools.partial( some_obj.notify_users_about_discount, discount_rate=0.5 ) command() # equals to some_obj.notify_users_about_discount(discount_rate=0.5)

slide-28
SLIDE 28

COMMAND - CONCLUSION

With classes makes a little sense in Python

slide-29
SLIDE 29

VISITOR PATTERN

Let's say we have a complicated, nested data structure to parse.

slide-30
SLIDE 30

VISITOR - EXAMPLE

ASTs

import time def ten_seconds_ago(): now = time.time() return now - 10

slide-31
SLIDE 31

VISITOR IMPLEMENTATION - JAVA

public class ASTVisitor { public void visit(Import import) {} public void visit(FunctionDef functionDef) {} public void visit(Assign assign) {} }

slide-32
SLIDE 32

PYTHON NAIVE IMPLEMENTATION

class ASTVisitor: def visit(node): if type(node) == Import: self.visit_import() elif type(node) == FunctionDef: self.visit_functiondef() elif type(node) == Assign: self.visit_assign() else: raise AttributeError

slide-33
SLIDE 33

PYTHON BETTER IMPLEMENTATION

class ASTVisitor: def visit(node): normalized_type_name = type(node).__name__.lower() # 'assign' method_name = '_visit_' + normalized_type_name # '_visit_assign' method = getattr(self, method_name) method()

This example comes from Python Cookbook 3rd edition

slide-34
SLIDE 34

PYTHON WITH @SINGLEDISPATCH

from functools import singledispatch @singledispatch def visit(node): type_name = type(node).__name__ raise AttributeError(f'No handler found for {type_name}') from ast_nodes import Assign, FunctionDef @visit.register(Assign) def visit(node): pass @visit.register(FunctionDef) def visit(node): pass

Can't be used in classes :(

slide-35
SLIDE 35

DECORATOR

Decorator pattern != @decorator functions in Python Extend behaviour of a given object Possible during runtime Multiple times, with different decorators and order

slide-36
SLIDE 36

DECORATOR - EXAMPLE

assert hasattr(original_object, 'anyattr') decorated_object = Decorator(original_object) assert hasattr(decorated_object, 'anyattr') assert type(original_object) != type(decorated_object)

Different types, but hey - duck typing

slide-37
SLIDE 37

DECORATOR - EXAMPLE 2

class OriginalClass: def get_text(self): pass def get_number(self): pass

We have to reimplement all methods

class Decorator: def __init__(self, decorated_obj): self.decorated_obj = decorated_obj def get_text(self): return f'<b>{self.decorated_obj.get_text()}</b>' def get_number(self): return self.decorated_obj.get_number()

slide-38
SLIDE 38

WAIT A SEC... WHAT HAPPENS IF I REQUEST AN ATTRIBUTE?

some_object.some_method #<bound method SomeClass.some_method of <SomeClass object at 0x0>>

Methods are just attributes Firstly, Python calls a special __getattribute__ If no attribute was found, __getattr__ is called. By default it just throws an exception

slide-39
SLIDE 39

WHAT HAPPENS IF I REQUEST AN ATTRIBUTE? - __DICT__

class ClsVsObject: some_attr = 1 def __init__(self): self.some_attr = 2 example = ClsVsObject() example.__dict__['some_attr'] # 2, == example.some_attr example.__class__.__dict__['some_attr'] # 1 == ClsVsObject.some_attr example.some_attr # 2 ClsVsObject.some_attr # 1

slide-40
SLIDE 40

DECORATOR - IMPLEMENTATION

class Decorator: def __init__(self, decorated_obj): self.decorated_obj = decorated_obj def get_text(self): return f'{self.decorated_obj.get_text()}' def __getattr__(self, attr_name): return getattr(self.decorated_obj, attr_name)

To get a full compatiblity, add other methods: __setattr__, __delattr__ and so on.

slide-41
SLIDE 41

SUMMARY

Python is a very flexible tool

slide-42
SLIDE 42

IS MAGIC WORTH THE EFFORT?

slide-43
SLIDE 43

SUMMARY (THIS TIME FOR REAL)

know well your tools (Python!) get inspiration from other languages and communities know a business domain of your project

slide-44
SLIDE 44

SEBASTIAN BUCZYŃSKI

Working for Blogging under Twitter: STX Next breadcrumbscollector.tech EnforcerPL