The Naming of Ducks:
Where Dynamic Types Meet
Smart Conventions
@brandon_rhodes
The Ides of March
PyCon 2013
Pythonic
evolves
tweet.png

Background to that tweet:

PEP-8 is where
the Python community
has enshrined its general
graphic-design habits
“Limit all lines to a maximum
of 79 characters.” — PEP-8
“Anything from 45 to 75 characters is
widely regarded as a satisfactory length
of line for a single-column page…
The 66-character line
(counting both letters and spaces)
is widely regarded as ideal.”
— Robert Bringhurst in
The Elements of Typographic Style
PEP-8 might have been called
The Elements of Pythonic Style
So how do we create beauty
within a finite page width?

What Carl and I both discovered:

# Ugly
page.canvas.drawString(x * em, y * lineheight,
                       message)

# Better
page.canvas.drawString(
    x * em, y * lineheight, message)

# Most general
page.canvas.drawString(
    x * em,
    y * lineheight,
    message,
    )

Q: Why the trailing comma?

# Most general
page.canvas.drawString(
    x * em,
    y * lineheight,
    message,
    )
A: The symmetry is very friendly
to version control!
 page.canvas.drawString(
     x * em,
     y * lineheight,
-    message
+    message,
+    font='Gentium'
     )

 # vs

 page.canvas.drawString(
     x * em,
     y * lineheight,
     message,
+    font='Gentium',
     )
Carl’s tweet has gotten me thinking
about how Python has been teaching
different people the same things
See the video from my
PyCon Canada 2012 talk,
A Python Æsthetic:
Beauty and Why I Python

My most popular slide:

canvas.drawString(x, y,
    'Please press {}'.format(key))

# What if you introduce a name instead?

message = 'Please press {}'.format(key)
canvas.drawString(x, y, message)
I adapted this from idea from XP
(Their goal: Destroy All Comments!)
widget.reset(True)  # forces re-draw

vs.

force_redraw = True
widget.reset(force_redraw)
So see my Canada talk
for more about how coding
involves graphic design
message = 'Please press {}'.format(key)
canvas.drawString(x, y, message)
This maneuver got me thinking
more about naming in Python
“The naming of cats is
* a difficult matter”*

— T. S. Eliot
“The naming of ducks is
 a difficult matter”
Python does no type checks,
so code relies upon duck typing
def _1(_2, _3):
    _4 = _2.get(_3)
    return _4.text
def fetch(session, url):
    response = session.get(url)
    return response.text
Names are how the Python
programmer signals intent
Names are our lifeline
Names are all we’ve got
So if the internal logic
of the Python language has been
teaching us about graphic design—
—what might it have been
teaching us about names?

Well-Factored Nouns

def main():
    parser.add_argument('page',
        help='the page to download')
    args = parser.parse_args()
    download_page(args.page)
def download_page(page):
    ? = requests.get(page)

# Whoops
# Instead:

def download_page(url):
    page = requests.get(url)
    process_page(page)
def process_page(page):
    ? = page.text

# Whoops
# Instead:

def process_page(response):
    page = response.text
    ...

Q: What have I done?

A: I have failed to refactor!

def main():
... download_page(args.page) ...

def download_page(url):
... process_page(page) ...

def process_page(response):
... page = response.text ...
Each discovery should be pushed
back into all previous code
def main():
... download_page(args.url) ...

def download_page(url):
... process_page(response) ...

def process_page(response):
... page = response.text ...
Learning that a name was imprecise
is knowledge and that knowledge
should be available everywhere

Aside:

They say Python can never work
because it lacks type checking
Does this code provide any sanity check
of argument vs parameter types?
    
    process_page(page)
    

def process_page(response):
    page = response.content
    
If you consistently refactor names
you get a kind of rudimentary
type checking for free!
    
    process_page(response)
    

def process_page(response):
    page = response.content
    

Well-Factored Nouns

Advice: you should exuberantly
refactor by pushing name changes
back into all your previous code

Relentless Verbs

Guideline:
For stuff that does
something, use a verb
Explicit is better than implicit
Plain old functions should
in almost all cases be verbs
def database():
    'Not at all clear what this does.'
    'And, it steals a good variable name!'

def create_database():
    'Much clearer; code will read smoothly.'
Exception:
Methods are often perfectly easy
to understand without verbs;
the parens help!
# Pretty clear despite brevity
document.md5()
# No conflict with local variable `md5`
md5 = document.md5()
Namespaces are
one honking great idea
let’s do more of those!
Exception:
In an object oriented language,
the word new is itself a verb
# A bit silly and redundant
obj = create_new_obj()

# Because this is just fine
obj = new_obj()
Exception:
Verbs can be too verbose for routines
that are math-like and will be combined
frequently to make larger expressions
# Would you rather type this?
d = compute_length(compute_center(boston) -
                   compute_top(mt_adams))

# Or this?
d = length(center(boston) - top(mt_adams))

Relentless Verbs

Function names should include a
verb instead of simply naming
what they build or generate

Sin of Synecdoche

In Lewis Carroll’s book
Through the Looking-Glass, Alice
meets a Knight who offers to sing
“The name of the song is
called ‘Haddocks’ Eyes.’”

~

“Oh, that’s the name of the song, is it?”
Alice said, trying to feel interested.
“No, you don’t understand,”
the Knight said, looking a little vexed.
“That’s what the name is called. The name
really is ‘The Aged Aged Man.’”

~

“Then I ought to have said,
‘That’s what the song is called’?”
Alice corrected herself.
“No, you oughtn’t: that’s another thing.
The song is called ‘Ways and Means’:
but that’s only what it’s called,
you know!”

~

“Well, what is the song, then?” said Alice,
who was by this time completely bewildered.
“I was coming to that,” the Knight said.
"The song really is ‘A sitting on a Gate’:
and the tune’s my own invention.”

Lie

The tune was a well-known one

But the Knight’s attitude
toward naming should be very
familiar to programmers!
url = 'http://lyrics.com/a-sitting-on-a-gate'
song = 'A sitting on a Gate...'
name-is-called = ‘name’
song-is-called = ‘song’

Sin of Synecdoche

Very often occurs because we
confuse a name with its referent

Sin of Synecdoche

song = 'http://lyrics.com/a-sitting-on-a-gate'
document = '/home/brandon/2013-03-pycon.rst'

Cure

Always name the part

song_url = 'http://lyrics.com/sitting-on-a-gate'
song_text = 'A sitting on a gate'

# or

url = 'http://lyrics.com/sitting-on-a-gate'
text = 'A sitting on a gate'
Feel free to allow the
context to absolve you of
the need to name the whole

PEP-8

“The X11 library uses
a leading X for all its public
functions. In Python, this style is
generally deemed unnecessary
A surrounding module or class namespace
can be responsible for naming the whole
# In medialib.py:
def fetch_songs(urls):
    ...

# In songlib.py:
def fetch(urls):
    ...
But in both cases, please call the
parameter urls so I can read your code!
# In medialib.py:
def fetch_songs(urls):
    ...

# In songlib.py:
def fetch(urls):
    ...
The logging module
both gets this right:
class Logger
class Handler
class Filter
and gets it wrong:
class LogRecord
Short unqualified names are wonderful
because they leave the caller in charge
of whether to qualify them or not.
# The caller has their choice:

from songlib import fetch
fetch(...)

# or:

import songlib
songlib.fetch(...)

I sum up

Avoid the sin of synecdoche!
Be explicit about wholes and parts
But feel free to split a whole-and-part
between the namespace and the name

Aside:

Good Python programming is often
about doing what you ought
rather than what you can

You can do this, but you oughtn’t

# foo.py
def go():
    print(value)


# program.py
import foo
if __name__ == '__main__':
    foo.value = 5
    foo.go()
Writing good Python is a
long exercise in propriety

Sin of Synecdoche

Make names as specific as possible by
naming parts in preference to wholes

Problem of Pluralization

What is the
“problem of pluralization?”
Is it the fact that someone’s popular
library took away a plural noun you
could otherwise have used?
for line in lines: ...
for url in urls: ...

for request in AAAAAARGH

No

That is not the
“problem of pluralization”

The problem has three prongs

1.

Python offers many data structures;
which one lies behind a given plural?
>>> connections
[<SQLConnection at 0xb72ff4d8>,
 <SQLConnection at 0xb72ff4d0>,
 <SQLConnection at 0xb72ff4f0>]

>>> connections
{'master': <SQLConnection at 0xb72ff4d8>,
 'backup': <SQLConnection at 0xb72ff4f0>}

2.

Plurals are not always unique in English

for box in boxes: ...
for foot in feet: ...
for brother in brethren: ...

for species in AAAAAARGH

3.

The plural is also used for counting
# Ugly:
numconnections = 8

# These must be too much typing:
connection_count = 8
number_of_connections = 8
total_connections = 8

# Because we are all tempted to:
connections = 8
The Python Community has
its own Anti-Plural Party
tweet2.png

Anti-Plural Party

# You built it, know its type:

connection_set
connection_list
connection_dict
connection_deque

# You only care about its interface:

def close_all(connection_seq):
    ...
def reset_all(connection_map):
    ...
The Anti-Plural Party have a
quite distinguished lineage:
The relational database best-practice
is to give tables singular names
instead of plural names
Modern scientific Python code
almost looks magic because it uses
singulars for what are really arrays
x = linspace(-10.0, 10.0, 200)
plot(x, sin(x) + 2.0 * cos(x / 2.0))

f(x) = 3 * x ** 2 - 2 * x + 1
plot(x, f(x))
Plural Alternatives
sometimes cannot be avoided!
Since some words have no plural,
you might find yourself forced to use
one of these techniques someday!

Dictionary-like Objects

# I have tried these and more:
connection_dict = {}
connection_by_url = {}
url_to_connection = {}

# Current favorite:
connection_map = {}

# But sometimes I *still* say:
connections = {}
# AAAAAARGH MY EYES

url2connection = {}

Problem of Pluralization

Python pluralization is still the wild west,
and I have not settled on a single doctrine

Two naming tidbits

1. Always default to safety

# Never EVER design an API this way:
yaml.load()
yaml.safe_load()

# Instead:
yaml.load()
yaml.dangerous_load()

Ned Batchelder, “War is Peace”

2. Use the singular to document key/value

# Does this mean that each name maps to
# ONE vector, or a LIST of vectors?

vector_map = {}  # scientific names -> vectors

# So use the singular, which is clear:

vector_map = {}  # scientific name -> vector

Thank you very much!

@brandon_rhodes