8000 Add lectures up to type hinting. · tecladocode/python-refresher@558cde6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 558cde6

Browse files
committed
Add lectures up to type hinting.
1 parent d699d1c commit 558cde6

File tree

8 files changed

+364
-2
lines changed

8 files changed

+364
-2
lines changed

19_unpacking_arguments/code.py

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ def add(x, y):
3434
print(add(**nums))
3535

3636
# -- Unpacking kwargs --
37-
38-
3937
def named(**kwargs):
4038
print(kwargs)
4139

@@ -47,6 +45,8 @@ def named(**kwargs):
4745
) # Unpack dict into arguments. This is OK, but slightly more confusing. Good when working with variables though.
4846

4947
# -- Unpacking and repacking --
48+
def named(**kwargs):
49+
print(kwargs)
5050

5151

5252
def print_nicely(**kwargs):
@@ -56,3 +56,46 @@ def print_nicely(**kwargs):
5656

5757

5858
print_nicely(name="Bob", age=25)
59+
60+
# -- Forced named parameter --
61+
62+
63+
def multiply(*args):
64+
total = 1
65+
for arg in args:
66+
total = total * arg
67+
68+
return total
69+
70+
71+
def apply(*args, operator):
72+
if operator == "*":
73+
return multiply(args)
74+
elif operator == "+":
75+
return sum(args)
76+
else:
77+
raise ValueError("No valid operator provided to apply().")
78+
79+
80+
print(apply(1, 3, 6, 7, operator="+"))
81+
print(apply(1, 3, 5, "+")) # Error
82+
83+
# -- Both --
84+
85+
86+
def both(*args, **kwargs):
87+
print(args)
88+
print(kwargs)
89+
90+
91+
both(1, 3, 5, name="Bob", age=25)
92+
93+
# This is normally used to accept an unlimited number of arguments and keyword arguments, such that some of them can be passed onto other functions.
94+
# You'll frequently see things like these in Python code:
95+
96+
"""
97+
def post(url, data=None, json=None, **kwargs):
98+
return request('post', url, data=data, json=json, **kwargs)
99+
"""
100+
101+
# While the implementation is irrelevant at this stage, what it allows is for the caller of `post()` to pass arguments to `request()`.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
student = {"name": "Rolf", "grades": (89, 90, 93, 78, 90)}
2+
3+
4+
def average(sequence):
5+
return sum(sequence) / len(sequence)
6+
7+
8+
print(average(student["grades"]))
9+
10+
# But wouldn't it be nice if we could...
11+
# print(student.average()) ?
12+
13+
14+
class Student:
15+
def __init__(self):
16+
self.name = "Rolf"
17+
self.grades = (89, 90, 93, 78, 90)
18+
19+
def average(self):
20+
return sum(self.grades) / len(self.grades)
21+
22+
23+
student = Student()
24+
print(student.average())
25+
# Identical to Student.average(student)
26+
27+
28+
# -- Parameters in __init__ --
29+
30+
31+
class Student:
32+
def __init__(self, name, grades):
33+
self.name = name
34+
self.grades = grades
35+
36+
def average(self):
37+
return sum(self.grades) / len(self.grades)
38+
39+
40+
student = Student("Bob", (36, 67, 90, 100, 100))
41+
print(student.average())
42+
43+
# -- Remember *args ? --
44+
45+
46+
class Student:
47+
def __init__(self, name, *grades):
48+
self.name = name
49+
self.grades = grades
50+
51+
def average(self):
52+
return sum(self.grades) / len(self.grades)
53+
54+
55+
student = Student("Bob", 36, 67, 90, 100, 100)
56+
print(student.average())

21_functions_vs_methods/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Functions vs. Methods
2+
3+
A function that lives inside a class is called a method.
4+
5+
So, methods are functions, but not all functions are methods.
6+
7+
## Function
8+
9+
```python
10+
def average(sequence):
11+
return sum(sequence) / len(sequence)
12+
```
13+
14+
## Method
15+
16+
```python
17+
class Student:
18+
def __init__(self): # method
19+
self.name = "Rolf"
20+
self.grades = (79, 90, 95, 99)
21+
22+
def average(self): # method
23+
return sum(self.grades) / len(self.grades)
24+
```

22_str_and_repr/code.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
class Person:
2+
def __init__(self, name, age):
3+
self.name = name
4+
self.age = age
5+
6+
7+
bob = Person("Bob", 35)
8+
print(bob) # Not the nicest thing to read!
9+
10+
# -- __str__ --
11+
# The goal of __str__ is to return a nice, easy to read string for end users.
12+
13+
14+
class Person:
15+
def __init__(self, name, age):
16+
self.name = name
17+
self.age = age
18+
19+
def __str__(self):
20+
return f"Person {self.name}, {self.age} years old"
21+
22+
23+
bob = Person("Bob", 35)
24+
print(bob) # Much nicer
25+
26+
# -- __repr__ --
27+
# The goal of __repr__ is to be unambiguous, and if possible what it outputs should allow us to re-create an identical object.
28+
29+
30+
class Person:
31+
def __init__(self, name, age):
32+
self.name = name
33+
self.age = age
34+
35+
def __repr__(self):
36+
# I'm adding the < > just so it's clear that this is an object we're printing out!
37+
return (
38+
f"<Person({self.name!r}, {self.age})>"
39+
) # !r calls the __repr__ method of the thing.
40+
41+
42+
bob = Person("Bob", 35)
43+
print(bob) # Not as nice, but we could re-create "Bob" very easily.

23_classmethod_staticmethod/code.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
class ClassTest:
2+
def instance_method(self):
3+
print(f"Called instance_method of {self}")
4+
5+
@classmethod
6+
def class_method(cls):
7+
print(f"Called class_method of {cls}")
8+
9+
@staticmethod
10+
def static_method():
11+
print(f"Called static_method. We don't get any object or class info here.")
12+
13+
14+
instance = ClassTest()
15+
instance.instance_method()
16+
F438 17+
ClassTest.class_method()
18+
ClassTest.static_method()
19+
20+
# -- What are they used for? --
21+
22+
# Instance methods are used for most things. When you want to produce an action that uses the data stored in an object.
23+
# Static methods are used to just place a method inside a class because you feel it belongs there (i.e. for code organisation, mostly!)
24+
# Class methods are often used as factories.
25+
26+
27+
class Book:
28+
TYPES = ("hardcover", "paperback")
29+
30+
def __init__(self, name, book_type, weight):
31+
self.name = name
32+
self.book_type = book_type
33+
self.weight = weight
34+
35+
def __repr__(self):
36+
return f"<Book {self.name}, {self.book_type}, weighing {self.weight}g>"
37+
38+
@classmethod
39+
def hardcover(cls, name, page_weight):
40+
return cls(name, cls.TYPES[0], page_weight + 100)
41+
42+
@classmethod
43+
def paperback(cls, name, page_weight):
44+
return cls(name, cls.TYPES[1], page_weight)
45+
46+
47+
heavy = Book.hardcover("Harry Potter", 1500)
48+
light = Book.paperback("Python 101", 600)
49+
50+
print(heavy)
51+
print(light)

24_class_inheritance/code.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
class Device:
2+
def __init__(self, name, connected_by):
3+
self.name = name
4+
self.connected_by = connected_by
5+
self.connected = True
6+
7+
def __str__(self):
8+
return f"Device {self.name!r} ({self.connected_by})"
9+
10+
def disconnect(self):
11+
self.connected = False
12+
13+
14+
# printer = Device("Printer", "USB")
15+
# print(printer)
16+
17+
# We don't want to add printer-specific stuff to Device, so...
18+
19+
20+
class Printer(Device):
21+
def __init__(self, name, connected_by, capacity):
22+
super().__init__(name, connected_by)
23+
self.capacity = capacity
24+
self.remaining_pages = capacity
25+
26+
def __str__(self):
27+
return f"{super().__str__()} ({self.remaining_pages} pages remaining)"
28+
29+
def print(self, pages):
30+
if not self.connected:
31+
raise TypeError("Device is disconnected at this time, cannot print.")
32+
print(f"Printing {pages} pages.")
33+
self.remaining_pages -= pages
34+
35+
36+
printer = Printer("Printer", "USB", 500)
37+
printer.print(20)
38+
print(printer)
39+
printer.print(50)
40+
print(printer)
41+
printer.disconnect()
42+
printer.print(30) # Error

25_class_composition/code.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Something I see a lot, but you SHOULDN'T DO
2+
3+
4+
class BookShelf:
5+
def __init__(self, quantity):
6+
self.quantity = quantity
7+
8+
def __str__(self):
9+
return f"BookShelf with {self.quantity} books."
10+
11+
12+
shelf = BookShelf(300)
13+
14+
15+
class Book(BookShelf):
16+
def __init__(self, name, quantity):
17+
super().__init__(quantity)
18+
self.name = name
19+
20+
21+
# This makes no sense, because now you need to pass `quantity` to a single book:
22+
23+
book = Book("Harry Potter", 120)
24+
print(book) # What?
25+
26+
# -- Composition over inheritance here --
27+
28+
# Inheritance: "A Book is a BookShelf"
29+
# Composition: "A BookShelf has many Books"
30+
31+
32+
class BookShelf:
33+
def __init__(self, books):
34+
self.books = books
35+
36+
def __str__(self):
37+
return f"BookShelf with {len(self.books)} books."
38+
39+
40+
class Book:
41+
def __init__(self, name):
42+
self.name = name
43+
44+
45+
book = Book("Harry Potter")
46+
book2 = Book("Python 101")
47+
shelf = BookShelf([book, book2])
48+
print(shelf)

26_type_hinting/code.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
def list_avg(sequence: list) -> float:
2+
return sum(sequence) / len(sequence)
3+
4+
5+
# -- Type hinting classes --
6+
7+
8+
class Book:
9+
def __init__(self, name: str, page_count: int):
10+
self.name = name
11+
self.page_count = page_count
12+
13+
14+
# -- Lists and collections --
15+
16+
from typing import List # , Tuple, Set, etc...
17+
18+
19+
class BookShelf:
20+
def __init__(self, books: List[Book]):
21+
self.books = books
22+
23+
def __str__(self) -> str:
24+
return f"BookShelf with {len(self.books)} books."
25+
26+
27+
# Key benefit is now you'll get told if you pass in the wrong thing...
28+
29+
book = Book(
30+
"Harry Potter", "352"
31+
) # Suggests this is incorrect if you have a tool that will analyse your code (e.g. PyCharm or Pylint)
32+
shelf = BookShelf(book) # Suggests this is incorrect too
33+
# Type hinting is that: hints. It doesn't stop your code from working... although it can save you at times!
34+
35+
# -- Hinting the current object --
36+
37+
38+
class Book:
39+
TYPES = ("hardcover", "paperback")
40+
41+
def __init__(self, name: str, book_type: str, weight: int):
42+
self.name = name
43+
self.book_type = book_type
44+
self.weight = weight
45+
46+
def __repr__(self) -> str:
47+
return f"<Book {self.name}, {self.book_type}, weighing {self.weight}g>"
48+
49+
@classmethod
50+
def hardcover(cls, name: str, page_weight: int) -> "Book":
51+
return cls(name, cls.TYPES[0], page_weight + 100)
52+
53+
@classmethod
54+
def paperback(cls, name: str, page_weight: int) -> "Book":
55+
return cls(name, cls.TYPES[1], page_weight)

0 commit comments

Comments
 (0)
0