SlideShare une entreprise Scribd logo
1  sur  166
Exhibition of Atrocity
     Mike Pirnat // PyCon 2011
             @mpirnat
Disclaimer
Lies, Damn Lies, and
Hungarian Notation
Good Intentions
Good Intentions
• strFirstName
Good Intentions
• strFirstName
• intYear
Good Intentions
• strFirstName
• intYear
• blnSignedIn
Good Intentions
• strFirstName
• intYear
• blnSignedIn
• fltTaxRate
Good Intentions
• strFirstName
• intYear
• blnSignedIn
• fltTaxRate
• lstProducts
Good Intentions
• strFirstName
• intYear
• blnSignedIn
• fltTaxRate
• lstProducts
• dctParams
Silliness
Silliness

• lctResults ??
Silliness

• lctResults ??
• objMyReallyLongName ??
Silliness

• lctResults ??
• objMyReallyLongName ??
• objMRLN ??
Silliness

• lctResults ??
• objMyReallyLongName ??
• objMRLN ??
• varValue ??
Silliness

• lctResults ??
• objMyReallyLongName ??
• objMRLN ??
• varValue ??
• blnMagic ??
LIES
LIES

• strCustomerNumber
LIES

• strCustomerNumber
• blnFoo = 'N'
LIES

• strCustomerNumber
• blnFoo = 'N'
• ‘NoneType’ object has no attribute...
Crimes Against
    PEP-8
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
class MyGiganticUglyClass(object):

 def iUsedToWriteJava(self,x,y = 42):
   blnTwoSpacesAreMoreEfficient = 1
   while author.tragicallyConfused():
      print "Three spaces FTW roflbbq!!1!"
   if (new_addition):
       four_spaces_are_best = True
   if (multipleAuthors 
       or peopleDisagree):
           print "tabs! spaces are so mainstream"
   return ((pain) and (suffering))
from regrets              import unfortunate_choices

class AnotherBadHabit(object):

    short_name            = 'foo'
    much_longer_name      = 'bar'

    def __init__(self, x, y, z):
        self.x          = x
        self.y          = y
        self.z_is_a_silly_name = z
        self.came_later        = 42
        self.leftover   = ‘timewaster’

        self.dictionary   =   {
            'foo'         :   'bar',
            'bar'         :   'baz',
            'baz'         :   'quux',
        }
from regrets              import unfortunate_choices

class AnotherBadHabit(object):

    short_name            = 'foo'
    much_longer_name      = 'bar'

    def __init__(self, x, y, z):
        self.x          = x
        self.y          = y
        self.z_is_a_silly_name = z
        self.came_later        = 42
        self.leftover   = ‘timewaster’

        self.dictionary   =   {
            'foo'         :   'bar',
            'bar'         :   'baz',
            'baz'         :   'quux',
        }
from regrets              import unfortunate_choices

class AnotherBadHabit(object):

    short_name            = 'foo'
    much_longer_name      = 'bar'

    def __init__(self, x, y, z):
        self.x          = x
        self.y          = y
        self.z_is_a_silly_name = z
        self.came_later        = 42
        self.leftover   = ‘timewaster’

        self.dictionary   =   {
            'foo'         :   'bar',
            'bar'         :   'baz',
            'baz'         :   'quux',
        }
A Twisty Maze of
Single-Character
Variables, All Alike
f.write(string.join(map(lambda
x,y=self.__dicProfiles,z=strPy:"%0.3s %s:
%s:(%s)" % (z,x,y[x][0],y[x]
[1]),self.__dicProfiles.keys()),'n')
+'n')
#!/usr/bin/env python
import os,sys
C=os.chdir
S=os.system
M=os.mkdir
J=os.path.join
A=os.path.abspath
D=os.path.dirname
E=os.path.exists
W=sys.stdout.write
V=sys.argv
X=sys.exit
ERR=lambda m:W(m+"n")
PRNT=lambda m:W(m+"n")
assert len(V)==2,"you must provide a sandbox name"
SB=V[1]
H=A(D(__file__))
SBD=J(D(H),SB)
C(SBD)
PST=J(SBD,'bin/paster')
VAR=J(SBD,'var')
ETC=J(SBD,'etc')
S("mkdir -p "+VAR)
PRNT("restarting "+SB)
CMD=";".join(['source %s'%J(SBD,'bin/activate'),PST+" serve --daemon --pid-
file=%s/sandbox.pid --log-file=%s/sandbox.log %s/sandbox.ini start"%
(VAR,VAR,ETC)])
PRNT(CMD)
S(CMD)
PRNT("All done!")
X(0)
#!/usr/bin/env python
import os,sys
C=os.chdir
S=os.system
M=os.mkdir
J=os.path.join
A=os.path.abspath
D=os.path.dirname
E=os.path.exists
W=sys.stdout.write
V=sys.argv
X=sys.exit
ERR=lambda m:W(m+"n")
PRNT=lambda m:W(m+"n")
assert len(V)==2,"you must provide a sandbox name"
SB=V[1]
H=A(D(__file__))
SBD=J(D(H),SB)
C(SBD)
PST=J(SBD,'bin/paster')
VAR=J(SBD,'var')
ETC=J(SBD,'etc')
S("mkdir -p "+VAR)
PRNT("restarting "+SB)
CMD=";".join(['source %s'%J(SBD,'bin/activate'),PST+" serve --daemon --pid-
file=%s/sandbox.pid --log-file=%s/sandbox.log %s/sandbox.ini start"%
(VAR,VAR,ETC)])
PRNT(CMD)
S(CMD)
PRNT("All done!")
X(0)
Lambdas, Lambdas
  Everywhere
lstRollOut = lstRollOut + filter(lambda x:
x[-1] == '0', filter(lambda x: x != '0|0',
lstMbrSrcCombo))
lstRollOut = lstRollOut + filter(lambda x:
x[-1] == '0', filter(lambda x: x != '0|0',
lstMbrSrcCombo))

if not filter(lambda lst, sm=sm: sm in
lst, map(lambda x, dicA=dicA: dicA.get(x,
[]), lstAttribute)):
_make_keys = lambda cc, pm:
tuple(map(lambda m, c=cc: ("%s.%s" % (c,
m), m), pm))
_make_keys = lambda cc, pm:
tuple(map(lambda m, c=cc: ("%s.%s" % (c,
m), m), pm))

return [map(lambda l: l[0],
lstResults),map(lambda l: l[1],
lstResults)]
_make_keys = lambda cc, pm:
tuple(map(lambda m, c=cc: ("%s.%s" % (c,
m), m), pm))

return [map(lambda l: l[0],
lstResults),map(lambda l: l[1],
lstResults)]

sum = lambda lst: lst and reduce(lambda x,
y: x + y, lst) or 0
_make_keys = lambda cc, pm:
tuple(map(lambda m, c=cc: ("%s.%s" % (c,
m), m), pm))

return [map(lambda l: l[0],
lstResults),map(lambda l: l[1],
lstResults)]

sum = lambda lst: lst and reduce(lambda x,
y: x + y, lst) or 0

assert reduce(lambda x,y: x or y, [z.id ==
event.id for z in events])
The List
Comprehension that
  Ate Cincinnati
lstCrumb = [y for y in [x.replace('"','') for x in
lstCrumb] if y]
lstCrumb = [y for y in [x.replace('"','') for x in
lstCrumb] if y]

return [dict(x) for x in [zip(lstKeys,x) for x in
lstValues]]
lstCrumb = [y for y in [x.replace('"','') for x in
lstCrumb] if y]

return [dict(x) for x in [zip(lstKeys,x) for x in
lstValues]]

prop_list = [FilterProp(prop='P_EXCLUDED',
data='_'.join([i,j,k])) for i in prop_data[0] for
j in prop_data[1] for k in prop_data[2]]
def feedparser_entries_to_RSSItems(entries):
    rss_items = [PyRSS2Gen.RSSItem(
            title = x.get('title','Untitled'),
            link = x.link,
            description = x.summary,
            guid = x.link,
            pubDate = datetime.datetime(
                     x.modified_parsed[0],
                     x.modified_parsed[1],
                     x.modified_parsed[2],
                     x.modified_parsed[3],
                     x.modified_parsed[4],
                     x.modified_parsed[5]))
            for x in entries]
    return rss_items
def feedparser_entries_to_RSSItems(entries):
    rss_items = [rss_item(entry) for entry in entries]
    return rss_items
The Beast with a
 Thousand Elifs
def do_awesome_stuff():
    ...
    if site.isSiteOne():
        ...
    elif site.isSiteTwo():
        ...
    elif site.getSite() in ['site3', 'site4']:
        ...
    elif site.isSiteFive():
        ...
    else:
        ...
    ...
def strategy1():
    ...

def strategy2():
    ...

site_strategies = {
    'site1': strategy1,
    'site2': strategy2,
    ...
}

def do_awesome_stuff():
    which_one = ...
    strategy = site_strategies[which_one]
    strategy()
def strategy1():
    ...

def strategy2():
    ...

site_strategies = {
    'site1': strategy1,
    'site2': strategy2,
    ...
}

def do_awesome_stuff():
    which_one = ...
    strategy = site_strategies[which_one]
    strategy()
def strategy1():
    ...

def strategy2():
    ...

site_strategies = {
    'site1': strategy1,
    'site2': strategy2,
    ...
}

def do_awesome_stuff():
    which_one = ...
    strategy = site_strategies[which_one]
    strategy()
def strategy1():
    ...

def strategy2():
    ...

site_strategies = {
    'site1': strategy1,
    'site2': strategy2,
    ...
}

def do_awesome_stuff():
    which_one = ...
    strategy = site_strategies[which_one]
    strategy()
def do_awesome_stuff(strategy):
    ...
    strategy()
    ...
def do_awesome_stuff(strategy):
    ...
    strategy()
    ...
class Foo(object):

   def __init__(self, strategy):
       self.strategy = strategy

   def do_awesome_stuff(self):
       ...
       self.strategy()
       ...
class Foo(object):

   def __init__(self, strategy):
       self.strategy = strategy

   def do_awesome_stuff(self):
       ...
       self.strategy()
       ...
class Foo(object):

   @inject(strategy=IStrategy)
   def __init__(self, strategy):
       self.strategy = strategy

   def do_awesome_stuff(self):
       ...
       self.strategy()
       ...
class Foo(object):

   @inject(strategy=IStrategy)
   def __init__(self, strategy):
       self.strategy = strategy

   def do_awesome_stuff(self):
       ...
       self.strategy()
       ...
class Foo(object):

   @inject(strategy=IStrategy)
   def __init__(self, strategy):
       self.strategy = strategy

   def do_awesome_stuff(self):
       ...
       self.strategy()
       ...
The Malignant Menace
 of Mutable Keyword
 Argument Defaults
def set_reminders(self, event, reminders=[]):
    ...
def set_reminders(self, event, reminders=[]):
    ...
• Extra reminders came from other users'
  requests...
• Extra reminders came from other users'
  requests...

• What if this was really sensitive data?
def set_reminders(self, event, reminders=None):
    reminders = reminders or []
    ...
def set_reminders(self, event, reminders=None):
    reminders = reminders or []
    ...
def set_reminders(self, event, reminders=None):
    reminders = reminders or []
    ...
DCT_BRAND_REMINDERS = {
    SITE_X: [
       HOLIDAYS: [Reminder(...), ...],
       OTHER: [Reminder(...), ...],
       CUSTOM: [Reminder(...), ...],
    ], ...
}

...

class BrandWrangler(object):
    ...
    def get_default_reminders(self, brand):
        return DCT_BRAND_REMINDERS.get(brand, {})
DCT_BRAND_REMINDERS = {
    SITE_X: [
       HOLIDAYS: [Reminder(...), ...],
       OTHER: [Reminder(...), ...],
       CUSTOM: [Reminder(...), ...],
    ], ...
}

...

class BrandWrangler(object):
    ...
    def get_default_reminders(self, brand):
        return DCT_BRAND_REMINDERS.get(brand, {})
DCT_BRAND_REMINDERS = {
    SITE_X: [
       HOLIDAYS: [Reminder(...), ...],
       OTHER: [Reminder(...), ...],
       CUSTOM: [Reminder(...), ...],
    ], ...
}

...

class BrandWrangler(object):
    ...
    def get_default_reminders(self, brand):
        return DCT_BRAND_REMINDERS.get(brand, {})
DCT_BRAND_REMINDERS = {
    SITE_X: [
       HOLIDAYS: [Reminder(...), ...],
       OTHER: [Reminder(...), ...],
       CUSTOM: [Reminder(...), ...],
    ], ...
}

...

class BrandWrangler(object):
    ...
    def get_default_reminders(self, brand):
        return copy.deepcopy(
                DCT_BRAND_REMINDERS.get(brand, {}))
DCT_BRAND_REMINDERS = {
    SITE_X: [
       HOLIDAYS: [Reminder(...), ...],
       OTHER: [Reminder(...), ...],
       CUSTOM: [Reminder(...), ...],
    ], ...
}

...

class BrandWrangler(object):
    ...
    def get_default_reminders(self, brand):
        return copy.deepcopy(
                DCT_BRAND_REMINDERS.get(brand, {}))
Java PTSD:
Getters and Setters
class InviteEvent(object):
    ...

   def getEventNumber(self):
       return self._intEventNumber

   ...

   def setEventNumber(self, x):
       self._intEventNumber = int(x)

   ...

event.setEventNumber(10)
print event.getEventNumber()
class InviteEvent(object):
    ...

   def getEventNumber(self):
       return self._intEventNumber

   ...

   def setEventNumber(self, x):
       self._intEventNumber = int(x)

   ...

event.setEventNumber(10)
print event.getEventNumber()
class InviteEvent(object):
    ...

   def getEventNumber(self):
       return self._intEventNumber

   ...

   def setEventNumber(self, x):
       self._intEventNumber = int(x)

   ...

event.setEventNumber(10)
print event.getEventNumber()
class InviteEvent(object):
    ...

   def getEventNumber(self):
       return self._intEventNumber

   ...

   def setEventNumber(self, x):
       self._intEventNumber = int(x)

   ...

event.setEventNumber(10)
print event.getEventNumber()
class InviteEvent(object):
    ...

   @property
   def event_number(self):
       return self._event_number

   @event_number.setter
   def _set_event_number(self, x):
       self._intEventNumber = int(x)

   ...

event.event_number = 10
print event.event_number
class InviteEvent(object):
    ...

   @property
   def event_number(self):
       return self._event_number

   @event_number.setter
   def _set_event_number(self, x):
       self._intEventNumber = int(x)

   ...

event.event_number = 10
print event.event_number
class InviteEvent(object):
    ...

   @property
   def event_number(self):
       return self._event_number

   @event_number.setter
   def _set_event_number(self, x):
       self._intEventNumber = int(x)

   ...

event.event_number = 10
print event.event_number
DTO Bondage
class Calendar(DataObject):
    calendar_id = None
    user_id = None
    label = None
    description = None
class DataObject(object):

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for key, value in cls.__dict__.items():
            if not callable(value) 
                   and not hasattr(value, '__get__') 
                   and not key.startswith('_'):
                setattr(obj, key, value)
        return obj
class DataObject(object):

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for key, value in cls.__dict__.items():
            if not callable(value) 
                   and not hasattr(value, '__get__') 
                   and not key.startswith('_'):
                setattr(obj, key, value)
        return obj
class DataObject(object):

    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for key, value in cls.__dict__.items():
            if not callable(value) 
                   and not hasattr(value, '__get__') 
                   and not key.startswith('_'):
                setattr(obj, key, value)
        return obj
class StrictDataObject(DataObject):

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                raise AttributeError(
                    'key %r must be predefined '
                    'at the class level' % key)
class StrictDataObject(DataObject):

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                raise AttributeError(
                    'key %r must be predefined '
                    'at the class level' % key)
class StrictDataObject(DataObject):

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                raise AttributeError(
                    'key %r must be predefined '
                    'at the class level' % key)
class StrictDataObject(DataObject):

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                raise AttributeError(
                    'key %r must be predefined '
                    'at the class level' % key)
class MutantDataObject(StrictDataObject):

   def __new__(cls, *args, **kwargs):
       new_object = object.__new__(cls)
       for c in cls.__mro__[::-1]:
           update_friendly_dict = 
               _get_update_friendly_dict_from_class_dictproxy(c.__dict__)
           new_object.update(update_friendly_dict)
       return new_object

   def __init__(self, **kwargs):
       deferred = {}
       clean = {}
       for key, value in kwargs.items():
           if hasattr(self, key):
               clean[key] = value
           else:
               deferred[key] = value

         super(MutantDataObject, self).__init__(**clean)
         self.update(deferred)

   ...
time passes...
Time to instantiate 100000

1.9



1.8



1.7



1.6



1.5
      DataObject                     StrictDataObject
Time to instantiate 100000

  15



11.25



  7.5



 3.75



   0
        DataObject           StrictDataObject     MutantDataObject
class DataObject(object):

    _mutables = None
    __metaclass__ = DataObjectMeta

    def __new__(cls, **kwargs):
        obj = object.__new__(cls)
        for name, default_func in cls._mutables:
            if name not in kwargs:
                setattr(obj, name, default_func())
        return obj

    def __init__(self, **kwargs):
        for name, value in kwargs.iteritems():
            setattr(self, name, value)
class DataObject(object):

    _mutables = None
    __metaclass__ = DataObjectMeta

    def __new__(cls, **kwargs):
        obj = object.__new__(cls)
        for name, default_func in cls._mutables:
            if name not in kwargs:
                setattr(obj, name, default_func())
        return obj

    def __init__(self, **kwargs):
        for name, value in kwargs.iteritems():
            setattr(self, name, value)
class DataObject(object):

    _mutables = None
    __metaclass__ = DataObjectMeta

    def __new__(cls, **kwargs):
        obj = object.__new__(cls)
        for name, default_func in cls._mutables:
            if name not in kwargs:
                setattr(obj, name, default_func())
        return obj

    def __init__(self, **kwargs):
        for name, value in kwargs.iteritems():
            setattr(self, name, value)
class DataObject(object):

    _mutables = None
    __metaclass__ = DataObjectMeta

    def __new__(cls, **kwargs):
        obj = object.__new__(cls)
        for name, default_func in cls._mutables:
            if name not in kwargs:
                setattr(obj, name, default_func())
        return obj

    def __init__(self, **kwargs):
        for name, value in kwargs.iteritems():
            setattr(self, name, value)
class DataObjectMeta(type):

   def __new__(meta, classname, bases, classdict):
       mutables = find_mutables(classdict, bases)
       classdict['_mutables'] = mutables
       return type.__new__(meta, classname, bases, classdict)


def find_mutables(classdict, bases):
    def create_default(name, value):
        return name, lambda: copy.copy(value)

   found_mutables = set()
   mutables = set()
   for name, value in classdict.items():
       if (not name.startswith('__') and not callable(value)
               and not hasattr(value, '__get__') and
               isinstance(value, (list, dict))):
           if not name in found_mutables:
               found_mutables.add(name)
               mutables.add(create_default(name, value))
               del classdict[name]
   for base in bases:
       if hasattr(base, '_mutables'):
           for name, value in base._mutables:
               if not name in found_mutables:
                    found_mutables.add(name)
                    mutables.add((name, value))
   return frozenset(mutables)
Time to instantiate 100000

  15



11.25



  7.5



 3.75



   0
        DataObject   StrictDataObject   MutantDataObject   Metaclass DataObject
God Objects,
 God Methods, and
The Mile-Long Club
class CardOrderPage(WebPage):
    ...

   ~2900 lines
   69 methods
       __init__            ~85   lines
       _populateFromUPD   ~200   lines
       _populateMisc      ~200   lines
       _completeOrder     ~300   lines
       _buildOrderDct     ~300   lines
       _redirectTo        ~200   lines
class MemberOrderPage(WebPage):
    ...

   ~2400 lines
   58 methods
       __init__               ~90 lines
       processInit            ~160 lines
       processSubmit          ~120 lines
       _processOrder          ~220 lines
       _processVirtualOrder   ~110 lines
       _createUCSOrder        ~240 lines
       _sendWelcomeEmail      ~120 lines
class Session(object):
    ...

   ~1000 lines
   79 methods, mostly small
       __init__        ~180 lines
       _buildReturnURL ~125 lines
def update_everything(...):
    ...
def update_everything(...):
    ...


def do_everything(...):
    ...
def update_everything(...):
    ...


def do_everything(...):
    ...


def go(...):
    ...
Refactor
The Seductive Lure
  of Global State
# gvars.py

dctEnv = None
objSession = None
objWebvars = None
objHeaders = None
objUserAgent = None
from col.web import gvars

...

if gvars.objSession.hasSomething():
    ...

if gvars.objWebvars.get('foo') == 'bar':
    ...

strUserName = 
    gvars.objSession.objCustomer.getName()
• Highly convenient when writing front-end
  website code
• Highly convenient when writing front-end
  website code

• Highly painful when doing anything else
  (scripts, cron jobs, back-end systems)
• Highly convenient when writing front-end
  website code

• Highly painful when doing anything else
  (scripts, cron jobs, back-end systems)

• Any code that uses it is not reusable
  outside the website context without
  extensive faking
• Highly convenient when writing front-end
  website code

• Highly painful when doing anything else
  (scripts, cron jobs, back-end systems)

• Any code that uses it is not reusable
  outside the website context without
  extensive faking

• Safety issues/data leakage
Breaking the Law
  (of Demeter)
gvars.objSession.objCustomer.objMemberStatus
        .isPAID()
gvars.objSession.objCustomer.objMemberStatus
        .isPAID()

if gvars.dctEnv['session'].getCustomer()
        .isSignedIn():
gvars.objSession.objCustomer.objMemberStatus
        .isPAID()

if gvars.dctEnv['session'].getCustomer()
        .isSignedIn():

current_url = self.objSession._getCurrentURL()
gvars.objSession.objCustomer.objMemberStatus
        .isPAID()

if gvars.dctEnv['session'].getCustomer()
        .isSignedIn():

current_url = self.objSession._getCurrentURL()

return (iEvent._objGuestList.getGuestList()[0]
        .getEventSequence())
class MyController(BaseController):
    @require_auth(...)
    def my_action(self, request):
        ...

class require_auth(object):
    ...
    def __call__(self, function):
        def decorated(controller, request,
                 *args, **kwargs):
            user = controller.user
            if user.signed_in:
                ...
            ...
        return decorated
class MyController(BaseController):
    @require_auth(...)
    def my_action(self, request):
        ...

class require_auth(object):
    ...
    def __call__(self, function):
        def decorated(controller, request,
                 *args, **kwargs):
            user = controller.user
            if user.signed_in:
                ...
            ...
        return decorated
The Diaper Pattern
def get_important_object():
    try:
         data = talk_to_database(...)
         return ImportantObject(data)
    except:
         pass

foo = get_important_object(...)
foo.do_something_important()
def get_important_object():
    try:
         data = talk_to_database(...)
         return ImportantObject(data)
    except:
         pass

foo = get_important_object(...)
foo.do_something_important()
def get_important_object():
    try:
         data = talk_to_database(...)
         return ImportantObject(data)
    except:
         pass

foo = get_important_object(...)
foo.do_something_important()
def get_important_object():
    data = talk_to_database(...)
    return ImportantObject(data)

foo = get_important_object(...)
foo.do_something_important()
Creative Ways to
 Break the Build
class DuckPuncher(object):
    def __init__(...): ...
    def setup(...): ...
    def teardown(...): ...
    def punch(...): ...
    def hug(...): ...

    def with_setup(self, func):
       def test_func_wrapper(*args, **kwargs):
            self.setup()
            ret = func(*args, **kwargs)
            self.teardown()
            return ret

        test_func_wrapper = wraps(func)(test_func_wrapper)
        return test_func_wrapper
class DuckPuncher(object):
    def __init__(...): ...
    def setup(...): ...
    def teardown(...): ...
    def punch(...): ...
    def hug(...): ...

    def with_setup(self, func):
       def test_func_wrapper(*args, **kwargs):
            self.setup()
            ret = func(*args, **kwargs)
            self.teardown()
            return ret

        test_func_wrapper = wraps(func)(test_func_wrapper)
        return test_func_wrapper
class DuckPuncher(object):
    def __init__(...): ...
    def setup(...): ...
    def teardown(...): ...
    def punch(...): ...
    def hug(...): ...

    def with_setup(self, func):
       def test_func_wrapper(*args, **kwargs):
            self.setup()
            try:
                 ret = func(*args, **kwargs)
            finally:
                 self.teardown()
            return ret

        test_func_wrapper = wraps(func)(test_func_wrapper)
        return test_func_wrapper
class DuckPuncher(object):
    def __init__(...): ...
    def setup(...): ...
    def teardown(...): ...
    def punch(...): ...
    def hug(...): ...

    def with_setup(self, func):
       def test_func_wrapper(*args, **kwargs):
            self.setup()
            try:
                 ret = func(*args, **kwargs)
            finally:
                 self.teardown()
            return ret

        test_func_wrapper = wraps(func)(test_func_wrapper)
        return test_func_wrapper
from mock import patch


class TestMyThing(...):

    @patch(‘__builtin__.open’):
    def test_writes_files(self, mock_open):
        ...
Adventures in Wheel
    Reinvention
•   log
•   log

•   xlog
•   log

•   xlog

•   simplelog
•   log

•   xlog

•   simplelog

•   superlog
•   log

•   xlog

•   simplelog

•   superlog

•   pylogger
•   log

•   xlog

•   simplelog

•   superlog

•   pylogger

•   prettylog
•   log         •   logging (not stdlib!)

•   xlog

•   simplelog

•   superlog

•   pylogger

•   prettylog
•   log         •   logging (not stdlib!)

•   xlog        •   logmanager

•   simplelog

•   superlog

•   pylogger

•   prettylog
•   log         •   logging (not stdlib!)

•   xlog        •   logmanager

•   simplelog   •   logging (stdlib!)

•   superlog

•   pylogger

•   prettylog
•   log         •   logging (not stdlib!)

•   xlog        •   logmanager

•   simplelog   •   logging (stdlib!)

•   superlog    •   logwrangler

•   pylogger

•   prettylog
•   log         •   logging (not stdlib!)

•   xlog        •   logmanager

•   simplelog   •   logging (stdlib!)

•   superlog    •   logwrangler

•   pylogger    •   agi.log

•   prettylog
tl;dr

• Hungarian notation BAD
• PEP-8 GOOD
• Unclean code BAD
• Use existing tools GOOD
• Weird build breaking BAD
Things to Explore
Things to Explore

• Clean Code: http://ASIN.cc/~11GQo
Things to Explore

• Clean Code: http://ASIN.cc/~11GQo
• http://code.google.com/p/snake-guice/
Things to Explore

• Clean Code: http://ASIN.cc/~11GQo
• http://code.google.com/p/snake-guice/
• http://www.voidspace.org.uk/python/mock/
Links
• Twitter: @mpirnat
• Blog: http://mike.pirnat.com
• Inspiration:
  •   Gary Numan: http://youtu.be/Oj2PR9CI9xY

  •   Joy Division: http://youtu.be/5AqeqAQ1ILI

  •   Hyperbole and a Half: http://hyperboleandahalf.blogspot.com/2010/06/
      this-is-why-ill-never-be-adult.html

Contenu connexe

Tendances

Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to PythonUC San Diego
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in RustIngvar Stepanyan
 
Stupid Awesome Python Tricks
Stupid Awesome Python TricksStupid Awesome Python Tricks
Stupid Awesome Python TricksBryan Helmig
 
PHP 7 – What changed internally? (Forum PHP 2015)
PHP 7 – What changed internally? (Forum PHP 2015)PHP 7 – What changed internally? (Forum PHP 2015)
PHP 7 – What changed internally? (Forum PHP 2015)Nikita Popov
 
Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기JangHyuk You
 
Slides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersSlides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersGiovanni924
 
Functional Algebra: Monoids Applied
Functional Algebra: Monoids AppliedFunctional Algebra: Monoids Applied
Functional Algebra: Monoids AppliedSusan Potter
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objectsHusain Dalal
 
PHP 7 – What changed internally?
PHP 7 – What changed internally?PHP 7 – What changed internally?
PHP 7 – What changed internally?Nikita Popov
 
Pig Introduction to Pig
Pig Introduction to PigPig Introduction to Pig
Pig Introduction to PigChris Wilkes
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecLoïc Descotte
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기진성 오
 
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...takeoutweight
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)James Titcumb
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is AwesomeAstrails
 

Tendances (20)

Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to Python
 
Building fast interpreters in Rust
Building fast interpreters in RustBuilding fast interpreters in Rust
Building fast interpreters in Rust
 
Stupid Awesome Python Tricks
Stupid Awesome Python TricksStupid Awesome Python Tricks
Stupid Awesome Python Tricks
 
ES6 and BEYOND
ES6 and BEYONDES6 and BEYOND
ES6 and BEYOND
 
PHP 7 – What changed internally? (Forum PHP 2015)
PHP 7 – What changed internally? (Forum PHP 2015)PHP 7 – What changed internally? (Forum PHP 2015)
PHP 7 – What changed internally? (Forum PHP 2015)
 
Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기Programming Lisp Clojure - 2장 : 클로저 둘러보기
Programming Lisp Clojure - 2장 : 클로저 둘러보기
 
Slides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammersSlides chapter3part1 ruby-forjavaprogrammers
Slides chapter3part1 ruby-forjavaprogrammers
 
Functional Algebra: Monoids Applied
Functional Algebra: Monoids AppliedFunctional Algebra: Monoids Applied
Functional Algebra: Monoids Applied
 
Groovy grails types, operators, objects
Groovy grails types, operators, objectsGroovy grails types, operators, objects
Groovy grails types, operators, objects
 
PHP 7 – What changed internally?
PHP 7 – What changed internally?PHP 7 – What changed internally?
PHP 7 – What changed internally?
 
Pig Introduction to Pig
Pig Introduction to PigPig Introduction to Pig
Pig Introduction to Pig
 
ddd+scala
ddd+scaladdd+scala
ddd+scala
 
Grammarware Memes
Grammarware MemesGrammarware Memes
Grammarware Memes
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
 
Swift 함수 커링 사용하기
Swift 함수 커링 사용하기Swift 함수 커링 사용하기
Swift 함수 커링 사용하기
 
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
 
Why Learn Python?
Why Learn Python?Why Learn Python?
Why Learn Python?
 
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
Mirror, mirror on the wall: Building a new PHP reflection library (DPC 2016)
 
Ruby is Awesome
Ruby is AwesomeRuby is Awesome
Ruby is Awesome
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 

En vedette

The Prow website presentation
The Prow website presentationThe Prow website presentation
The Prow website presentationAleš Vanek
 
Website Design Sample
Website Design Sample Website Design Sample
Website Design Sample Yolanda Berry
 
Holocaust atrocity m_rickabaugh
Holocaust atrocity m_rickabaughHolocaust atrocity m_rickabaugh
Holocaust atrocity m_rickabaughMRickabaugh
 
Website presentation
Website presentationWebsite presentation
Website presentationDaniel Harris
 
Mei Chen Siang the Sample of website
Mei Chen Siang the Sample of websiteMei Chen Siang the Sample of website
Mei Chen Siang the Sample of websitelinmickey
 
Gems of Joy Designs Promotional Card
Gems of Joy Designs Promotional CardGems of Joy Designs Promotional Card
Gems of Joy Designs Promotional CardYolanda Berry
 
Website Presentation
Website PresentationWebsite Presentation
Website Presentationcolmreilly
 
Website Presentation
Website PresentationWebsite Presentation
Website PresentationTod Baker
 
Sample Presnetation for Website Development Project
Sample Presnetation for Website Development ProjectSample Presnetation for Website Development Project
Sample Presnetation for Website Development ProjectPatrick Ogbuitepu
 
Final Website Presentation
Final Website PresentationFinal Website Presentation
Final Website PresentationKyle Cavazos
 
Building a Professional Website for Your Business
Building a Professional Website for Your BusinessBuilding a Professional Website for Your Business
Building a Professional Website for Your BusinessDennis Hong
 
Sample Guide for Writing Website Development Proposal
Sample Guide for Writing Website Development ProposalSample Guide for Writing Website Development Proposal
Sample Guide for Writing Website Development ProposalPatrick Ogbuitepu
 
Website Architecture: Sitemap & Wireframes
Website Architecture: Sitemap & WireframesWebsite Architecture: Sitemap & Wireframes
Website Architecture: Sitemap & WireframesAmy Lamp
 
6 Examples of Beautiful Website Redesigns
6 Examples of Beautiful Website Redesigns 6 Examples of Beautiful Website Redesigns
6 Examples of Beautiful Website Redesigns HubSpot
 
Website Architecture Presentation from Web Strategy Workshops
Website Architecture Presentation from Web Strategy WorkshopsWebsite Architecture Presentation from Web Strategy Workshops
Website Architecture Presentation from Web Strategy WorkshopsCharles Edmunds
 
Website Layout and Structure
Website Layout and StructureWebsite Layout and Structure
Website Layout and StructureMichael Zinniger
 

En vedette (20)

sample website layout
sample website layoutsample website layout
sample website layout
 
The Prow website presentation
The Prow website presentationThe Prow website presentation
The Prow website presentation
 
Website Design Sample
Website Design Sample Website Design Sample
Website Design Sample
 
Holocaust atrocity m_rickabaugh
Holocaust atrocity m_rickabaughHolocaust atrocity m_rickabaugh
Holocaust atrocity m_rickabaugh
 
Website presentation
Website presentationWebsite presentation
Website presentation
 
Mei Chen Siang the Sample of website
Mei Chen Siang the Sample of websiteMei Chen Siang the Sample of website
Mei Chen Siang the Sample of website
 
Gems of Joy Designs Promotional Card
Gems of Joy Designs Promotional CardGems of Joy Designs Promotional Card
Gems of Joy Designs Promotional Card
 
Website Presentation
Website PresentationWebsite Presentation
Website Presentation
 
Website Presentation
Website PresentationWebsite Presentation
Website Presentation
 
Sample Presnetation for Website Development Project
Sample Presnetation for Website Development ProjectSample Presnetation for Website Development Project
Sample Presnetation for Website Development Project
 
Final Website Presentation
Final Website PresentationFinal Website Presentation
Final Website Presentation
 
Building a Professional Website for Your Business
Building a Professional Website for Your BusinessBuilding a Professional Website for Your Business
Building a Professional Website for Your Business
 
Website
WebsiteWebsite
Website
 
Sample Guide for Writing Website Development Proposal
Sample Guide for Writing Website Development ProposalSample Guide for Writing Website Development Proposal
Sample Guide for Writing Website Development Proposal
 
Dynamic website quotation
Dynamic website quotationDynamic website quotation
Dynamic website quotation
 
Website Architecture: Sitemap & Wireframes
Website Architecture: Sitemap & WireframesWebsite Architecture: Sitemap & Wireframes
Website Architecture: Sitemap & Wireframes
 
6 Examples of Beautiful Website Redesigns
6 Examples of Beautiful Website Redesigns 6 Examples of Beautiful Website Redesigns
6 Examples of Beautiful Website Redesigns
 
Creating a Website: Design and Layout
Creating a Website: Design and LayoutCreating a Website: Design and Layout
Creating a Website: Design and Layout
 
Website Architecture Presentation from Web Strategy Workshops
Website Architecture Presentation from Web Strategy WorkshopsWebsite Architecture Presentation from Web Strategy Workshops
Website Architecture Presentation from Web Strategy Workshops
 
Website Layout and Structure
Website Layout and StructureWebsite Layout and Structure
Website Layout and Structure
 

Similaire à Exhibition of Atrocity

Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2Evgeny Borisov
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scaladjspiewak
 
A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009Jordan Baker
 
The groovy puzzlers (as Presented at Gr8Conf US 2014)
The groovy puzzlers (as Presented at Gr8Conf US 2014)The groovy puzzlers (as Presented at Gr8Conf US 2014)
The groovy puzzlers (as Presented at Gr8Conf US 2014)GroovyPuzzlers
 
The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)GroovyPuzzlers
 
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdf
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdfpython3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdf
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdfinfo706022
 
Python_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfPython_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfsagar414433
 
Python_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfPython_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfsagar414433
 
Probabilistic Programming in Scala
Probabilistic Programming in ScalaProbabilistic Programming in Scala
Probabilistic Programming in ScalaBeScala
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2Hang Zhao
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05cKaz Yoshikawa
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Baruch Sadogursky
 
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsBaruch Sadogursky
 
Python Workshop - Learn Python the Hard Way
Python Workshop - Learn Python the Hard WayPython Workshop - Learn Python the Hard Way
Python Workshop - Learn Python the Hard WayUtkarsh Sengar
 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScriptniklal
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapModern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapHoward Lewis Ship
 
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
Implement the following sorting algorithms  Bubble Sort  Insertion S.pdfImplement the following sorting algorithms  Bubble Sort  Insertion S.pdf
Implement the following sorting algorithms Bubble Sort Insertion S.pdfkesav24
 

Similaire à Exhibition of Atrocity (20)

Intro to Python
Intro to PythonIntro to Python
Intro to Python
 
Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2Groovy puzzlers jug-moscow-part 2
Groovy puzzlers jug-moscow-part 2
 
High Wizardry in the Land of Scala
High Wizardry in the Land of ScalaHigh Wizardry in the Land of Scala
High Wizardry in the Land of Scala
 
A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009
 
The groovy puzzlers (as Presented at Gr8Conf US 2014)
The groovy puzzlers (as Presented at Gr8Conf US 2014)The groovy puzzlers (as Presented at Gr8Conf US 2014)
The groovy puzzlers (as Presented at Gr8Conf US 2014)
 
The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)The groovy puzzlers (as Presented at JavaOne 2014)
The groovy puzzlers (as Presented at JavaOne 2014)
 
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdf
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdfpython3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdf
python3 HashTableSolutionmain.pyfrom ChainingHashTable impor.pdf
 
Python_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfPython_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdf
 
Python_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdfPython_Cheat_Sheet_Keywords_1664634397.pdf
Python_Cheat_Sheet_Keywords_1664634397.pdf
 
Probabilistic Programming in Scala
Probabilistic Programming in ScalaProbabilistic Programming in Scala
Probabilistic Programming in Scala
 
Python speleology
Python speleologyPython speleology
Python speleology
 
Fp in scala part 2
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
 
Idioms in swift 2016 05c
Idioms in swift 2016 05cIdioms in swift 2016 05c
Idioms in swift 2016 05c
 
Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014Groovy puzzlers по русски с Joker 2014
Groovy puzzlers по русски с Joker 2014
 
The Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 SeasonsThe Groovy Puzzlers – The Complete 01 and 02 Seasons
The Groovy Puzzlers – The Complete 01 and 02 Seasons
 
Python Workshop - Learn Python the Hard Way
Python Workshop - Learn Python the Hard WayPython Workshop - Learn Python the Hard Way
Python Workshop - Learn Python the Hard Way
 
Wrong
WrongWrong
Wrong
 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter BootstrapModern Application Foundations: Underscore and Twitter Bootstrap
Modern Application Foundations: Underscore and Twitter Bootstrap
 
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
Implement the following sorting algorithms  Bubble Sort  Insertion S.pdfImplement the following sorting algorithms  Bubble Sort  Insertion S.pdf
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
 

Dernier

Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?XfilesPro
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 

Dernier (20)

Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?How to Remove Document Management Hurdles with X-Docs?
How to Remove Document Management Hurdles with X-Docs?
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 

Exhibition of Atrocity

  • 1. Exhibition of Atrocity Mike Pirnat // PyCon 2011 @mpirnat
  • 2.
  • 4. Lies, Damn Lies, and Hungarian Notation
  • 8. Good Intentions • strFirstName • intYear • blnSignedIn
  • 9. Good Intentions • strFirstName • intYear • blnSignedIn • fltTaxRate
  • 10. Good Intentions • strFirstName • intYear • blnSignedIn • fltTaxRate • lstProducts
  • 11. Good Intentions • strFirstName • intYear • blnSignedIn • fltTaxRate • lstProducts • dctParams
  • 14. Silliness • lctResults ?? • objMyReallyLongName ??
  • 15. Silliness • lctResults ?? • objMyReallyLongName ?? • objMRLN ??
  • 16. Silliness • lctResults ?? • objMyReallyLongName ?? • objMRLN ?? • varValue ??
  • 17. Silliness • lctResults ?? • objMyReallyLongName ?? • objMRLN ?? • varValue ?? • blnMagic ??
  • 18. LIES
  • 21. LIES • strCustomerNumber • blnFoo = 'N' • ‘NoneType’ object has no attribute...
  • 22. Crimes Against PEP-8
  • 23. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 24. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 25. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 26. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 27. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 28. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 29. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 30. class MyGiganticUglyClass(object): def iUsedToWriteJava(self,x,y = 42): blnTwoSpacesAreMoreEfficient = 1 while author.tragicallyConfused(): print "Three spaces FTW roflbbq!!1!" if (new_addition): four_spaces_are_best = True if (multipleAuthors or peopleDisagree): print "tabs! spaces are so mainstream" return ((pain) and (suffering))
  • 31. from regrets import unfortunate_choices class AnotherBadHabit(object): short_name = 'foo' much_longer_name = 'bar' def __init__(self, x, y, z): self.x = x self.y = y self.z_is_a_silly_name = z self.came_later = 42 self.leftover = ‘timewaster’ self.dictionary = { 'foo' : 'bar', 'bar' : 'baz', 'baz' : 'quux', }
  • 32. from regrets import unfortunate_choices class AnotherBadHabit(object): short_name = 'foo' much_longer_name = 'bar' def __init__(self, x, y, z): self.x = x self.y = y self.z_is_a_silly_name = z self.came_later = 42 self.leftover = ‘timewaster’ self.dictionary = { 'foo' : 'bar', 'bar' : 'baz', 'baz' : 'quux', }
  • 33. from regrets import unfortunate_choices class AnotherBadHabit(object): short_name = 'foo' much_longer_name = 'bar' def __init__(self, x, y, z): self.x = x self.y = y self.z_is_a_silly_name = z self.came_later = 42 self.leftover = ‘timewaster’ self.dictionary = { 'foo' : 'bar', 'bar' : 'baz', 'baz' : 'quux', }
  • 34. A Twisty Maze of Single-Character Variables, All Alike
  • 35. f.write(string.join(map(lambda x,y=self.__dicProfiles,z=strPy:"%0.3s %s: %s:(%s)" % (z,x,y[x][0],y[x] [1]),self.__dicProfiles.keys()),'n') +'n')
  • 36. #!/usr/bin/env python import os,sys C=os.chdir S=os.system M=os.mkdir J=os.path.join A=os.path.abspath D=os.path.dirname E=os.path.exists W=sys.stdout.write V=sys.argv X=sys.exit ERR=lambda m:W(m+"n") PRNT=lambda m:W(m+"n") assert len(V)==2,"you must provide a sandbox name" SB=V[1] H=A(D(__file__)) SBD=J(D(H),SB) C(SBD) PST=J(SBD,'bin/paster') VAR=J(SBD,'var') ETC=J(SBD,'etc') S("mkdir -p "+VAR) PRNT("restarting "+SB) CMD=";".join(['source %s'%J(SBD,'bin/activate'),PST+" serve --daemon --pid- file=%s/sandbox.pid --log-file=%s/sandbox.log %s/sandbox.ini start"% (VAR,VAR,ETC)]) PRNT(CMD) S(CMD) PRNT("All done!") X(0)
  • 37. #!/usr/bin/env python import os,sys C=os.chdir S=os.system M=os.mkdir J=os.path.join A=os.path.abspath D=os.path.dirname E=os.path.exists W=sys.stdout.write V=sys.argv X=sys.exit ERR=lambda m:W(m+"n") PRNT=lambda m:W(m+"n") assert len(V)==2,"you must provide a sandbox name" SB=V[1] H=A(D(__file__)) SBD=J(D(H),SB) C(SBD) PST=J(SBD,'bin/paster') VAR=J(SBD,'var') ETC=J(SBD,'etc') S("mkdir -p "+VAR) PRNT("restarting "+SB) CMD=";".join(['source %s'%J(SBD,'bin/activate'),PST+" serve --daemon --pid- file=%s/sandbox.pid --log-file=%s/sandbox.log %s/sandbox.ini start"% (VAR,VAR,ETC)]) PRNT(CMD) S(CMD) PRNT("All done!") X(0)
  • 38. Lambdas, Lambdas Everywhere
  • 39.
  • 40. lstRollOut = lstRollOut + filter(lambda x: x[-1] == '0', filter(lambda x: x != '0|0', lstMbrSrcCombo))
  • 41. lstRollOut = lstRollOut + filter(lambda x: x[-1] == '0', filter(lambda x: x != '0|0', lstMbrSrcCombo)) if not filter(lambda lst, sm=sm: sm in lst, map(lambda x, dicA=dicA: dicA.get(x, []), lstAttribute)):
  • 42.
  • 43. _make_keys = lambda cc, pm: tuple(map(lambda m, c=cc: ("%s.%s" % (c, m), m), pm))
  • 44. _make_keys = lambda cc, pm: tuple(map(lambda m, c=cc: ("%s.%s" % (c, m), m), pm)) return [map(lambda l: l[0], lstResults),map(lambda l: l[1], lstResults)]
  • 45. _make_keys = lambda cc, pm: tuple(map(lambda m, c=cc: ("%s.%s" % (c, m), m), pm)) return [map(lambda l: l[0], lstResults),map(lambda l: l[1], lstResults)] sum = lambda lst: lst and reduce(lambda x, y: x + y, lst) or 0
  • 46. _make_keys = lambda cc, pm: tuple(map(lambda m, c=cc: ("%s.%s" % (c, m), m), pm)) return [map(lambda l: l[0], lstResults),map(lambda l: l[1], lstResults)] sum = lambda lst: lst and reduce(lambda x, y: x + y, lst) or 0 assert reduce(lambda x,y: x or y, [z.id == event.id for z in events])
  • 47. The List Comprehension that Ate Cincinnati
  • 48.
  • 49. lstCrumb = [y for y in [x.replace('"','') for x in lstCrumb] if y]
  • 50. lstCrumb = [y for y in [x.replace('"','') for x in lstCrumb] if y] return [dict(x) for x in [zip(lstKeys,x) for x in lstValues]]
  • 51. lstCrumb = [y for y in [x.replace('"','') for x in lstCrumb] if y] return [dict(x) for x in [zip(lstKeys,x) for x in lstValues]] prop_list = [FilterProp(prop='P_EXCLUDED', data='_'.join([i,j,k])) for i in prop_data[0] for j in prop_data[1] for k in prop_data[2]]
  • 52. def feedparser_entries_to_RSSItems(entries): rss_items = [PyRSS2Gen.RSSItem( title = x.get('title','Untitled'), link = x.link, description = x.summary, guid = x.link, pubDate = datetime.datetime( x.modified_parsed[0], x.modified_parsed[1], x.modified_parsed[2], x.modified_parsed[3], x.modified_parsed[4], x.modified_parsed[5])) for x in entries] return rss_items
  • 53. def feedparser_entries_to_RSSItems(entries): rss_items = [rss_item(entry) for entry in entries] return rss_items
  • 54. The Beast with a Thousand Elifs
  • 55. def do_awesome_stuff(): ... if site.isSiteOne(): ... elif site.isSiteTwo(): ... elif site.getSite() in ['site3', 'site4']: ... elif site.isSiteFive(): ... else: ... ...
  • 56. def strategy1(): ... def strategy2(): ... site_strategies = { 'site1': strategy1, 'site2': strategy2, ... } def do_awesome_stuff(): which_one = ... strategy = site_strategies[which_one] strategy()
  • 57. def strategy1(): ... def strategy2(): ... site_strategies = { 'site1': strategy1, 'site2': strategy2, ... } def do_awesome_stuff(): which_one = ... strategy = site_strategies[which_one] strategy()
  • 58. def strategy1(): ... def strategy2(): ... site_strategies = { 'site1': strategy1, 'site2': strategy2, ... } def do_awesome_stuff(): which_one = ... strategy = site_strategies[which_one] strategy()
  • 59. def strategy1(): ... def strategy2(): ... site_strategies = { 'site1': strategy1, 'site2': strategy2, ... } def do_awesome_stuff(): which_one = ... strategy = site_strategies[which_one] strategy()
  • 60. def do_awesome_stuff(strategy): ... strategy() ...
  • 61. def do_awesome_stuff(strategy): ... strategy() ...
  • 62. class Foo(object): def __init__(self, strategy): self.strategy = strategy def do_awesome_stuff(self): ... self.strategy() ...
  • 63. class Foo(object): def __init__(self, strategy): self.strategy = strategy def do_awesome_stuff(self): ... self.strategy() ...
  • 64. class Foo(object): @inject(strategy=IStrategy) def __init__(self, strategy): self.strategy = strategy def do_awesome_stuff(self): ... self.strategy() ...
  • 65. class Foo(object): @inject(strategy=IStrategy) def __init__(self, strategy): self.strategy = strategy def do_awesome_stuff(self): ... self.strategy() ...
  • 66. class Foo(object): @inject(strategy=IStrategy) def __init__(self, strategy): self.strategy = strategy def do_awesome_stuff(self): ... self.strategy() ...
  • 67. The Malignant Menace of Mutable Keyword Argument Defaults
  • 68. def set_reminders(self, event, reminders=[]): ...
  • 69. def set_reminders(self, event, reminders=[]): ...
  • 70.
  • 71.
  • 72.
  • 73. • Extra reminders came from other users' requests...
  • 74. • Extra reminders came from other users' requests... • What if this was really sensitive data?
  • 75. def set_reminders(self, event, reminders=None): reminders = reminders or [] ...
  • 76. def set_reminders(self, event, reminders=None): reminders = reminders or [] ...
  • 77. def set_reminders(self, event, reminders=None): reminders = reminders or [] ...
  • 78. DCT_BRAND_REMINDERS = { SITE_X: [ HOLIDAYS: [Reminder(...), ...], OTHER: [Reminder(...), ...], CUSTOM: [Reminder(...), ...], ], ... } ... class BrandWrangler(object): ... def get_default_reminders(self, brand): return DCT_BRAND_REMINDERS.get(brand, {})
  • 79. DCT_BRAND_REMINDERS = { SITE_X: [ HOLIDAYS: [Reminder(...), ...], OTHER: [Reminder(...), ...], CUSTOM: [Reminder(...), ...], ], ... } ... class BrandWrangler(object): ... def get_default_reminders(self, brand): return DCT_BRAND_REMINDERS.get(brand, {})
  • 80. DCT_BRAND_REMINDERS = { SITE_X: [ HOLIDAYS: [Reminder(...), ...], OTHER: [Reminder(...), ...], CUSTOM: [Reminder(...), ...], ], ... } ... class BrandWrangler(object): ... def get_default_reminders(self, brand): return DCT_BRAND_REMINDERS.get(brand, {})
  • 81. DCT_BRAND_REMINDERS = { SITE_X: [ HOLIDAYS: [Reminder(...), ...], OTHER: [Reminder(...), ...], CUSTOM: [Reminder(...), ...], ], ... } ... class BrandWrangler(object): ... def get_default_reminders(self, brand): return copy.deepcopy( DCT_BRAND_REMINDERS.get(brand, {}))
  • 82. DCT_BRAND_REMINDERS = { SITE_X: [ HOLIDAYS: [Reminder(...), ...], OTHER: [Reminder(...), ...], CUSTOM: [Reminder(...), ...], ], ... } ... class BrandWrangler(object): ... def get_default_reminders(self, brand): return copy.deepcopy( DCT_BRAND_REMINDERS.get(brand, {}))
  • 84. class InviteEvent(object): ... def getEventNumber(self): return self._intEventNumber ... def setEventNumber(self, x): self._intEventNumber = int(x) ... event.setEventNumber(10) print event.getEventNumber()
  • 85. class InviteEvent(object): ... def getEventNumber(self): return self._intEventNumber ... def setEventNumber(self, x): self._intEventNumber = int(x) ... event.setEventNumber(10) print event.getEventNumber()
  • 86. class InviteEvent(object): ... def getEventNumber(self): return self._intEventNumber ... def setEventNumber(self, x): self._intEventNumber = int(x) ... event.setEventNumber(10) print event.getEventNumber()
  • 87. class InviteEvent(object): ... def getEventNumber(self): return self._intEventNumber ... def setEventNumber(self, x): self._intEventNumber = int(x) ... event.setEventNumber(10) print event.getEventNumber()
  • 88. class InviteEvent(object): ... @property def event_number(self): return self._event_number @event_number.setter def _set_event_number(self, x): self._intEventNumber = int(x) ... event.event_number = 10 print event.event_number
  • 89. class InviteEvent(object): ... @property def event_number(self): return self._event_number @event_number.setter def _set_event_number(self, x): self._intEventNumber = int(x) ... event.event_number = 10 print event.event_number
  • 90. class InviteEvent(object): ... @property def event_number(self): return self._event_number @event_number.setter def _set_event_number(self, x): self._intEventNumber = int(x) ... event.event_number = 10 print event.event_number
  • 92. class Calendar(DataObject): calendar_id = None user_id = None label = None description = None
  • 93. class DataObject(object): def __new__(cls, *args, **kwargs): obj = object.__new__(cls) for key, value in cls.__dict__.items(): if not callable(value) and not hasattr(value, '__get__') and not key.startswith('_'): setattr(obj, key, value) return obj
  • 94. class DataObject(object): def __new__(cls, *args, **kwargs): obj = object.__new__(cls) for key, value in cls.__dict__.items(): if not callable(value) and not hasattr(value, '__get__') and not key.startswith('_'): setattr(obj, key, value) return obj
  • 95. class DataObject(object): def __new__(cls, *args, **kwargs): obj = object.__new__(cls) for key, value in cls.__dict__.items(): if not callable(value) and not hasattr(value, '__get__') and not key.startswith('_'): setattr(obj, key, value) return obj
  • 96. class StrictDataObject(DataObject): def __init__(self, **kwargs): for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) else: raise AttributeError( 'key %r must be predefined ' 'at the class level' % key)
  • 97. class StrictDataObject(DataObject): def __init__(self, **kwargs): for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) else: raise AttributeError( 'key %r must be predefined ' 'at the class level' % key)
  • 98. class StrictDataObject(DataObject): def __init__(self, **kwargs): for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) else: raise AttributeError( 'key %r must be predefined ' 'at the class level' % key)
  • 99. class StrictDataObject(DataObject): def __init__(self, **kwargs): for key, value in kwargs.items(): if hasattr(self, key): setattr(self, key, value) else: raise AttributeError( 'key %r must be predefined ' 'at the class level' % key)
  • 100. class MutantDataObject(StrictDataObject): def __new__(cls, *args, **kwargs): new_object = object.__new__(cls) for c in cls.__mro__[::-1]: update_friendly_dict = _get_update_friendly_dict_from_class_dictproxy(c.__dict__) new_object.update(update_friendly_dict) return new_object def __init__(self, **kwargs): deferred = {} clean = {} for key, value in kwargs.items(): if hasattr(self, key): clean[key] = value else: deferred[key] = value super(MutantDataObject, self).__init__(**clean) self.update(deferred) ...
  • 102. Time to instantiate 100000 1.9 1.8 1.7 1.6 1.5 DataObject StrictDataObject
  • 103. Time to instantiate 100000 15 11.25 7.5 3.75 0 DataObject StrictDataObject MutantDataObject
  • 104. class DataObject(object): _mutables = None __metaclass__ = DataObjectMeta def __new__(cls, **kwargs): obj = object.__new__(cls) for name, default_func in cls._mutables: if name not in kwargs: setattr(obj, name, default_func()) return obj def __init__(self, **kwargs): for name, value in kwargs.iteritems(): setattr(self, name, value)
  • 105. class DataObject(object): _mutables = None __metaclass__ = DataObjectMeta def __new__(cls, **kwargs): obj = object.__new__(cls) for name, default_func in cls._mutables: if name not in kwargs: setattr(obj, name, default_func()) return obj def __init__(self, **kwargs): for name, value in kwargs.iteritems(): setattr(self, name, value)
  • 106. class DataObject(object): _mutables = None __metaclass__ = DataObjectMeta def __new__(cls, **kwargs): obj = object.__new__(cls) for name, default_func in cls._mutables: if name not in kwargs: setattr(obj, name, default_func()) return obj def __init__(self, **kwargs): for name, value in kwargs.iteritems(): setattr(self, name, value)
  • 107. class DataObject(object): _mutables = None __metaclass__ = DataObjectMeta def __new__(cls, **kwargs): obj = object.__new__(cls) for name, default_func in cls._mutables: if name not in kwargs: setattr(obj, name, default_func()) return obj def __init__(self, **kwargs): for name, value in kwargs.iteritems(): setattr(self, name, value)
  • 108. class DataObjectMeta(type): def __new__(meta, classname, bases, classdict): mutables = find_mutables(classdict, bases) classdict['_mutables'] = mutables return type.__new__(meta, classname, bases, classdict) def find_mutables(classdict, bases): def create_default(name, value): return name, lambda: copy.copy(value) found_mutables = set() mutables = set() for name, value in classdict.items(): if (not name.startswith('__') and not callable(value) and not hasattr(value, '__get__') and isinstance(value, (list, dict))): if not name in found_mutables: found_mutables.add(name) mutables.add(create_default(name, value)) del classdict[name] for base in bases: if hasattr(base, '_mutables'): for name, value in base._mutables: if not name in found_mutables: found_mutables.add(name) mutables.add((name, value)) return frozenset(mutables)
  • 109. Time to instantiate 100000 15 11.25 7.5 3.75 0 DataObject StrictDataObject MutantDataObject Metaclass DataObject
  • 110. God Objects, God Methods, and The Mile-Long Club
  • 111. class CardOrderPage(WebPage): ... ~2900 lines 69 methods __init__ ~85 lines _populateFromUPD ~200 lines _populateMisc ~200 lines _completeOrder ~300 lines _buildOrderDct ~300 lines _redirectTo ~200 lines
  • 112. class MemberOrderPage(WebPage): ... ~2400 lines 58 methods __init__ ~90 lines processInit ~160 lines processSubmit ~120 lines _processOrder ~220 lines _processVirtualOrder ~110 lines _createUCSOrder ~240 lines _sendWelcomeEmail ~120 lines
  • 113. class Session(object): ... ~1000 lines 79 methods, mostly small __init__ ~180 lines _buildReturnURL ~125 lines
  • 114.
  • 116. def update_everything(...): ... def do_everything(...): ...
  • 117. def update_everything(...): ... def do_everything(...): ... def go(...): ...
  • 119. The Seductive Lure of Global State
  • 120. # gvars.py dctEnv = None objSession = None objWebvars = None objHeaders = None objUserAgent = None
  • 121. from col.web import gvars ... if gvars.objSession.hasSomething(): ... if gvars.objWebvars.get('foo') == 'bar': ... strUserName = gvars.objSession.objCustomer.getName()
  • 122.
  • 123. • Highly convenient when writing front-end website code
  • 124. • Highly convenient when writing front-end website code • Highly painful when doing anything else (scripts, cron jobs, back-end systems)
  • 125. • Highly convenient when writing front-end website code • Highly painful when doing anything else (scripts, cron jobs, back-end systems) • Any code that uses it is not reusable outside the website context without extensive faking
  • 126. • Highly convenient when writing front-end website code • Highly painful when doing anything else (scripts, cron jobs, back-end systems) • Any code that uses it is not reusable outside the website context without extensive faking • Safety issues/data leakage
  • 127. Breaking the Law (of Demeter)
  • 128.
  • 130. gvars.objSession.objCustomer.objMemberStatus .isPAID() if gvars.dctEnv['session'].getCustomer() .isSignedIn():
  • 131. gvars.objSession.objCustomer.objMemberStatus .isPAID() if gvars.dctEnv['session'].getCustomer() .isSignedIn(): current_url = self.objSession._getCurrentURL()
  • 132. gvars.objSession.objCustomer.objMemberStatus .isPAID() if gvars.dctEnv['session'].getCustomer() .isSignedIn(): current_url = self.objSession._getCurrentURL() return (iEvent._objGuestList.getGuestList()[0] .getEventSequence())
  • 133. class MyController(BaseController): @require_auth(...) def my_action(self, request): ... class require_auth(object): ... def __call__(self, function): def decorated(controller, request, *args, **kwargs): user = controller.user if user.signed_in: ... ... return decorated
  • 134. class MyController(BaseController): @require_auth(...) def my_action(self, request): ... class require_auth(object): ... def __call__(self, function): def decorated(controller, request, *args, **kwargs): user = controller.user if user.signed_in: ... ... return decorated
  • 136. def get_important_object(): try: data = talk_to_database(...) return ImportantObject(data) except: pass foo = get_important_object(...) foo.do_something_important()
  • 137. def get_important_object(): try: data = talk_to_database(...) return ImportantObject(data) except: pass foo = get_important_object(...) foo.do_something_important()
  • 138. def get_important_object(): try: data = talk_to_database(...) return ImportantObject(data) except: pass foo = get_important_object(...) foo.do_something_important()
  • 139. def get_important_object(): data = talk_to_database(...) return ImportantObject(data) foo = get_important_object(...) foo.do_something_important()
  • 140. Creative Ways to Break the Build
  • 141. class DuckPuncher(object): def __init__(...): ... def setup(...): ... def teardown(...): ... def punch(...): ... def hug(...): ... def with_setup(self, func): def test_func_wrapper(*args, **kwargs): self.setup() ret = func(*args, **kwargs) self.teardown() return ret test_func_wrapper = wraps(func)(test_func_wrapper) return test_func_wrapper
  • 142. class DuckPuncher(object): def __init__(...): ... def setup(...): ... def teardown(...): ... def punch(...): ... def hug(...): ... def with_setup(self, func): def test_func_wrapper(*args, **kwargs): self.setup() ret = func(*args, **kwargs) self.teardown() return ret test_func_wrapper = wraps(func)(test_func_wrapper) return test_func_wrapper
  • 143. class DuckPuncher(object): def __init__(...): ... def setup(...): ... def teardown(...): ... def punch(...): ... def hug(...): ... def with_setup(self, func): def test_func_wrapper(*args, **kwargs): self.setup() try: ret = func(*args, **kwargs) finally: self.teardown() return ret test_func_wrapper = wraps(func)(test_func_wrapper) return test_func_wrapper
  • 144. class DuckPuncher(object): def __init__(...): ... def setup(...): ... def teardown(...): ... def punch(...): ... def hug(...): ... def with_setup(self, func): def test_func_wrapper(*args, **kwargs): self.setup() try: ret = func(*args, **kwargs) finally: self.teardown() return ret test_func_wrapper = wraps(func)(test_func_wrapper) return test_func_wrapper
  • 145. from mock import patch class TestMyThing(...): @patch(‘__builtin__.open’): def test_writes_files(self, mock_open): ...
  • 146. Adventures in Wheel Reinvention
  • 147.
  • 148. log
  • 149. log • xlog
  • 150. log • xlog • simplelog
  • 151. log • xlog • simplelog • superlog
  • 152. log • xlog • simplelog • superlog • pylogger
  • 153. log • xlog • simplelog • superlog • pylogger • prettylog
  • 154. log • logging (not stdlib!) • xlog • simplelog • superlog • pylogger • prettylog
  • 155. log • logging (not stdlib!) • xlog • logmanager • simplelog • superlog • pylogger • prettylog
  • 156. log • logging (not stdlib!) • xlog • logmanager • simplelog • logging (stdlib!) • superlog • pylogger • prettylog
  • 157. log • logging (not stdlib!) • xlog • logmanager • simplelog • logging (stdlib!) • superlog • logwrangler • pylogger • prettylog
  • 158. log • logging (not stdlib!) • xlog • logmanager • simplelog • logging (stdlib!) • superlog • logwrangler • pylogger • agi.log • prettylog
  • 159. tl;dr • Hungarian notation BAD • PEP-8 GOOD • Unclean code BAD • Use existing tools GOOD • Weird build breaking BAD
  • 161. Things to Explore • Clean Code: http://ASIN.cc/~11GQo
  • 162. Things to Explore • Clean Code: http://ASIN.cc/~11GQo • http://code.google.com/p/snake-guice/
  • 163. Things to Explore • Clean Code: http://ASIN.cc/~11GQo • http://code.google.com/p/snake-guice/ • http://www.voidspace.org.uk/python/mock/
  • 164.
  • 165.
  • 166. Links • Twitter: @mpirnat • Blog: http://mike.pirnat.com • Inspiration: • Gary Numan: http://youtu.be/Oj2PR9CI9xY • Joy Division: http://youtu.be/5AqeqAQ1ILI • Hyperbole and a Half: http://hyperboleandahalf.blogspot.com/2010/06/ this-is-why-ill-never-be-adult.html

Notes de l'éditeur

  1. Hi, I’m Mike. I’ve been working with Python for 11 years and one week, and during that time [SWITCH]\n
  2. ...I’ve had THIS experience pretty regularly. I’ve come to think that one of the best ways to know what good code looks like is to know what it DOESN’T look like. So today we’re going to take a tour of some of my code sins and try to save you some of that toe-stubbing, face-palming pain.\n
  3. I write most of my code for work; for this talk, most of the examples I’m going to show have been abstracted down from actual code or distilled from memory of past habits so that I can have permission to use them; hopefully this helps us focus on actual insights rather than wasting your time with reams of gnarly source code. I’ve got a lot of stuff to talk about and I’m going to move fast to get through it.\n
  4. When I started with Python, our shop was religious about Hungarian notation\n
  5. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  6. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  7. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  8. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  9. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  10. That’s where you prefix your variable names to indicate what sort of things they are. Meant to be a guidepost for the future, hint at intentions, set expectations. At first, this seems quite reasonable.\n
  11. But this quickly leads to silliness - a list of dictionaries, useless “hey look an object” notation, abbreviations, things that don’t know what they want, and frightening ambiguity\n
  12. But this quickly leads to silliness - a list of dictionaries, useless “hey look an object” notation, abbreviations, things that don’t know what they want, and frightening ambiguity\n
  13. But this quickly leads to silliness - a list of dictionaries, useless “hey look an object” notation, abbreviations, things that don’t know what they want, and frightening ambiguity\n
  14. But this quickly leads to silliness - a list of dictionaries, useless “hey look an object” notation, abbreviations, things that don’t know what they want, and frightening ambiguity\n
  15. But this quickly leads to silliness - a list of dictionaries, useless “hey look an object” notation, abbreviations, things that don’t know what they want, and frightening ambiguity\n
  16. From there it’s a short trip to outright lies -- a number that isn’t a number, fake false values in booleans, and, if something goes wrong, AttributeErrors when you get a None where you don’t expect to.\n
  17. From there it’s a short trip to outright lies -- a number that isn’t a number, fake false values in booleans, and, if something goes wrong, AttributeErrors when you get a None where you don’t expect to.\n
  18. From there it’s a short trip to outright lies -- a number that isn’t a number, fake false values in booleans, and, if something goes wrong, AttributeErrors when you get a None where you don’t expect to.\n
  19. From there it’s a short trip to outright lies -- a number that isn’t a number, fake false values in booleans, and, if something goes wrong, AttributeErrors when you get a None where you don’t expect to.\n
  20. It was also a time before I really knew or cared about community standards and good style\n
  21. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  22. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  23. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  24. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  25. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  26. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  27. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  28. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  29. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  30. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  31. I picked up bad habits from the existing codebase and perpetuated them for much longer than I should have. [quick remarks about each]\n
  32. There’s an appealing foolishness to lining things up; it may look nice at first but you’re quickly locked into a hell-spiral of adjusting the interior whitespace as code changes, and annoying artifacts are inevitably left behind.\n
  33. There’s an appealing foolishness to lining things up; it may look nice at first but you’re quickly locked into a hell-spiral of adjusting the interior whitespace as code changes, and annoying artifacts are inevitably left behind.\n
  34. Short variable names mean you can fit more code into an 80-character line, so they must be good, right?\n
  35. Wrong. Inscrutable variable names lead to code that’s hard to read and that people are afraid to clean up.\n
  36. I can only take credit for finding this one--check out how defining everything as single characters lets us write lines like this:\n
  37. I’ve become increasingly allergic to lambdas. Even when they’re simple, they’re hard to read...\n
  38. And they’ll get complicated in a hurry. (Sadly, I couldn’t find the triple-lambda that I remember writing many moons ago...)\n
  39. And they’ll get complicated in a hurry. (Sadly, I couldn’t find the triple-lambda that I remember writing many moons ago...)\n
  40. Worse, the desire for brevity drives us to use inscrutable single-character variable names!\nIn the interest of optimizing for our fellow humans, give your functions descriptive names instead of packing as much cleverness as possible into a single line. Your functions deserve it, and your colleagues deserve it.\n
  41. Worse, the desire for brevity drives us to use inscrutable single-character variable names!\nIn the interest of optimizing for our fellow humans, give your functions descriptive names instead of packing as much cleverness as possible into a single line. Your functions deserve it, and your colleagues deserve it.\n
  42. Worse, the desire for brevity drives us to use inscrutable single-character variable names!\nIn the interest of optimizing for our fellow humans, give your functions descriptive names instead of packing as much cleverness as possible into a single line. Your functions deserve it, and your colleagues deserve it.\n
  43. Worse, the desire for brevity drives us to use inscrutable single-character variable names!\nIn the interest of optimizing for our fellow humans, give your functions descriptive names instead of packing as much cleverness as possible into a single line. Your functions deserve it, and your colleagues deserve it.\n
  44. List comprehensions are really powerful, and beautiful until you start filling them full of crap.\n
  45. Nesting list comprehensions adds significant cognitive load for most mortals. First one’s not too bad; second one starts to scare the junior devs who aren’t friends with zip yet; but this last one is just freaky.\n
  46. Nesting list comprehensions adds significant cognitive load for most mortals. First one’s not too bad; second one starts to scare the junior devs who aren’t friends with zip yet; but this last one is just freaky.\n
  47. Nesting list comprehensions adds significant cognitive load for most mortals. First one’s not too bad; second one starts to scare the junior devs who aren’t friends with zip yet; but this last one is just freaky.\n
  48. This is taken from a personal project that scrapes my LiveJournal friends page and converts it into an RSS feed. Minus some formatting issues, it’s not terribly unclean, but so much space gets taken by the setup of RSSItem that it’s easy to lose track of the list comprehension itself.\n
  49. Doesn’t this look cleaner?\n
  50. This deadly pattern emerges when you aim to provide a “one stop shop” that has to deal with a lot of special cases.\n
  51. For example, swapping out functionality based on what site we’re serving. Once you start tacking on a few elifs, it’s hard to stop!\n
  52. Pull the insides of the if/elif block out into separate functions; build a map based on some key, then just choose the key, grab the function, and call it. But this can get ugly too as determining the key can be a lot of work.\n
  53. Pull the insides of the if/elif block out into separate functions; build a map based on some key, then just choose the key, grab the function, and call it. But this can get ugly too as determining the key can be a lot of work.\n
  54. Pull the insides of the if/elif block out into separate functions; build a map based on some key, then just choose the key, grab the function, and call it. But this can get ugly too as determining the key can be a lot of work.\n
  55. Pull the insides of the if/elif block out into separate functions; build a map based on some key, then just choose the key, grab the function, and call it. But this can get ugly too as determining the key can be a lot of work.\n
  56. Pull the insides of the if/elif block out into separate functions; build a map based on some key, then just choose the key, grab the function, and call it. But this can get ugly too as determining the key can be a lot of work.\n
  57. So why not externalize it entirely -- let it get chosen by the caller, who may know better than we do about those deciding factors.\n
  58. And then it’s just a quick step into Dependency Injection, where our code gets provided with what it needs.\n
  59. You can even use a DI framework like snake-guice to help wire up your objects.\n
  60. You can even use a DI framework like snake-guice to help wire up your objects.\n
  61. You can even use a DI framework like snake-guice to help wire up your objects.\n
  62. \n
  63. I didn’t actually do this one, as I had learned the lesson the hard way; instead I let this happen to a coworker who was new to Python by not making it part of his early onboarding to the language. Our app was only supposed to let users set a max of 4 reminders for an event, but...\n
  64. we’d keep accumulating more and more data in that list as the process handled more and more requests. Periodically we’d restart the app server and the problem would vanish, only to build again.\n
  65. we’d keep accumulating more and more data in that list as the process handled more and more requests. Periodically we’d restart the app server and the problem would vanish, only to build again.\n
  66. \n
  67. \n
  68. There are various ways of doing this; here’s one.\n
  69. There are various ways of doing this; here’s one.\n
  70. There are various ways of doing this; here’s one.\n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. In between Perl and Python, I was briefly a Java guy, and while my exposure was brief, the effects were lasting.\n
  78. I wrote an invitations app years ago, all of the model classes were very protective of their internals, but without much benefit. Same basic get/set pain repeated ~40 times in that class. And the calling syntax gets cumbersome and ugly.\n
  79. I wrote an invitations app years ago, all of the model classes were very protective of their internals, but without much benefit. Same basic get/set pain repeated ~40 times in that class. And the calling syntax gets cumbersome and ugly.\n
  80. I wrote an invitations app years ago, all of the model classes were very protective of their internals, but without much benefit. Same basic get/set pain repeated ~40 times in that class. And the calling syntax gets cumbersome and ugly.\n
  81. I wrote an invitations app years ago, all of the model classes were very protective of their internals, but without much benefit. Same basic get/set pain repeated ~40 times in that class. And the calling syntax gets cumbersome and ugly.\n
  82. I wrote an invitations app years ago, all of the model classes were very protective of their internals, but without much benefit. Same basic get/set pain repeated ~40 times in that class. And the calling syntax gets cumbersome and ugly.\n
  83. Much nicer to just access attributes directly and use modern property syntax if we really need special logic\n
  84. Much nicer to just access attributes directly and use modern property syntax if we really need special logic\n
  85. Much nicer to just access attributes directly and use modern property syntax if we really need special logic\n
  86. That invite stuff that had all those getters and setters also had wicked __init__s that would take 40 or more parameters and then call all those getters and setters. So in my next big project I thought I’d learn from that and do some magic.\n
  87. Was building a backend for a reminder system that was going to use Data Transfer Objects to speak XMLRPC with our web app front end, and needed to make sure that all the data attributes made it across the wire...\n
  88. So with a little __new__ magic, we transform all of the class attributes into instance attributes so that xmlrpclib will include our Nones\n
  89. So with a little __new__ magic, we transform all of the class attributes into instance attributes so that xmlrpclib will include our Nones\n
  90. So with a little __new__ magic, we transform all of the class attributes into instance attributes so that xmlrpclib will include our Nones\n
  91. Then, remembering those brutal inits, it seems reasonable to automatically set everything that got passed in our keyword args--we just saved a ton of typing, right? And maybe only allow it to be set if we declared up front that it’s something our DTO *should* be getting.\n
  92. Then, remembering those brutal inits, it seems reasonable to automatically set everything that got passed in our keyword args--we just saved a ton of typing, right? And maybe only allow it to be set if we declared up front that it’s something our DTO *should* be getting.\n
  93. Then, remembering those brutal inits, it seems reasonable to automatically set everything that got passed in our keyword args--we just saved a ton of typing, right? And maybe only allow it to be set if we declared up front that it’s something our DTO *should* be getting.\n
  94. Then, remembering those brutal inits, it seems reasonable to automatically set everything that got passed in our keyword args--we just saved a ton of typing, right? And maybe only allow it to be set if we declared up front that it’s something our DTO *should* be getting.\n
  95. Then, remembering those brutal inits, it seems reasonable to automatically set everything that got passed in our keyword args--we just saved a ton of typing, right? And maybe only allow it to be set if we declared up front that it’s something our DTO *should* be getting.\n
  96. Eventually I wanted to be able to declare mutables -- like lists and dictionaries -- in my class and have fresh, unshared copies of them for each instance that’s created, and thus was born MutantDataObject.\n
  97. This init-less convenience becomes quite popular and used in a number of systems. Then one day we get a nasty surprise from a system that uses them heavily--it’s dog slow.\n
  98. \n
  99. The lookup and copying of mutables blows MutantDataObject’s startup time through the roof. So how do we fix it?\n
  100. Our replacement DataObject is now based on a metaclass that knows how to find and copy those mutables more efficiently, and it also takes advantage of iterators\n
  101. Our replacement DataObject is now based on a metaclass that knows how to find and copy those mutables more efficiently, and it also takes advantage of iterators\n
  102. Our replacement DataObject is now based on a metaclass that knows how to find and copy those mutables more efficiently, and it also takes advantage of iterators\n
  103. Our replacement DataObject is now based on a metaclass that knows how to find and copy those mutables more efficiently, and it also takes advantage of iterators\n
  104. Our replacement DataObject is now based on a metaclass that knows how to find and copy those mutables more efficiently, and it also takes advantage of iterators\n
  105. And here’s what the metaclass looks like--don’t look at it too hard now, I’ll have the slides up soon.\n
  106. The important thing is that it saves a lot of time!\n
  107. A “god object” or “god method” is one that has accumulated too many responsibilities. It may start out subtle at first, but they’re easy to recognize before long...\n
  108. \n
  109. \n
  110. \n
  111. \n
  112. \n
  113. \n
  114. Favor small functions and small classes with as few responsibilities as possible. Strive to do no work in the init. Break up the wanna-be gods before they get out of hand.\n
  115. This is one of my deepest regrets.\n
  116. It started so simply, as a favor to another developer, who needed easy access to some objects and didn’t want to pass them around everywhere... Basically it’s a module with module-level globals that get repopulated by the app server on every request\n
  117. If you import it, you can talk to those globals. You can do this at any level, from the lofty heights that first see the request, to the deepest, darkest depths.\n
  118. Try not to do this.\n
  119. Try not to do this.\n
  120. Try not to do this.\n
  121. Try not to do this.\n
  122. Try not to do this.\n
  123. Having easy access to all that awesome global state also encourages you to break the Law of Demeter, which basically says that you should only interact with the things you know about and not reach through to “friends of friends” or “strangers”. But it’s damn convenient!\n
  124. Here are just a few of countless examples of my crimes -- reaching across multiple objects to call a method, through dictionaries, into single-underscore “internals”, into the contents of a list...\n
  125. Here are just a few of countless examples of my crimes -- reaching across multiple objects to call a method, through dictionaries, into single-underscore “internals”, into the contents of a list...\n
  126. Here are just a few of countless examples of my crimes -- reaching across multiple objects to call a method, through dictionaries, into single-underscore “internals”, into the contents of a list...\n
  127. Here are just a few of countless examples of my crimes -- reaching across multiple objects to call a method, through dictionaries, into single-underscore “internals”, into the contents of a list...\n
  128. \n
  129. The Diaper Pattern is a popular term in my office for the antipattern of catching and silencing exceptions\n
  130. A lot of us have young kids, so we naturally call this a “diaper” because it tries to catch all the crap. Unfortunately, it doesn’t always work so well, and we can have “blowouts” -- if anything had gone wrong getting foo, our call to do_something_important will die!\n\nThe farther away the diaper is from the code that it breaks, the harder it is to find--in a large codebase, it can be buried quite a ways down.\n
  131. A lot of us have young kids, so we naturally call this a “diaper” because it tries to catch all the crap. Unfortunately, it doesn’t always work so well, and we can have “blowouts” -- if anything had gone wrong getting foo, our call to do_something_important will die!\n\nThe farther away the diaper is from the code that it breaks, the harder it is to find--in a large codebase, it can be buried quite a ways down.\n
  132. A lot of us have young kids, so we naturally call this a “diaper” because it tries to catch all the crap. Unfortunately, it doesn’t always work so well, and we can have “blowouts” -- if anything had gone wrong getting foo, our call to do_something_important will die!\n\nThe farther away the diaper is from the code that it breaks, the harder it is to find--in a large codebase, it can be buried quite a ways down.\n
  133. We’re better off when exceptions bubble up, then at least you know where they came from. Errors, as they say, should not pass silently.\nOK to diaper at system boundaries - eg, around a web service or xmlrpc backend where you always want reasonable output\n
  134. Coming into the home stretch now, let’s talk about some fun ways to make people angry. We’re going to meet a thing called DUCK PUNCHER.\n
  135. If the test breaks, the teardown never happens, and whatever was “punched” stays punched. Maybe this hurts you immediately -- if “open” got punched, nose will die because it can’t get at the stack trace -- or maybe it hurts you thirty directories away when something that wasn’t changed starts failing.\n
  136. The immediate fix is to make sure we have a try/finally block.\n\n
  137. Really folks should just learn to use Mock's @patch decorator or context manager and not reinvent the wheel\n
  138. \n
  139. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  140. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  141. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  142. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  143. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  144. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  145. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  146. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  147. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  148. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  149. Once upon a time in the bad old days of Python 1.4, we needed to log.\n
  150. \n
  151. \n
  152. \n
  153. \n
  154. \n
  155. \n
  156. \n