In [ ]:
import this
What's this? I guess you could call it an "Easter Egg", a surprise put into the standard library. But it's also a set of rules and conventions that Python coders try to follow. Let's explore them.
Here is a good time to point out PEP8. PEP stands for Python Enhancement Proposals and it is where ideas for changing Python are formalized, to eventually become part of the langauge or die out. If you browse the PEPs you can read about the rationale for many language features at the time they were first proposed, and see some potential new features (PEP465 is exciting for scientists!). Anyways, PEP8 is a little different, it contains all the style recommendations for coding in Python, such as where to put whitespace and how to name things. Sticking to the convention makes it easier for others to read your code (and trains your own eyes to better read others' code). At least skim it.
For example, make your varaible and function names descriptive (there is never a good reason to call a variable temp
), and use lowercase_with_underscores
.
(Another different sort of PEP is PEP20...)
Also code can be beautiful (see Euclidean algorithm for greatest common divisor):
In [ ]:
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
In [ ]:
gcd(120, 32)
Your code shouldn't be too beautiful that it runs foul of the next one though...
In [ ]:
some_dict = {'a': 2, 'b': 3}
list(some_dict.items())
Well, there was a much stronger rationale for not returning a list by default--we'll see it later, but being explicit is a reason that it wasn't considered a drawback to have to use list
when you want a list.
In [ ]:
def nested(x, y):
if x > 0:
if y > 100:
raise ValueError('y is too large')
else:
return y
else:
if x == 0:
return False
else:
raise ValueError('x cannot be negative')
In [ ]:
def flat(x, y):
if x > 0 and y > 100:
raise ValueError('y is too large.')
elif x > 0:
return y
elif x == 0:
return False
else:
raise ValueError('x cannot be negative')
In [ ]:
from math import sqrt
def fancy_sqrt(x):
if x > 0: return sqrt(x)
elif x == 0: return 0
else: return 1j * sqrt(-x)
In [ ]:
def fancy_sqrt(x):
if x > 0:
return sqrt(x)
elif x == 0:
return 0
else:
return 1j * sqrt(-x)
Note: not actaully necessary; math.sqrt
handles negative numbers fine.
This is why Python has no special type for characters (a special case of strings of length 1).
Also, don't write functions that take either a list of elements or a single element. The single element is a special case; it's more consistent and not that much of a hassle to require the function caller to use a single-element list.
In [ ]:
# Don't do this:
try:
import yaml
except ImportError:
print('yaml module not available')
Why?
Because if there's something wrong with the yaml
module, and it raises an ImportError
, you are going to be very confused trying to figure out why it's saying yaml
is not available when you know you installed it.
In [ ]:
1 + '1'
In [ ]:
some_dict = {'a': 1, 'b': 2}
try:
c = some_dict['c']
except KeyError:
c = 0
# instead we have the get method
c = some_dict.get('c', 0)
In [ ]:
names = [
'Alice',
'Bob',
'Carol',
]
all_names = ''
for name in names:
all_names += name
if name is not names[-1]:
all_names += ', '
print(all_names)
# instead we have the join method
print(', '.join(names))
A namespace is a mapping of names to values.
.
of an object (e.g. methods) is a namespace.Having lots of namespaces is nice because it reduces the chance of collisions. It lets us choose good names!
In [ ]:
def chase():
from animals import cat, dog, mouse
dog.chase(cat)
cat.chase(mouse)
animals
is not a real module, but if it were, the dog.chase
and cat.chase
would be two completely different things because they're in different namespaces.
However we can probably assume some symmetry if the module is well-designed.