2. Daniel Greenfeld
@pydanny
I do cartwheels
Daniel Greenfeld (@pydanny)
Pythonista at Cartwheel
Djangonaut at Revsys
Co-lead of Django Packages &
Open Comparison
Learned Python at NASA
Fiancé of Audrey Roy
3. Daniel Greenfeld
@pydanny
Talk Format
Each section will have three components:
• At least one ‘Python Worst Practice’ slide
• At least one ‘Fixed Python Practice’ slide
• A side-by-side comparison slide
These slides are already online!
4. Daniel Greenfeld
@pydanny
Warning!
Don’t use the
‘Python Worst Practices’
examples in your code*
You may be hunted down and killed
*Sometimes these are caught by
various code checking tools
5. Daniel Greenfeld
@pydanny
Advice!
Do consider using the
‘Fixed Python Practices’
examples in your code
You may get complimented
7. Daniel Greenfeld
@pydanny
Python Worst Practice
object = MyObject()
map = Map()
zip = 90213 # common US developer mistake
id = 34 # I still fight this one
I’m guilty
• I still use ‘id’ when I shouldn’t
• I admit I have a problem
• That gives me license to pick on others
8. Daniel Greenfeld
@pydanny
Fixed Python Practice
obj = MyObject() # necessary abbreviation
object_ = MyObject() # Underscore so we don't overwrite
map_obj = Map() # combine name w/necessary abbreviation
map_ = Map()
zip_code = 90213 # Explicit name with US focus
postal_code = 90213 # i18n explicit name
zip_ = 90213
pk = 34 # pk is often synonymous with id
id_ = 34
9. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
object = MyObject() obj = MyObject() # Use a necessary abbreviation
map = Map() object_ = MyObject() # Use underscore so we don't overwrite
zip = 90213 # common US developer mistake
id = 34 # I still fight this one map_obj = Map() # combine name with necessary abbreviation
map_ = Map()
zip_code = 90213 # Explicit name with US focus
postal_code = 90213 # International explicit name
zip_ = 90213
pk = 34 # pk is often synonymous with id
id_ = 34
10. Daniel Greenfeld
@pydanny
Python Worst Practice
Flipping the booleans
true = 0
false = 1
True = False
Usually done to support an API
11. Daniel Greenfeld
@pydanny
This sort of API
def crazy_posting_api(value):
""" If a value is supplied
successfully return ‘0’.
Otherwise return ‘1’
"""
if value:
return 0
return 1
12. Daniel Greenfeld
@pydanny
Fixed Python Practice
class CrazyApiConsumer(object):
def __init__(self, value):
self.value = value
def post(self):
# fix booleans in/around the return statement
response = crazy_posting_api(self.value)
return not bool(response)
cac1 = CrazyApiConsumer("hello")
print(cac1.post())
cac2 = CrazyApiConsumer("")
print(cac2.post())
13. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
true = 0 class CrazyApiConsumer(object):
false = 1
True = False def __init__(self, value):
self.value = value
def post(self):
# fix booleans in/around the return statement
response = crazy_posting_api(self.value)
return not bool(response)
cac1 = CrazyApiConsumer("hello")
print(cac1.post())
cac2 = CrazyApiConsumer("")
print(cac2.post())
14. Daniel Greenfeld
@pydanny
Python Worst Practice
Identifying variable types with prefixes
strColor = "green"
boolActive = False
intPythonYears = 20
dtPythonFirstUsed = "04/20/2011"
Mixing case doesn’t help either
15. Daniel Greenfeld
@pydanny
Python Worst Practice
Conserving pixels by removing the vowels
clr = "green"
ctv = False
pythnYrs = 20
pthnFrstSd = "04/20/2011"
16. Daniel Greenfeld
@pydanny
Python Worst Practice
c = "green"
a = False
p = 20
t = "04/20/2011"
17. Daniel Greenfeld
@pydanny
Fixed Python Practice
color = "green"
active = False
python_years = 20
python_first_used = "04/20/2011"
Python assumes we are all consenting adults
• Infer from naming schemes the type/purpose
• Don’t be constrained by type
18. Daniel Greenfeld
@pydanny
Fixed Python Practice
color = "green"
active = False
python_years = 20
python_first_used = "04/20/2011"
The pixel shortage is over.
Use reasonably long variable names.
19. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
c = "green"
a = False color = "green"
p = 20
t = "04/20/2011"
active = False
python_years = 20
python_first_used = "04/20/2011"
clr = "green"
ctv = False
pythnYrs = 20
pthnFrstSd = "04/20/2011"
strColor = "green"
boolActive = False
intPythonYears = 20
dtPythonFirstUsed = "04/20/2011"
20. Daniel Greenfeld
@pydanny
Python Worst Practice
Don’t use enumerate
foo = [1, 2, 3]
for i, item in zip(range(len(foo)), foo):
print i, item
21. Daniel Greenfeld
@pydanny
Fixed Python Practice
Use enumerate
foo = [1, 2, 3]
for i, item in enumerate(foo):
print i, item
• Memorize the Python built-ins
• Makes your code easier to read
• Proven code
22. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
foo = [1, 2, 3] foo = [1, 2, 3]
zip(range(len(foo)), foo): for i, item in enumerate(foo):
print i, item print i, item
24. Daniel Greenfeld
@pydanny
Python Worst Practice
Present using
High Contrast
Easy-to-read fonts
All devices off
All programs off
25. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
Present using
Present using
Different Fonts
High Contrast
Dark Text
Easy-to-read fonts
Dire Backgr#nds All devices off
All programs off
27. Daniel Greenfeld
@pydanny
Python Worst Practice
Implementing Java-style getters and setters
import logging
log = logging.getLogger()
class JavaStyle:
""" Quiz: what else am I doing wrong here? """
def __init__(self):
self.name = ""
def get_name(self):
return self.name
def set_name(self, name):
log.debug("Setting the name to %s" % name)
if isinstance(name, str):
self.name = name
else:
raise TypeError()
if __name__ == "__main__":
j = JavaStyle()
j.set_name("pydanny did this back in 2006!")
print(j.get_name())
28. Daniel Greenfeld
@pydanny
Fixed Python Practice
Python properties!
import logging
log = logging.getLogger()
class PythonStyle(object):
def __init__(self):
self._name = "" Accessor
@property
def name(self):
return self._name
Mutator
@name.setter
def name(self, value):
""" Because name is probably a string we'll assume that we can
infer the type from the variable name"""
log.debug("Setting the name to %s" % value)
self._name = value
if __name__ == "__main__":
p = PythonStyle()
p.name = "pydanny doing it the right way"
print(p.name)
29. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
import logging import logging
log = logging.getLogger() log = logging.getLogger()
class JavaStyle: class PythonStyle(object):
""" Quiz: what else am I doing wrong here? """
def __init__(self):
def __init__(self): self._name = ""
self.name = ""
@property
def get_name(self): def name(self):
return self.name return self._name
def set_name(self, name): @name.setter
log.debug("Setting the name to %s" % name)
def name(self, value):
if isinstance(name, str): """ Because name is probably a string we'll assume that we can
self.name = name infer the type from the variable name"""
else: log.debug("Setting the name to %s" % value)
raise TypeError() self._name = value
if __name__ == "__main__": if __name__ == "__main__":
j = JavaStyle() p = PythonStyle()
j.set_name("pydanny did thisp.namein 2006!") doing it the right way"
back = "pydanny
print(j.get_name()) print(p.name)
30. Daniel Greenfeld
@pydanny
Python Worst Practice
Using property setters as action methods!
class WebService(object):
@property
def connect(self):
self.proxy = xmlrpc.Server("http://service.xml")
if __name__ == '__main__':
ws = WebService()
ws.connect
A.K.A.Trying to make your Python code look like Ruby
31. Daniel Greenfeld
@pydanny
Fixed Python Practice
Methods please!
class WebService(object):
def connect(self):
self.proxy = xmlrpc.Server("http://service.xml")
if __name__ == '__main__':
ws = WebService()
ws.connect()
32. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
class WebService(object): class WebService(object):
@property
def connect(self): def connect(self):
self.proxy = xmlrpc.Server("http://service.xml")
self.proxy = xmlrpc.Server("http://service.xml")
if __name__ == '__main__':
if __name__ == '__main__':
ws = WebService()
ws.connect
ws = WebService()
ws.connect()
34. Daniel Greenfeld
@pydanny
Python Worst Practice
Passing Generic Exceptions silently
try:
do_akshun(value)
except:
pass
• Ignorance is not bliss
• You have no idea what your system is doing
• Arguably better to not have this in your code
35. Daniel Greenfeld
@pydanny
Fixed Python Practice
Use specific exceptions and/or logging
class AkshunDoesNotDo(Exception):
""" Custom exceptions makes for maintainable code """
pass
try:
do_akshun(value)
except AttributeError as e:
log.info("Can I get attribution for these slides?")
do_bakup_akshun(vlue)
except Exception as e:
log.debug(str(e))
raise AkshunDoesNotDo(e)
36. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
try: class AkshunDoesNotDo(Exception):
do_akshun(value) """ Custom exceptions makes for maintainable code """
except: pass
pass
try:
do_akshun(value)
except AttributeError as e:
log.info("Can I get attribution for these slides?")
do_bakup_akshun(vlue)
except Exception as e:
log.debug(str(e))
raise AkshunDoesNotDo(e)
38. Daniel Greenfeld
@pydanny
Python Worst Practice
Using exec for dynamic imports
imports = "from {0} import {1}".format("random", "randrange")
exec(imports)
print(randrange(10))
• Hard to debug
• Security risk
• Sets bad precedents
39. Daniel Greenfeld
@pydanny
Fixed Python Practice
Using importlib for dynamic imports
import importlib
funstuff = importlib.import_module('random')
print(funstuff.randrange(10))
• importlib is in the standard library
• Really explicit
• Direct tie into the Python machinery
40. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
imports = "from {0} import {1}".format("random", "randrange")
import importlib
exec(imports) funstuff = importlib.import_module('random')
print(randrange(10)) print(funstuff.randrange(10))
41. Daniel Greenfeld
@pydanny
Python Worst Practice
Generally using lambdas
swap = lambda a, x, y: lambda f = a.__setitem__: (f(x, (a[x], a[y])), f(y, a[x][0]), f(x, a[x][1]))()
• Too many characters on one line
• Lambdas by design does not have docstrings
• Does not necessarily mean less characters
• I can’t get this sample to work!
42. Daniel Greenfeld
@pydanny
Fixed Python Practice
def swap(a, x, y):
""" Swap two position values in a list """
a[x],a[y] = a[y],a[x]
• Doc strings that show up nicely in help/Sphinx
• Easier to read
• In Python, functions are first class objects
• Whenever possible avoid using lambdas
43. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
swap = lambda a, x, y: def swap(a, x, y):
lambda f = a.__setitem__: """ Swap two position values in a list """
(f(x, (a[x], a[y])), a[x],a[y] = a[y],a[x]
f(y, a[x][0]), f(x, a[x][1]))()
44. Daniel Greenfeld
@pydanny
Python Worst Practice
Configuring your project with XML
<pydanny-ml>
<do action="call_view">com.pydanny.nextSlide</do>
<global name="spam" value="eggs" />
</pydanny-ml>
• You can’t convince me that XML is the better way
• You are forcing me to learn a new language
45. Daniel Greenfeld
@pydanny
Fixed Python Practice ?
Use Python for configuration!
spam = "eggs"
actions = [
('call_view', 'com.pydanny.nextSlide')
]
• Is this the right way?
• This allows conditional logic
• Iterators
• i.e. “Magic Configuration”
46. Daniel Greenfeld
@pydanny
Python Worst Practice
‘Magical configuration code’
INSTALLED_APPS += [p for p in os.listdir(BASE) if os.path.isdir(p)]
MIDDLEWARE_CLASSES = [...]
def callback(arg, dirname, fnames):
if 'middleware.py' in fnames:
m = '%s.middleware' % os.path.split(dirname)[-1]
MIDDLEWARE_CLASSES.append(m)
urlpatterns = patterns('', ...)
for app in settings.INSTALLED_APPS:
if not app.startswith('django'):
p = url('^%s/' % app, include('%s.urls') % app)
urlpatterns += patterns('', p)
Ugh.
http://www.slideshare.net/jacobian/the-best-and-worst-of-django
47. Daniel Greenfeld
@pydanny
Fixed Python Practice
urlpatterns = patterns("",
PREREQ_APPS = [
# Django url(r"^$", homepage, name="home"),
"django.contrib.admin", url(r"^accounts/", include("accounts.urls")),
"django.contrib.auth", url(r"^admin/", include(admin.site.urls)),
"django.contrib.contenttypes", url(r"^about/", include("about.urls")),
"django.contrib.sessions", url(r"^profiles/", include("profiles.urls")),
"django.contrib.sites", url(r"^notices/", include("notification.urls")),
"django.contrib.messages", ...
"django.contrib.humanize", MIDDLEWARE_CLASSES = [
)
"django.contrib.flatpages", "django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
# external "django.middleware.csrf.CsrfViewMiddleware",
"notification", # must be first "django.contrib.auth.middleware.AuthenticationMiddleware",
"staticfiles", "reversion.middleware.RevisionMiddleware",
"uni_form", "django.contrib.messages.middleware.MessageMiddleware",
... ...
] ]
Explicit is better
This isn’t that much typing, is it?
then Implicit
48. Daniel Greenfeld
@pydanny
Fixed Python Practice
urlpatterns = patterns("",
PREREQ_APPS = [
# Django url(r"^$", homepage, name="home"),
"django.contrib.admin", url(r"^accounts/", include("accounts.urls")),
"django.contrib.auth", url(r"^admin/", include(admin.site.urls)),
"django.contrib.contenttypes", url(r"^about/", include("about.urls")),
"django.contrib.sessions", url(r"^profiles/", include("profiles.urls")),
"django.contrib.sites", url(r"^notices/", include("notification.urls")),
"django.contrib.messages", ...
"django.contrib.humanize", MIDDLEWARE_CLASSES = [
)
"django.contrib.flatpages", "django.middleware.common.CommonMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
# external "django.middleware.csrf.CsrfViewMiddleware",
"notification", # must be first "django.contrib.auth.middleware.AuthenticationMiddleware",
"staticfiles", "reversion.middleware.RevisionMiddleware",
"uni_form", "django.contrib.messages.middleware.MessageMiddleware",
... ...
] ]
Python’s design is predicated on the proposition that
code is more often read than written.
http://www.slideshare.net/jacobian/the-best-and-worst-of-django/44
49. Daniel Greenfeld
@pydanny
Fixed Python Practice
Use a config file
spam = "eggs"
[actions]
call_view = com.pydanny.nextSlide
Read up on config parser
http://docs.python.org/library/configparser.html
50. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
XML simple python files
logic heavy python config (.cfg) files
52. Daniel Greenfeld
@pydanny
Python Worst Practice
Bad docstrings
class Pythonista(): # Old style class!
""" This class represents a Python programmer """
def code(self):
"""Write some code """
code, inspiration = Code(), Inspiration()
for hour in Effort():
try:
code += hour + inspiraion
except CurseWorthyBug:
...
• Do really obvious objects require doc strings?
• Complex methods require more than docstrings!
53. Daniel Greenfeld
@pydanny
Fixed Python Practice
class Pythonista(object):
def code(self):
""" Writes code following these steps
1. Create a space for coding
2. Get some inspiration
3. Loop through some hours of effort
Spend a 4. Write some code
few minutes 5. Pull out hair cause of bugs
"""
documenting code = Code()
the critical inspiration = Inspiration()
for hour in Effort():
stuff, okay? try:
code += hour + inspiraion
except CurseWorthyBug:
...
54. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
class Pythonista(): # Old style class! class Pythonista(object):
""" This class represents a Python programmer """
def code(self):
def code(self): """ Writes code following these steps
"""Write some code """ 1. Create a space for coding
code, inspiration = Code(), Inspiration() 2. Get some inspiration
for hour in Effort(): 3. Loop through some hours of effort
try: 4. Write some code
code += hour + inspiraion 5. Pull out hair cause of bugs
except CurseWorthyBug: """
... code = Code()
inspiration = Inspiration()
for hour in Effort():
try:
code += hour + inspiraion
except CurseWorthyBug:
...
55. Daniel Greenfeld
@pydanny
Python Worst Practice
Using a wiki for project documentation
“Wikis are where project documentation goes to die”
Jacob Kaplan-Moss
• Generally not version controlled
• Backups? Mirrors?
• Editing via the web? Ugh.
• No pull requests - smaller group of contributors
56. Daniel Greenfeld
@pydanny
Fixed Python Practice
• Use Restructured Text
• Use Sphinx
• Host on http://readthedocs.org
57. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
58. Daniel Greenfeld
@pydanny
Python Worst Practice
>>> import that
The Anti-Zen of Python, by Daniel Greenfeld
Ugly is better than beautiful.
Implicit is better than explicit.
Complicated is better than complex.
Complex is better than simple.
Nested is better than flat.
Dense is better than sparse.
Line code counts.
Special cases are special enough to break the rules.
Although purity beats practicality.
Errors should always pass silently.
Spelchek iz fur loosers.
In the face of explicity, succumb to the temptation to guess.
There should be many ways to do it.
Because only a tiny minority of us are Dutch.
Later is the best time to fix something.
If the implementation is hard to explain, it's a good sell.
If the implementation is easy to explain, it won't take enough time to do.
Namespaces are too hard, just use import *!
http://pypi.python.org/pypi/that
59. Daniel Greenfeld
@pydanny
Fixed Python Practice
>>> import this
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Core Python
60. Daniel Greenfeld
@pydanny
Side-by-side comparison
Worst Practice Fixed Practice
>>> import this
>>> import that
The Zen of Python, by Tim Peters
The Anti-Zen of Python, by Daniel Greenfeld
Beautiful is better than ugly.
Ugly is better than beautiful.
Explicit is better than implicit.
Implicit is better than explicit.
Simple is better than complex.
Complicated is better than complex.
Complex is better than complicated.
Complex is better than simple.
Flat is better than nested.
Nested is better than flat.
Sparse is better than dense.
Dense is better than sparse.
Readability counts.
Line code counts.
Special cases aren't special enough to break the rules.
Special cases are special enough to break the rules.
Although practicality beats purity.
Although purity beats practicality.
Errors should never pass silently.
Errors should always pass silently.
Unless explicitly silenced.
Spelchek iz fur loosers.
In the face of ambiguity, refuse the temptation to guess.
In the face of explicity, succumb to the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
There should be many ways to do it.
Although that way may not be obvious at first unless you're Dutch.
Because only a tiny minority of us are Dutch.
Now is better than never.
Later is the best time to fix something.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a good sell.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it won't take enough time to do.
If the implementation is easy to explain, it may be a good idea.
Namespaces are too hard, just use import *!
Namespaces are one honking great idea -- let's do more of those!