# Coding Standard & Code Quality
Two of the most important qualities of good code are readability and understandability. Thus, in addition to the functionality of your submitted code, the adherance to certain rules called the 'coding standard' and general cleanliness of code will be graded. In each assignment, a given amount of points is allocated for these matters. Point deductions will be made as percentages from there as given below. This means, if 2 points are allocated for code quality, you can't lose more than 2 points due to these violations.
In general, we encourage you to follow the rules and guidelines from *PEP8*, the style guide created by the people who develop python ([pep8.org](https://pep8.org/)). We will not enforce every detail contained in PEP8, but put special emphasis on certain aspects of it.
To facilitate following the guidelines from PEP8, people created so called *linters* (e.g. *pylint*). They will check your code and warn you about all PEP8-infringements. Most IDEs also allow continuous linting. Keep in mind that a linter will not check for all rules that apply in this course!
## Updates
* 21.10.2020 - 16:21 -> Added 'Not closing files' to No-Gos
## Line length (20%)
Each line of code should contain a maximum of `120` characters - including indentation and comments. Occasional minor violations of this rule are fine, but use common sense ;) Most IDEs allow setting a vertical ruler to simplify this.
## Indentation (20%)
Use exactly `4` spaces for each indentation level. Do not use tabs. Most IDEs and text editors will convert each press of <kbd>Tab</kbd> to four spaces (if yours doesn't, change the settings accordingly).
## Language (20%)
All variable names and comments have to be in English.
## PEP8 Variable Names (20%)
Follow the [PEP8 Naming Conventions](https://pep8.org/#prescriptive-naming-conventions).
* `snake_case`: variables, arguments, functions
* `CapWords`: classes, exceptions
* `CONSTANT_CASE`: constants (defined on module level)
I.e. in this course, in almost all cases you will use snake_case (lowercase with words separated by underscores).
Points are deducted after 2 violations.
## Semantically wrong names (20%)
All variable names should be semantically correct. Semantics involve the meaning of a statement. For variables this means that they have to make sense in the context they are used.
```python=3.8
letters = [2,4,6,8] # This is semantically wrong
even_numbers = [2,4,6,8] # This is semantically correct
```
Points are deducted after 2 violations.
## Non-descriptive names (20%)
Use descriptive names for all variables! This means that the purpose of a variable should become clear from its name. Examples for undescriptive names: `i,j,temp,tmp,list,dict,dic,sum,a,b,c,my_var,my_dict,my_list,val,value,key,string,str,check,flag,bool`.
If a variable servers no purpose (e.g. unused loop counters), use a `_` (single undescore).
Points are deducted after 4 violations.
## Excessive unwanted output (20%)
You should remove or comment out (add a `#` in front of the line you want to comment out, or select the code to comment and press <kbd>Ctrl</kbd><kbd>#</kbd>) all unnecessary `print()` statements. Of course you can use the `print()` function to test your code but comment it out before you submit your assignment.
## unused import (20%)
All imports you don't use in your code must be removed before handing in the assignment. Your IDE or linter will help you here!
## Missing or imcomplete comment header (10%)
Make sure to include and fill out the comment header in every source file!
```
################################################################################
# Author: Firstname Lastname
# MatNr: 01234567
# File: assignmentX.py / assignmentX.ipynb
# Description: ... short description of the file ...
# Comments: ... comments for the tutors ...
# ... e.g. things that you know don't work ...
# ... can be multiline ...
################################################################################
```
## Filenames (up to 100%)
Submit your code exactly as specified on the assignment page or task description. Enabling fileextensions in your file explorer will faciliate naming. If a python script (.py) is asked, don't submit a jupyter notebook (.ipynb) - or the other way around).
## NO-GO's (up to 30%)
Try to write 'pythonic' and simple constructs. To help you with that we have collected a few no-go's which should not appear in your code. (The deductions from the No-Go's will be summed up and capped at 30%).
### Not closing files (20%)
```python
# NO! This keeps the file open
book = open(cookbook_filename, "w")
book.write("Something")
# Yes
with open(cookbook_filename, "w") as book:
book.write("Something")
```
### If-pass-else (10%)
```python=3.8
# No
if some_condition:
pass
else:
do_something()
# Yes
if not some_condition:
do_something()
```
### While-/range-len-loops (in most cases) (10%)
```python=3.8
names = ["Anna", "Flo", "Moritz", "Karin", "Yasmine", "Thomas"]
# Yes
for name in names:
print(name)
# No
for i in range(len(names)):
print(names[i])
# Nooooo
i = 0
while i < len(names):
print(names[i])
i += 1
# when you really need the index as well, use enumerate:
for index, name in enumerate(names):
print(f"#{index}: {name}")
```
### Iterating over iterables to check for existence of an element (10%)
```python=3.8
numbers = [1,2,3,4,5]
target_number = 6
# No
for number in numbers:
if number == target_number:
print("found the target")
# Yes
if target_number in numbers:
print("found the target")
```
### inappropriate comparisons with `is` (10%)
```python=3.8
random_number = 4 # definitely random
# No
guess = int(input("Guess a number"))
if guess is random_number:
print("Yay")
else:
print("Nay")
# Yes
guess = input("Guess a number")
if guess == random_number:
print("Yay")
else:
print("Nay")
# Why? 'is' compares object identity, not values
a = range(10)
b = range(10)
print(a is b) # -> False
print(a == b) # -> True
```
### Avoidable and confusing nesting (10%)
```python=3.8
# No
if correct_type(arg1):
if correct_type(arg2):
if correct_type(arg3):
do_something()
else:
raise TypeError("Argument 3 has the wrong type")
else:
raise TypeError("Argument 2 has the wrong type")
else:
raise TypeError("Argument 1 has the wrong type")
# Yes
if not correct_type(arg1):
raise TypeError("Argument 1 has the wrong type")
if not correct_type(arg2):
raise TypeError("Argument 2 has the wrong type")
if not correct_type(arg3):
raise TypeError("Argument 3 has the wrong type")
do_something()
```
### dict.update for single key-value-pair insertion (10%)
```python=3.8
# No
cities.update({6010: "Innsbruck"})
# Yes
cities[6010] = "Innsbruck"
```
### Silent exception catching
```python=3.8
# NEVER SILENTLY CATCH AN EXCEPTION!!!
# No
try:
# anything that might fail, e.g.
number = int(input("Enter a number: "))
except:
pass
# Minimum, at least do _something_
try:
# anything that might fail, e.g.
number = int(input("Enter a number: "))
except Exception as error:
print(f"Something happened: {error!r}")
# Better, handle specific exceptions
try:
# anything that might fail, e.g.
number = int(input("Enter a number: "))
except ValueError as error:
print(f"Conversion failed, {error!r}")
```