Miscellaneous#
There is a lot of interesting stuff in the Python language which we don’t have time to cover entirely. In this section, we’ll sample some topics which may be useful to know about.
%pylab inline
%pylab is deprecated, use %matplotlib inline and import the required libraries.
Populating the interactive namespace from numpy and matplotlib
Ternary Operator#
Consider the computation of an absolute value x = abs(a)
.
If we were to write the expression using if/else
statements, we would have
x = 0
if (a > 0):
x = a
else:
x = -a
There are a few things that are not very satisfactory about the if/else
statements. First is that we have to initialize the variable x
in the appropriate scope. The second issue is that it is much more verbose. An alternative is
x = a
if not (a > 0):
x = -a
This is less verbose, and doesn’t waste the initial assignment of x
. However, it might be a bit confusing to read the code, since x
is assigned, but perhaps immediately re-assigned.
The ternary operator solves these issues with a compact, readable syntax
x = a if a > 0 else -a
This expression is easy to read in plain English: “x
is a
if a
is greater than zero, otherwise x
is -a
”
If you’re familiar with C
, you’ve probably already seen the ternary operator ?
. As an example, the above translates to
int x = (a > 0) ? a : -a;
Hash Tables & Dictionaries#
You can read a good overview here.
Hash Functions#
A hash function is a map from some type to an integer. Not all classes are “hashable”, particularly if data is mutable. Built-in hashable classes can be hashed using hash
, or the __hash__()
method
x = 1
print(x.__hash__())
print(hash(x))
1
1
x = 1.1
print(x.__hash__())
print(hash(x))
230584300921369601
230584300921369601
The hash function of an integer is the identiy. For non-integer types, the function can be more complex.
print(hash(1 + 4j))
print(hash("hello"))
4000013
5598587288408018935
Hash functions are typically “one-way” functions, meaning that you can’t compute the input from the output. This makes hash functions very useful for things like checking passwords without storing the password itself, or checking that source code has not been altered (why websites often provide MD5 sums with downloads).
Dictionaries#
Python dictionaries are implemented as a hash table, which uses hash functions to quickly map keys to values.
__slots__
Magic#
When defining a class, we might specify instance attributes
class MyComplex():
def __init__(self, re, im):
self.re = re
self.im = im
def __repr__(self):
return f"{self.re} + {self.im}i"
x = MyComplex(1,2)
x
1 + 2i
However, we can add attributes arbitarily
x.attr = 5
By defautlt, instance atrributes and their values are stored in a dict
, which you can access using __dict__
x.__dict__
{'re': 1, 'im': 2, 'attr': 5}
However, if we’re creating many instances of “small” objects, this might not be very efficient because of the (slight) dictionary overhead compared to data structures like a list or tuple.
The way around this is to use __slots__
, which allows us to specify what attributes are allowed in an instance
class MyComplex2():
__slots__ = ('re', 'im')
def __init__(self, re, im):
self.re = re
self.im = im
def __repr__(self):
return f"{self.re} + {self.im}i"
x = MyComplex2(1,2)
x
1 + 2i
Now we can’t set arbitrary attributes
x.attr = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[9], line 1
----> 1 x.attr = 5
AttributeError: 'MyComplex2' object has no attribute 'attr'
n = 200000
a = []
for i in range(n):
a.append(MyComplex(1,2))
b = []
for i in range(n):
b.append(MyComplex2(1,2))
sum([x.re for x in a ])
sum([x.re for x in b])
%timeit sum([x.re for x in a ])
%timeit sum([x.re for x in b])
11.6 ms ± 168 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
11.1 ms ± 150 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
We see the version of the data structure that uses __slots__
is a bit faster to access.
Exceptions#
try
, except
, final
keywords, as well as exceptions
Coroutines#
See this slide deck
Type Hints#
Type Hints are not enforced by the interpreter, but can be useful for documentation, and code analysis libraries
Other#
b'abc' # byte string - ascii only
b'abc'