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,507