Beautiful Python
Alicia Pérez
www.stylesage.co
About me
Accent?
We are flying!
>>> import antigravity
Zen of Python
>>> 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!
>>>
Strings
colors = ['red', 'blue', 'green', 'yellow']
result = ''
for s in colors:
result += s
# result: 'redbluegreenyellow'
colors = ['red', 'blue', 'green', 'yellow']
result = ''.join(colors)
# result: 'redbluegreenyellow'
Good
Bad
Create a string from a list
Strings
colors = ['red', 'blue', 'green', 'yellow']
result = ''
for s in colors:
result += s + ','
result = result[:-1]
# result: 'red,blue,green,yellow'
colors = ['red', 'blue', 'green', 'yellow']
result = ','.join(colors)
# result: 'red,blue,green,yellow'
Good
Bad
Create a string from a list joined by comma
Strings
my_very_big_string = """For a long time I used to go to bed early. Sometimes,
when I had put out my candle, my eyes would close so quickly that I had not even
time to say "Im going to sleep."""
my_very_big_string = (
"For a long time I used to go to bed early. Sometimes, "
"when I had put out my candle, my eyes would close so quickly "
"that I had not even time to say: Im going to sleep."
)
Good
Bad
Multi-line
Strings
contains?
stylesage = 'Fashion meets Big Data'
if stylesage.contains('Fashion'):
pass
Bad
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'contains'
Strings
stylesage = 'Fashion meets Big Data'
if stylesage.find("Fashion") != -1:
pass
Good
Bad
stylesage = 'Fashion meets Big Data'
if 'Fashion' in stylesage:
pass
contains in
Strings
format
# OR
'%s %s' % ('one', 'two') '%d %d' % (1, 2)
# output: 'one two' # output: '1 2'
New
Old
# OR
'{} {}'.format('one', 'two') '{} {}'.format(1, 2)
# output: 'one two' # output: '1 2'
Strings
Basic format
# OR
'%s %s' % ('one', 'two') '%d %d' % (1, 2)
# output: one two # output: 1 2
New
Old
# OR
'{} {}'.format('one', 'two') '{} {}'.format(1, 2)
# output: one two # output: one two
# explicit positional index
'{1} {0}'.format('one', 'two')
# output: two one
Strings
Padding and aligning
# OR
'%10s' % ('test',) '%-10s' % ('test',)
# output: ' test' # output: 'test '
New
Old
# OR
'{:>10}'.format('test') '{:10}'.format('test')
# output: ' test' # output: 'test '
Strings
Padding and aligning
# OR
'%10s' % ('test',) '%-10s' % ('test',)
# output: ' test' # output: 'test '
New
Old
# OR
'{:>10}'.format('test') '{:10}'.format('test')
# output: ' test' # output: 'test '
# choose the padding character
'{:_<10}'.format('test')
# output: 'test______'
Strings
Padding and aligning
# OR
'%10s' % ('test',) '%-10s' % ('test',)
# output: ' test' # output: 'test '
New
Old
# OR
'{:>10}'.format('test') '{:10}'.format('test')
# output: ' test' # output: 'test '
# choose the padding character
'{:_<10}'.format('test')
# output: 'test______'
# center align value
'{:_^10}'.format('test')
# output: '___test___'
Strings
Padding and aligning
New
# This operations are not available with old-style formatting
'{:=5d}'.format((- 23))
# output: '- 23'
'{:%Y-%m-%d %H:%M}'.format(datetime(2001, 2, 3, 4, 5))
# output: '2001-02-03 04:05'
person = {'first': 'Jean-Luc', 'last': 'Picard'}
'{p[first]} {p[last]}'.format(p=person)
# output: 'Jean-Luc Picard'
data = [4, 8, 15, 16, 23, 42]
'{d[4]} {d[5]}'.format(d=data)
# output: '23 42'
tu = (12,45,22222,103,6)
print '{0} {2} {1} {2} {3} {2} {4} {2}'.format(*tu)
# output: '12 22222 45 22222 103 22222 6 22222'
Booleans
if len(items) != 0:
pass
if name != "":
pass
Good
Bad
if items:
pass
if name:
pass
Check if variable equals a constant
False | True |
---|---|
False (== 0) | True (== 1) |
"" (empty string) | any string but "" (" ", "anything") |
0, 0.0 | any number but 0 (1, 0.1, -1, 3.14) |
[], (), {}, set() | any non-empty container ([0], (None,),['']) |
None | almost any object that's not explicitly False |
Booleans
Check if variable equals a constant
Booleans
if x >= start and x <= end:
# do stuff
Good
Bad
if start <= x <= end:
# do stuff
Chained comparisons
Comparisons
values = range(10)
i = 0
found = False
while not found and i < len(values):
found = not values[i] % 2
i += 1
Good
Bad
values = range(10)
anyPair = any([not value % 2 for value in values])
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# [True, False, True, False, True, False, True, False, True, False]
# anyPair: True
Any
Comparisons
values = range(10)
i = 0
allPairs = True
while allPairs and i < len(values):
allPairs = not values[i] % 2
i += 1
Good
Bad
values = range(10)
allPairs = all([not value % 2 for value in values])
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# [True, False, True, False, True, False, True, False, True, False]
# output: True
All
Comparisons
if x > 10:
result = 'Yes'
else:
result = 'No'
Good
Bad
result = 'Yes' if x > 10 else 'No'
Ternary operator
Lists
mylist = ['yellow', 'red', 'blue', 'green', 'black']
mylist[1:4]
# output: ['red', 'blue', 'green']
mylist[2:]
# output: ['blue', 'green', 'black']
mylist[:2]
# output: ['yellow', 'red']
mylist[-1]
# output: 'black'
mylist[1:-1]
# output: ['red', 'blue', 'green']
Manipulation
Lists
a = [3, 4, 5]
for i in range(len(a)):
a[i] += 3
Good
Bad
a = [3, 4, 5]
a = [i + 3 for i in a]
# Or:
a = map(lambda i: i + 3, a)
List comprehensions
Lists
a = [3, 4, 5]
b = []
for i in a:
if i > 4:
b.append(i)
Good
Bad
a = [3, 4, 5]
b = [i for i in a if i > 4]
# Or:
b = filter(lambda x: x > 4, a)
Filter
Lists
a = [3, 4, 5]
i = 0
for item in items:
print i, item
i += 1
Good
Bad
a = [3, 4, 5]
for i, item in enumerate(a):
print i, item
Enumerate
Generators
def firstn(n):
num, nums = 0, []
while num < n:
nums.append(num)
num += 1
return nums
sum_of_first_n = sum(firstn(1000000))
Bad
def firstn(n):
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
Good
Not in memory list
Generators
square = [i*i for i in xrange(1000000)]
List
square = (i*i for i in irange(1000000))
Generator
Generator vs list
Tuples
temp = a
a = b
b = temp
b, a = a, b
Good
Bad
Swap values
Tuples
l =['David', 'Pythonista', '+1-514-555-1234']
name, title, phone = l
# name: 'David'
# title: 'Pythonista'
# phone: '+1-514-555-1234'
Unpacking
Tuples
List of tuples
names = ['John', 'Eric', 'Terry']
surnames = ['Cleese', 'Idle', 'Gilliam']
people = []
for i in range(len(names)):
people.append((names[i], surnames[i]))
print(people)
# output: [('John', 'Cleese'), ('Eric', 'Idle'), ('Terry', 'Gilliam')]
names = ['John', 'Eric', 'Terry']
surnames = ['Cleese', 'Idle', 'Gilliam']
zip(names, surnames)
# output: [('John', 'Cleese'), ('Eric', 'Idle'), ('Terry', 'Gilliam')]
Good
Bad
Dictionaries
Good
Bad
if d.has_key(key):
print(d[key])
if key in d:
print(d[key])
Dictionaries
Good
Bad
for key in d.keys():
print key
for key in d:
print key
Except
for key in d.keys():
d[str(key)] = d[key]
Dictionaries
navs = {}
for (portfolio, equity, position) in data:
if portfolio not in navs:
navs[portfolio] = 0
navs[portfolio] += position * prices[equity]
Good
Bad
for (portfolio, equity, position) in data:
navs[portfolio] = (navs.get(portfolio, 0)
+ position * prices[equity])
navs = {}
for (portfolio, equity, position) in data:
if portfolio not in navs:
navs[portfolio] = 0
navs[portfolio] += position * prices[equity]
for (portfolio, equity, position) in data:
navs.setdefault(portfolio, 0)
navs[portfolio] += position * prices[equity]
Dictionaries
Good
Bad
navs = {}
for (portfolio, equity, position) in data:
if portfolio not in navs:
navs[portfolio] = 0
navs[portfolio] += position * prices[equity]
navs = defaultdict(int)
for (portfolio, equity, position) in data:
navs[portfolio] += position * prices[equity]
Dictionaries
Good
Bad
given = ['John', 'Eric', 'Terry', 'Michael']
family = ['Cleese', 'Idle', 'Gilliam', 'Palin']
pythons = {}
for i in range(len(given)):
pythons[given[i]] = family[i]
given = ['John', 'Eric', 'Terry', 'Michael']
family = ['Cleese', 'Idle', 'Gilliam', 'Palin']
pythons = dict(zip(given, family))
Dictionaries
Good
Bad
values = range(4)
res = {}
for numb in values:
res[numb] = chr(65 + numb)
res = {i : chr(65+i) for i in range(4)}
Dictionaries
Good
Bad
{0 : 'A', 1 : 'B', 2 : 'C', 3 : 'D'}
What!?!?!
Bonus!
Interactive "_"
>>> 1 + 1
2
>>> _
2
>>> import math
>>> math.pi / 3
1.0471975511965976
>>> angle = _
>>> math.cos(angle)
0.50000000000000011
>>> _
0.50000000000000011
filename = 'foobar.txt'
basename, _, ext = filename.rpartition('.')
Variable "_"
from __future__
>>> from __future__ import braces
File "<stdin>", line 1
SyntaxError: not a chance
from __future__ import division
print 8/7 # prints 1.1428571428571428
print 8//7 # prints 1
We want you!
http://stylesage.co/careers
Questions?
Opinions?
PyCon16 - Beautiful Python
By aliciapj
PyCon16 - Beautiful Python
PyConES 2017
- 2,759