Skip to content

Commit f8788e8

Browse files
authoredOct 21, 2022
Toc pitfalls (ssciwr#7)
* set structure * add mutability * more pitfalls * more * more
1 parent 48b796f commit f8788e8

File tree

10 files changed

+150
-2
lines changed

10 files changed

+150
-2
lines changed
 

‎Material_Part4_Pitfalls/README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Pitfalls: Common ways to introduce bugs in your Python code
2+
3+
## Instantiation of mutable default keyword arguments in function calls
4+
5+
Default arguments are only evaluated once: At the time the function is created. If you provide a mutable default keyword argument and then change it in the function, the next time the function is called without that keyword the default will point to the same address as in the first call; but the argument will have already changed, so the default in the first call and the default in the second call are different. Solution: Only provide non-mutable default arguments. See the [example](./mutable_default.py).
6+
7+
## Naming the module
8+
9+
A source of errors can be naming a module the same as another module that is imported, in this example the module is named `math.py` but also imports math from the standard Python library; and function calls using methods from the math module will fail, as Python will look for those in the `math.py` file. Solution: Name your module file different than the modules that you are importing. See the [example](./math.py).
10+
11+
## Exhausting iterators
12+
13+
Iterators and generators can be exhausted, meaning you can only use them once. Solution: If you create an iterator or a generator and you need it more than once you need to save it first. As in the [example](./exhaust_iterators.py) provided, the iterator is created using `zip`, and can be saved in a `list`.
14+
15+
## Variable assignment in different scopes
16+
17+
Assigning a variable within a function shadows any assignment that may have happened in an outer scope. Solution: Pass the variable as an argument into the inner scope or use the return value of a new assignment. See the [example](assignment.py).
18+
19+
## Closure variable binding
20+
Python uses late binding, resulting that in closures variables are only looked up once the inner function is called. Solution: Make sure the referenced variables are either passed to the inner function or is set correctly in the surrounding scope. See the [example](closure.py).

‎Material_Part4_Pitfalls/assignment.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
mylist = [1, 2, 3]
3+
4+
def myfunc():
5+
mylist.append("x")
6+
7+
myfunc()
8+
print(mylist)
9+
10+
mylist2 = [1, 2, 3]
11+
12+
def myfunc2():
13+
mylist2 += "x"
14+
15+
# myfunc2()
16+
print(mylist2)
17+
18+
def myfunc3():
19+
mylist3 = "x"
20+
return mylist3
21+
22+
mylist2 += myfunc3()
23+
print(mylist2)

‎Material_Part4_Pitfalls/closure.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
def make_multiplier_of(n):
2+
mylist = []
3+
for i in n:
4+
def multiplier(j):
5+
return i * j
6+
mylist.append(multiplier)
7+
return mylist
8+
9+
times_x = make_multiplier_of(list(range(5)))
10+
11+
for times in times_x:
12+
print(times(2))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
2+
lunch = ["Pizza", "Salad", "Pasta", "Sushi", "Sandwich"]
3+
4+
menu = zip(days, lunch)
5+
# menu = list(zip(days, lunch))
6+
7+
print(list(menu))
8+
9+
10+
for item in menu:
11+
print("On {} we offer {} for lunch.".format(item[0], item[1]))

‎Material_Part4_Pitfalls/math.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from math import radians, sin
2+
3+
4+
rads = radians(90)
5+
6+
print(sin(rads))
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import time
2+
from datetime import datetime
3+
4+
5+
def ingredients1(ingredient, all_ingredients=[]):
6+
all_ingredients.append(ingredient)
7+
print(all_ingredients)
8+
9+
10+
def ingredients2(ingredient, all_ingredients=None):
11+
if all_ingredients is None:
12+
all_ingredients = []
13+
all_ingredients.append(ingredient)
14+
print(all_ingredients)
15+
16+
def myfunc(a={"b": 0}):
17+
a["b"] += 5
18+
print(a)
19+
20+
def display_time(time_to_print=datetime.now()):
21+
print(time_to_print.strftime('%B %d, %Y %H:%M:%S'))
22+
23+
if __name__=="__main__":
24+
# method 1
25+
# a list is mutable
26+
print(ingredients1.__defaults__)
27+
ingredients1("flour")
28+
print(ingredients1.__defaults__)
29+
ingredients1("sugar")
30+
print(ingredients1.__defaults__)
31+
ingredients1("butter")
32+
# method 2
33+
# None is not mutable
34+
print(ingredients2.__defaults__)
35+
ingredients2("flour")
36+
print(ingredients2.__defaults__)
37+
ingredients2("sugar")
38+
print(ingredients2.__defaults__)
39+
ingredients2("butter")
40+
# a dictionary is mutable
41+
myfunc()
42+
myfunc()
43+
display_time()
44+
# default argument only evaluated once
45+
print(display_time.__defaults__)
46+
time.sleep(1)
47+
display_time()

‎Material_Part5_BetterCoding/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Better coding
2+
3+
## Why you should run code in your module only if `__name__` is `"__main__"`
4+
5+
Always encapsulate your module's content in functions/classes and have all function calls and instantiations contained after the statement
6+
```
7+
if __name__ == "__main__"
8+
```
9+
**Why?** Because, if you import your code as a module, all code that is not contained in a function or class will be run. Now, if you directly run the module,`__name__` will be set to `"__main__"`. But if the module is imported, `__name__` is set to the module's name. See the examples [module1.py](module1.py) and [module2.py](module2.py).
10+
11+
**Task:** Run the code in `module1.py` and `module2.py` and see what is run upon import and how `__name__` changes.
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
3+
print("This will always be printed.")
4+
5+
def print_name():
6+
print("Module one is {}".format(__name__))
7+
8+
if __name__ == "__main__":
9+
print("Runs as main")
10+
print_name()
11+
else:
12+
print("Runs as import")
13+
print_name()
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import module1
2+
3+
print("Running module 2 as {}".format(__name__))
4+
5+
module1.print_name()

‎README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ Course date: Nov 8th 2022, 9:00AM - 1:00PM
2929
1. [PEP recommendations](Material_Part1_PEP/README.md)
3030
1. [Linting](Material_Part2_Linter/README.md)
3131
1. [Code formatting](Material_Part3_Formatter/README.md)
32-
1. Write better code: Examples
33-
1. Write better code: Pitfalls
32+
1. Write better code: [Pitfalls](Material_part4_Pitfalls)
33+
1. Write better code: [Examples](Material_part5_Examples)

0 commit comments

Comments
 (0)
Please sign in to comment.