404 KiB
Table of Contents
- 1 DISCLAIMER
- 2 General Python Concepts
- 3 Collection of Python Code Snippets
DISCLAIMER¶
THIS DOCUMENT IS WORK-IN-PROGRESS
General Python Concepts¶
Indentation¶
The syntax used in Python relies heavily on indentation. If the code is not indented 100% correctly, running it will result in an error.
Every block of code that follows a :
needs to be indented. The standard indent is equal four spaces, which is default in most editors.
A code block can be e.g. a conditional statement:
if a > 0: # <- Remember colon after the condition
print(a) # <- Indent by four spaces inside if-block
print(2*a) # <- All code in the block must be indented
b = 2 # <- This code is outside the if-block
If this indentation is not followed, an error will be thrown. IF for instance the variable b
outside the if
statement was indented only a single space, running the code would result in an IndentationError
.
After having typed the colon, hitting enter in the editor should automatically indent the code correctly.
Getting used to this indentation can be a little hard at first, but quickly becomes second nature. Such kind of strictness in the syntax is common for all programming languages. Many other languages use parantheses or brackets of some kind or begin
/end
statements to keep blocks of code separated, which can become a little messier to look at. Some might argue that Python keeps the strictness to a minimum and that following the forced indentation scheme only helps keeping the code neat and tidy. Something that one should also strive to do in other languages.
The same concept of :
followed by indentation goes for for
and while
loops, functions, classes etc.
Common Nomenclature¶
Iterable¶
An iterable is an object that can be iterated/looped over. This could be strings, lists, tuples, keys of a dictionary etc. It can occur in a for loop like this:
for item in iterable:
# Some code
In more detail¶
An iterable is an object that has an __iter__()
method attached to it. Understanding the constuct of Python classes
is paramount to fully understand iterables.
Sequence¶
A construct that supports indexing and slicing. Most important ones are strings, lists, tuples.
Function¶
A function is a block of code that is first defined, and thereafter can be called to run as many times as needed. A function might have arguments, some of which can be optional if a default value is specified.
A function is called by parantheses: function_name()
. Arguments are placed inside the paranthes and comma separated if there are more than one.
Similar to f(x, y)
from mathematics.
A function can return one or more values to the caller. The values to return are put in the return
statement, which ends the function. If no return
statement is given, the function will return None
.
The general syntax of a function is:
def function_name(arg1, arg2, optional_arg1=0, optional_arg2=None):
'''
This is the so-called 'docstring', which documents concisely what the function does.
It is basically a multiline comment.
'''
# Function code goes here
# Possible 'return' statement which terminates the function.
# If 'return' is not specified, function returns None.
return value_to_return
If multiple values are to be returned, they can be separeted by commas. The returned entity will by default be a tuple.
Note: When using default arguments, it is good practice to only use immutable types as defaults. Using mutable types can have unexpected and hard-to-detect side effects.
Class¶
A class
is a convenient way to create customized objects in Python. When classes are being used in code the programming style is referred to as Object Oriented Programming (OOP).
Some benenits of OOP:
Code that is naturally tied can bunched together. If one has multiple functions that can perform related but still different opertions, they can be grouped inside a class and turned to methods. It makes the code more modular.
It avoids a lot of code repetition.
It can make it easier for users of the code to directly create objects and work with them.
Some drawbacks of OOP:
The concept is harder to grasp compared to working with standard functions.
Will often create many lines of code
OOP is mostly used for actual software programs and perhaps less so for standard single-file scripting.
Regardless whether ones has to use it directly or not, knowing the concept of OOP is still very integral to understanding how Python works. Many built-in features and third party libraries use this concept.
Consider the example below where a class for concrete sections is created:
class ConcreteSection():
def __init__(self, b, h):
self.b = b
self.h = h
# Print to see what it is
print(ConcreteSection)
__init__()
is a special method that initiates the the class instance. See explanations below for these concepts.
Class instance¶
Defining a class like ConcreteSection
above allows for creating many different concrete sections and storing them in variables:
# Create a concrete section with width 100 and height 200
section1 = ConcreteSection(100, 200)
print(section1)
The variable section1
is called an instance of the ConcreteSection
class.
The height of the cross section stored in section1
can be accesssed as
print(section1.h)
Method¶
A method is similar to a function, except it resides inside a class.
A method is called by 'dot'-notation: class_instance.method_name()
An example of a class is the predefined list
in Python.
The list class has many methods, for example list.append()
where append()
is a 'function' written and contained inside the list
-class, therefore called a method instead of a function.
The fact that a list is a class can be seen by
# Define a list
w = [1, 2, 3]
# Print the type of the variable 'w'
print(type(w))
# Use the 'append' method
w.append(4)
print(w)
Continuing from the ConcreteSection
class above, we could write a method that calculates and returns the area of the section:
class ConcreteSection():
def __init__(self, b, h):
self.b = b
self.h = h
def area(self):
'''
This method returns the area of the concrete section.
'''
return self.b * self.h
Use of self
¶
Notice the use of self
as the first argument to both the special __init__
method and the standard area
method. Think of self
is the instance itself.
So after an instance (section) is created, computation of the area for that section does not need to have the width and the height as input. These values are already stored inside the instance, i.e. inside self
. They can then be referred to as self.b
and self.h
.
This is a showcase of one of the advantages of OOP. It does require writing a lot of boiler-plate code to set it up, but afterwards it is easier to use since one can work directly with objects.
The 'ease' of use is demonstrated below:
# Define an instance of the ConcreteSection class
sect = ConcreteSection(b=500, h=1000)
# Use the 'area' method to calculate the area of the instance
print(sect.area())
Print some info for the section:
print(f'The section has dimensions (b, h)=({sect.b}, {sect.h}) and an area of {sect.area()}')
A more advanced scenario¶
In the same manner, many addtional methods could be created and tied to the instance sect
. Ones all desired methods are written it is easy to calculate everything by simply typing sect.method_name(...)
.
Consider a more advanced scenario of the ConcreteSection
class like:
# Create section as an instance of class ConcreteSection
sect = ConcreteSection(b, h, some_other_needed_parameters)
# Perform some desired operations by calling appropriate methods of the instance (section)
sect.plot_section()
sect.area()
sect.reinforcement_area()
sect.reinforcement_ratio()
sect.plot_mn_diagram()
sect.plot_capacity_surface()
sect.perform_sls_analysis(...)
sect.largest_crack_width(...)
sect.load_combination_with_largest_utilisation_ratio(...)
sect.spit_out_the_entire_damn_documentation_report(...)
Special method¶
__init__()
Generator¶
...
Iterator¶
... __next__()
method.
Common error messages¶
Some common error messages and their meaning:
IndentationError
¶
Occurs when atempting to run code that is uncorrectly indented. Python uses indentation to structure blocks of code instead of parantheses, brackets and end statements. Therefore, it is very strict about correct indentation. A code editor will assist so maintaining correct indentation becomes very easy. Make sure the editor will treat a tab as four spaces, otherwise indentation errors can be hard to spot. This configuration should be default in many editors though.
SyntaxError
¶
Occurs when syntax is incorrect. The following example will throw a SyntaxError
since the parathesis in function call to len()
is not closed:
len([1, 2, 3] # <--- Will throw SyntaxError
IndexError
¶
Occurs when atempting to access an index that is not present in the sequence.
L = [1, 2, 3]
L[10] # <--- IndexError, since index 10 is not present in L
ValueError
¶
Occurs when ..
TypeError
¶
Occurs when atempting to do an operation that is not supported by the given type. For example a function call of an integer:
4()
Collection of Python Code Snippets¶
Th chapters belwo contains small chunks of code that can serve as parts of bigger scripts and programs.
Iteration¶
Simplest forms of iteration¶
# Iterate over lists
for i in [1, 2, 3]:
print(i)
# Iterate over strings
for letter in 'abc':
print(letter)
Iteration while keeping track of index¶
names = ['Alpha', 'Bravo', 'Charlie']
idx = 0
for name in names:
print(idx, name)
idx += 1
A better way using the enumerate
function
# Use enumerate to access both index and value during iteration
for idx, name in enumerate(names):
print(idx, name)
# Index starting from 1 instead of default 0
for index, name in enumerate(names, start=1):
print(index, name)
Thus, enumerate
keeps track of both the value and the index that is being iterated over.
Iterating over multiple iterables at once¶
Iterating over multiple iterables at once accessing the first element of all, then the second element of all etc. can be done in multiple ways. The cleanest way is using the zip
function.
An iterable is something that can be iterated over, e.g. lists, stings, tuples, sets etc.
An example with two lists:
x_values = [1, 2, 3]
y_values = [3, 6, 4]
for x, y in zip(x_values, y_values):
print(f'x = {x} and y = {y}')
An example with three lists:
x_values = [1, 2, 3]
y_values = [3, 6, 4]
z_values = [10, 23, 26]
for x, y, z in zip(x_values, y_values, z_values):
print(f'x = {x} and y = {y} and z = {z}')
english = ['one', 'two', 'three']
german = ['eins', 'zwei', 'drei']
for e, g in zip(english, german):
print(f'{g} means {e} in german')
Iterating over multiple iterables while keeping track of index¶
By using enumerate
and zip
together, multiple iteratbles can be iterated over while keeping track of the index (loop counter).
In case of multiple iteratbles, enumerate
returns the index and all values in a tuple, which then need to be unpacked. See below:
for idx, val in enumerate(zip([12,3,4], [5, 3, 46], [52, 53, 2])):
print(f'idx = {idx} , val = {val}')
Another example:
english = ['one', 'two', 'three']
german = ['eins', 'zwei', 'drei']
for idx, val in enumerate( zip(english, german) ):
print(f'Index {idx}: {val[1]} means {val[0]} in german')
# Basic usage of f-strings
a = 2
b = 27
print(f'Multiplication: a * b = {a} * {b} = {a*b}')
print(f'Division: a / b = {a} / {b} = {a/b}')
Rounding anf formating numeric string values¶
When printing variables it can be useful to format them. Some common ways of formatting are shown below:
Number | Syntax | Output | Description |
---|---|---|---|
3.1415926 | {3.1415926:.2f} | 3.14 | 2 decimal places |
3.1415926 | {3.1415926:+.2f} | +3.14 | 2 decimal places with sign |
-1 | {-1:+.2f} | -1.00 | 2 decimal places with sign |
2.71828 | {2.71828:.0f} | 3 | No decimal places |
5 | {5:0>2d} | 05 | Pad number with zeros (left padding, width 2) |
5 | {5:x<4d} | 5xxx | Pad number with x’s (right padding, width 4) |
10 | {10:x<4d} | 10xx | Pad number with x’s (right padding, width 4) |
1000000 | {1000000:,} | 1,000,000 | Number format with comma separator |
0.25 | {0.25:.2%} | 25.00% | Format percentage |
1000000000 | {1000000000:.2e} | 1.00e+09 | Exponent notation |
The part of the syntax that actually formats the value is shown in bold.
Source: https://mkaz.blog/code/python-string-format-cookbook/
Some examples are shown below. Note the location where the formatting must be specified, i.e. directly after the variable to be formatted.
c = 50
d = 200
# f-string with formatting for number of decimal places
print(f'Division: c / d = {c} / {d} = {c/d:.3f}')
# f-string with formatting as percent
print(f'{c} is {c/d:.1%} of {d}')
List comprehensions¶
List comprehensions are often shorter and easier to read than their traditional for
loop counterparts. They are also faster since the entire list is built in one go instead of by appending element by element.
Consider the following to equivalent loops: TODO INPUT
Simple form¶
The general form of the simplest list comprehension is
result_list = [expression for item in iterable]
iterable
is a sequence that can be iterated over, this could be a list, a string, a tuple etc.item
is the counter for the iterable, think of this as the i'th elementexpression
can be anything, but will often include theitem
# This list comprehension
x = [r**2 for r in [5, 6, 8, 10]]
# Will be computed at this list
x = [5**2, 6**2, 8**2, 10**2]
# To be equal to
x = [25, 36, 64, 100]
x = [1, 2, 3, 4, 5] # Define a list (iterable)
# Double numbers from old list and put in new list
result_list = [2*i for i in x]
result_list
diameters = [10, 12, 16, 20] # Define list of rebar diameters (iterable)
# Compute rebar areas for each diameter
rebar_areas = [3.14 * dia**2 / 4 for dia in diameters]
rebar_areas
Form with conditional if
statement¶
The general form of a list comprehension with a conditional is
result_list = [expression for item in iterable if condition]
iterable
is a sequence that can be iterated over, this could be a list, a string, a tuple etc.condition
is a logical condition, e.g. "item > 3", which returns a boolean (True
/False
). This can act as as filter.result_list
is a new list containingexpression
for eachitem
that fulfilled thecondition
x = [1, 2, 3, 4, 5] # Define a list (iterable)
# Filter list with list comprehension by a condition
result_list = [i for i in x if i > 3]
result_list
A litlle more complex example that includes slicing of strings and type conversion from strings to integers within the conditional statement:
# Define list of steel profiles as a list of strings
profiles = ['HE170A', 'HE180A', 'HE190A', 'HE200A', 'HE210A', 'HE210A']
# Filter all profiles that are HE200A or smaller
filtered_profiles = [p for p in profiles if int(p[2:5]) <= 200 ]
filtered_profiles
Here int(p[2:5])
extracts the number part of the steel profile (e.g. '190' from 'HE190A') and converts it from a string to an integer. This is necessary because it is to be compared with an integer afterwards.
A statement like '190' <= 200
would throw a TypeError: '<=' not supported between instances of 'int' and 'str'
Form with conditional if
/ else
statement¶
The general form of a list comprehension with a conditional if
and else
is:
result_list = [expression1 if condition else expression2 for item in iterable]
iterable
is a sequence that can be iterated over, this could be a list, a string, a tuple etc.condition
is a logical condition, e.g. "item > 3", which returns a boolean (True
/False
). This can act as as filter.result_list
is a new list containing elements where eachitem
depends oncondition
. If it isTrue
,expression1
is put in. Elseexpression2
is put in.
V = [3, 62, 182, 26, 151, 174]
# Set all elements of V that are less than 100 equal to 0
W = [0 if i < 100 else i for i in V]
W
List comprehensions with multiple if
/ else
statements¶
Q1 = (-18, -27, 2, -21, -15, 5)
[0 if val > 0 else val if 0 > val > -25 else -25 for val in Q1]
Nested list comprehensions¶
It is possible to make nested list comprehensions, i.e. with for loops inside eachother.
[[j for j in range(25, 30)] for i in range(5)]
Complex one-liners can be quite hard to read, so for nested if
statements or loops it is sometimes better to create a traditional multi line for
loop.
Dictionaries¶
Create a dictionary from two lists¶
Making first list the keys and second the values
names = ['Pelé', 'Maradona', 'Zidane']
countries = ['Brazil', 'Argentina', 'France']
d = dict(zip(names, countries))
d
Functions¶
Default arguments¶
A little side note¶
A good practice is to use only immutable types as default arguments. Python will allow mutable arguments to be given as defaults, but the behavior can be confusing if the default argument is mutated within the function.
Consider this example:
TODO
A list (which is mutable) is given as default value and the list is mutated within the function. This is because it will mutate the deafult argument so the next function call has the mutated value as the default.
Examples¶
def abbreviate_day(day):
'''Return the specified day in abrreviated three character form'''
# Define dictionary for mapping days to their abbreviations
day_to_abbreviation = {'monday': 'Mon', 'tuesday': 'Tue',
'wednesday': 'Wed', 'thursday':
'Thu', 'friday': 'Fri',
'saturday': 'Sat', 'sunsay': 'Sun'}
# Convert input string to all lowercase
day = day.lower()
# Return abbreviation if day is a valid day, raise error otherwise
try:
return day_to_abbreviation[day]
except KeyError:
raise ValueError(f"Expected input 'day' to be a day name, but received '{day}'")
# Test function
d = 'Monday'
print(abbreviate_day(d))
- Note: The input parameter when the function was defined was a string called
day
, but the when the function was called, the input string was calledd
. Thus, the input parameters passed into a function do not need to have the same name as when the function is defined.
Tables as Pandas
dataframes¶
import pandas as pd
# Allow for dataframes to be displayed as HTML-tables
from IPython.display import display, HTML
merge
, join
and concat
¶
The best explanation is arguably from the documentation itself: https://pandas.pydata.org/pandas-docs/stable/user_guide/merging.html
Roughly speaking, merge
is used to combine two or more dataframes, while concat
is used to 'glue' dataframes together, i.e. append them to eachother or put them side by side. concat
is not content aware in that it will might include duplicate columns, whereas merge
will remove/merge duplicates. concat
requires the dataframe dimensions to be equal along the axis of concatenation, where merge
can treat any size.
All join
operations could also be achieved by using merge
, but join
commands have easier/shorter code for some common use cases.
Functionality similar to VLOOKUP in Excel¶
# Read pile data from csv-file
df_piles = pd.read_csv('piles.csv')
# Read steel profile data from csv-file
df_profiles = pd.read_csv('steel_profiles.csv')
# Merge dataframes on the "Profile" column (similar to Excel VLOOKUP)
df_merged = df_piles.merge(df_profiles, on='Profile', how='left')
display(df_piles, df_profiles, df_merged)
The first dataframe containing pile types and profile has been populated by the data of the steel profiles. The resulting dataframe has all profile data, but it could have been specified to include only certain columns of the profile properties.
Compared to e.g. Excel, this has the advantage of being scalable in each table direction. Meaning that if more rows or columns were to be added to either dataframe, the code need no change at al, it just adapts. No manual adjustment of ranges/cells.
Plotting with Matplotlib
¶
import matplotlib.pyplot as plt
# Enable for plots to be shown inside notebook cells
%matplotlib inline
Simplest forms of graphs¶
# Create x- and y-coordinates for f(x) = x^2
x = [i for i in range(-100, 105, 5)]
y = [i**2 for i in x]
# Create basic plot
plt.plot(x, y)
# Show plot
plt.show()
Note: When using an editor, one has to call plt.show()
to show the figure, which then opens a separate window.
Including title, labels and marker styling:
plt.title('Graph for $f(x) = x^2$')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.plot(x, y, 'x', color='limegreen', markersize=8)
plt.show()
Multiple plots in same figure¶
plt.plot(x, y, label='$f(x)=x^2$') # x and y defined above
plt.plot(x, [2*i for i in y], label='$f(x)=2x^2$')
plt.legend()
plt.show()
Subplots¶
In order for subplots to be used, it is necessary to access the underlying objects that control ....
Heatmaps/colormaps¶
Integration with Pandas
¶
Numerical computations¶
A large portion of the computations are carried out as optimized C-code under the hood, which is pretty much as fast as it gets.
Scipy¶
TODO: from scipy.signal import find_peaks