44
WHY YOU DON'T NEED DESIGN PATTERNS IN PYTHON? EuroPython 2017

W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

WHY YOU DON'T NEED DESIGNPATTERNS IN PYTHON?

EuroPython 2017

Page 2: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

EVERYTHING STARTS WITH A STORY...

Page 3: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

STORY OF A PYTHON DEVELOPER

Thousands+ lines of code

TDD FOR THE

WIN!!!

  Readability

first!

Zen of Python!

Page 4: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

and then, one project changed everything

Page 5: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

Weight of a project outside framework

Page 6: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

FRAMEWORKS ARE SETS OF BUILDINGBLOCKS

Page 7: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

THEY WORK FINE FOR A SPECIFIC RANGE OFPROBLEMS

Page 8: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #
Page 9: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #
Page 10: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #
Page 11: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

Design pattern

...general reusable solution to a commonlyoccurring problem...

...formalized best practices...

Page 12: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SINGLETONOnly one!clear way to get an instance

Page 13: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

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

Page 14: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

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

one_instance = Singleton() another_instance = Singleton()

one_instance is another_instance # True

Page 15: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SINGLETON - @CLASSMETHODclass Singleton: _instance = None

@classmethod def get_instance(cls): if not cls._instance: cls._instance = cls()

return cls._instance

one_instance = Singleton.get_instance() another_instance = Singleton()

one_instance is another_instance # False

Page 16: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

THERE IS A SIMPLER WAY...class Singleton: pass

singleton = Singleton()

# another modules from my_code import singleton

Page 17: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SINGLETONS IN PYTHON?

MODULES!Exactly one instance living in sys.modulesGet an instance easily import moduleRecreate using importlib.reload(module) #Py3

Page 18: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SINGLETON - CONCLUSIONUsing a module may be better than creating class

Page 19: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SPEAKING OF MODULES - FAÇADE

Page 20: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SPEAKING OF MODULES - FAÇADE

Page 21: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SPEAKING OF MODULES - FAÇADE

Page 22: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

FAÇADE - CLASSclass AdvertisementsFacade:

@classmethod def get_advert_for_single_post(post): pass

@classmethod def get_adverts_for_main_page(count): pass

Page 23: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

FAÇADE - MODULEdef 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)

Page 24: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

FAÇADE - CONCLUSIONHelpful to organize code, no need for a class

Page 25: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

COMMANDObject 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

Page 26: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

COMMAND - CLASSclass Command: ...

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

Page 27: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

COMMAND - FUNCTIONdef command(discount_rate): some_obj.notify_users_about_discount()

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

Page 28: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

COMMAND - CONCLUSIONWith classes makes a little sense in Python

Page 29: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

VISITOR PATTERNLet's say we have a complicated, nested data structure to

parse.

Page 30: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

VISITOR - EXAMPLEASTs

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

Page 31: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

VISITOR IMPLEMENTATION - JAVApublic class ASTVisitor { public void visit(Import import) {}

public void visit(FunctionDef functionDef) {}

public void visit(Assign assign) {} }

Page 32: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

PYTHON NAIVE IMPLEMENTATIONclass 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

Page 33: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

PYTHON BETTER IMPLEMENTATIONclass 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

Page 34: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

PYTHON WITH @SINGLEDISPATCHfrom 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 :(

Page 35: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

DECORATORDecorator pattern != @decorator functions in Python

Extend behaviour of a given objectPossible during runtimeMultiple times, with different decorators and order

Page 36: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

DECORATOR - EXAMPLEassert 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

Page 37: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

DECORATOR - EXAMPLE 2class 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()

Page 38: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

WAIT A SEC... WHAT HAPPENS IF I REQUESTAN 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. Bydefault it just throws an exception

Page 39: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

WHAT HAPPENS IF I REQUEST ANATTRIBUTE? - __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

Page 40: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

DECORATOR - IMPLEMENTATIONclass 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.

Page 41: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SUMMARYPython is a very flexible tool

Page 42: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

IS MAGIC WORTH THE EFFORT?

Page 43: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SUMMARY (THIS TIME FOR REAL)know well your tools (Python!)get inspiration from other languages and communitiesknow a business domain of your project

Page 44: W H Y YOU DON ' T N E E D DE S I G N PAT T E R N S I N PYT ......W A I T A S E C . . . W H AT H A P P E N S I F I R E QU E ST A N AT T R I B U T E ? some_object.some_method #

SEBASTIAN BUCZYŃSKIWorking for

Blogging under Twitter:

STX Nextbreadcrumbscollector.tech

EnforcerPL