for
¶Programming languages as C
and C++
only know "couting loops": loop from a starting value to an and value by a given stepsize.
In C
for
loops always look like this:
for (int i=0; i < 10: i+=2) {
...
}
The Python equivalent is:
for i in range(0, 10, 2):
print(i)
But Python
loops are more versatile, there are many objects you can "loop over":
for c in "abcde":
print(c)
data = [1, 2, 4, -2]
for item in data:
print(item, end=" ")
The previous example is much shorter than the counting loop version:
data = [1, 2, 4, -2]
for index in range(len(data)):
item = data[index]
print(item, end=" ")
tuple
s also work:
for item in (1, 2, 4, -2):
print(item, end=" ")
And if we iterate over a dictionary, we iterate over the keys:
data = {1: 1, 2:4, 3: 9}
for number in data:
print(number)
Finally we can also iterate over a text file:
file_name = 'text.txt'
# prepare example file
with open(file_name, 'wt') as fh:
print('hi jo', file=fh)
print('second line', file=fh)
# now read
print('using readlines')
with open(file_name, 'rt') as fh:
print(fh.readlines())
print()
print('using for loop')
with open(file_name, 'rt') as fh:
for line in fh:
print(repr(line))
Comment: Using a for
to iterate over a text file allows processing of files which don't fit into your computers memory
enumerate
iterates of an iterator and also provides the iteration number:
with open(file_name, 'rt') as fh:
for i, line in enumerate(fh):
print('line', i, 'is', line.rstrip())
for i, c in enumerate("ABCD"):
print('character', i, 'is', c)
zip
allows to iterate over two or more iterables at the same time (like a "zipper"), the shortest iterable determines the number of iterations:
for ai, bi in zip('abcd', range(2, 10, 3)):
print(ai, bi)
for ai, bi, ci in zip('abcd', range(2, 10, 3), [4,3,2,1]):
print(ai, bi, ci)
First we create some data required in the following example:
with open("abc", "w") as fh:
print("hi", file=fh)
Just a demo function:
def read(path):
with open(path, "r") as fh:
return f.read()
try:
read("abc")
except Exception:
print("can not read 'abc'")
try:
read("abc")
except IOError:
print("can not read 'abc")
import this
is a one of Pythons Easter Eggs (by the way: did you try import antigravity
already ?). This command shows some fundamental design decisions of the inventors of Python:
import this
The ones I like most as general principles of software development:
This has some improtant consequences for general programming:
assert
statements for this)One problem I worked on recently:
Negative effects:
My solution is:
This caused many (early) failures for the users, but the users could directly track what they did wrong. Before this change computations could finish without actually understanding the relation between results and configuration.
The official recommendations for good style of Python code are named https://www.python.org/dev/peps/pep-0008/ PEP8.
Most important rules:
prefer lower case letters + underscores for function and variable names (co called snake case):
e.g. this_is_my_counter = 0
.
User uppercase + lower case letters for classes,
e.g. class MyCounter:
Use spaces around algebraic operations and +
:
x = y + z
instead of x=y+z
.
Use space after comma:
my_func(1, 2, 3)
instead of my_func(1,2,3)
.
Official PEP 8 says "max 80 characters per line", many find a limit of 100 more practical.
No temporary variable required:
a = 3
b = 7
a, b = b, a
print(a, b)
In some circumstances and for some Python versions, the following code can get slow for very long strings:
result = ""
for i in range(100000):
result += str(i) * 100
Better collect your parts as a list of strings, and finally use "".join(..)
to construct the final string:
parts = []
for i in range(100000):
parts.append(str(i) * 100)
result2 = "".join(parts)
assert result == result2
Dictionaries and sets in Python are very fast. Lookup time is approximately constant, independent of the size of the actual dict or set (but at the cost of some overhead memory consumption): In contrary the time for checking if a given element occurs in a given list is on average proptional to the size of the list.
Further set operations can be very expressive:
def check_for_duplicates(collection):
return len(set(collection)) == len(collection)
print(check_for_duplicates("abcde"))
print(check_for_duplicates("abcdea"))
numbers = [1, 2, 3, 1]
print(check_for_duplicates(numbers))
data1 = [1, 2, 3, 4]
data2 = [2, 3, 6]
n_common = len((set(data1) & set(data2)))
print('both lists have', n_common, 'elements in common')
required = {'epsilon', 'num_iter', 'use_optimizations'}
config = {'num_iter': 1000, 'use_optimizations': True, 'delta': 1e-6}
missing = required - set(config)
unknown = set(config) - required
if missing:
print("setting(s) for {} are missing".format(", ".join(missing)))
if unknown:
print("setting(s) for {} not known".format(", ".join(unknown)))
any
and all
¶print(all([True, False]))
print(all([True, True]))
print(all([False, False]))
print(any([True, False]))
print(any([True, True]))
print(any([False, False]))
def average(data):
assert isinstance(data, list), "data must be of type list"
assert all(isinstance(item, (int, float)) for item in data), "data must be numbers"
if not data:
return None
return sum(data) / len(data)
print(average([1, 2.0, 3]))
print(average([1, 2.0, 3, "4"]))
Some data are considered as True
or False
when used within logical checks.
For example None, "", [], (), {}, set(), 0, 0.0
are considered as False
, other values as True
.
def check(value):
if value:
print("{!r} is interpreted as True".format(value))
else:
print("{!r} is interpreted as False".format(value))
check([])
check([1])
check(())
check((1,2))
check("")
check("abc")
check({})
check({1: 2})
check(set())