Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions common-content/en/module/decomposition/dataclasses/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Equality is one: ideally two value objects are the same if their fields are the
class Person:
def __init__(self, name: str, age: int, preferred_operating_system: str):
self.name = name
self.age = age
self.age = age
self.preferred_operating_system = preferred_operating_system

imran = Person("Imran", 22, "Ubuntu")
Expand All @@ -54,24 +54,27 @@ Python has a useful {{<tooltip text="decorator" title="Decorator">}}A decorator
from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
class Animal:
name: str
species: str
age: int
preferred_operating_system: str
noise: str

imran = Person("Imran", 22, "Ubuntu") # We can call this constructor - @dataclass generated it for us.
print(imran) # Prints Person(name='Imran', age=22, preferred_operating_system='Ubuntu')
indigo = Animal("indigo", "cat", 2, "meow") # We can call this constructor - @dataclass generated it for us.
print(indigo) # Prints Animal(name='Indigo', species='cat', age=2, noise='meow')

imran2 = Person("Imran", 22, "Ubuntu")
print(imran == imran2) # Prints True
indigo2 = Animal("indigo", "cat", 2, "meow")
print(indigo == indigo2) # Prints True
```

The `dataclass` decorator generated a constructor, a `__str__` method (which is called when string formatting the value), and a custom `__eq__` method (which is called when comparing two values). This saves us having to write all of that code.

Other languages have a similar idea of a value type, and tools to help make them, such as [Java's record classes](https://docs.oracle.com/en/java/javase/17/language/records.html) and [C#'s' structure types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct).

{{<note type="exercise">}}
Write a `Person` class using `@datatype` which uses a `datetime.date` for date of birth, rather than an `int` for age.
Convert your existing `Person` class into a value type using `@datatype` so you can print the class (and see it's type and fields) and compare class instances that are identical. Make sure your `is_adult` method and `drivers_license_check` free function both work as normal.

Make a new method on your Person class - `greet` which should return `"Hello <person name>!"` when used.

Re-add the `is_adult` method to it.
Take a look at the [`@datatype` documentation](https://docs.python.org/3/library/dataclasses.html) - what does `frozen=True` do to the class? What other options could you play around with and explore?
{{</note>}}
36 changes: 30 additions & 6 deletions common-content/en/module/decomposition/methods/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ objectives = [
"Define a method.",
"Define a free function.",
"Explain why methods can be more useful than free functions.",
"Implement a method on a class.",
"Explain how encapsulation can benefit class design.",
"Amend a method on a class.",
]

[build]
Expand Down Expand Up @@ -38,7 +39,7 @@ class Person:
return self.age >= 18

imran = Person("Imran", 22, "Ubuntu")
print(imran.is_adult())
print(imran.is_adult()) # True
```

This has a few advantages over {{<tooltip text="free functions" title="Free function">}}A free function is a function that isn't a method. It isn't bound to a particular type (but may take parameters).{{</tooltip>}}.
Expand All @@ -50,13 +51,36 @@ Think of the advantages of using methods instead of free functions. Write them d

<summary>Expand for some answers after you've listed your own.</summary>

* Ease of documentation - it makes it easier to find all of the things related to a string (or a Person) if they're attached to that type.
* Encapsulation - if we change the implementation of `Person` (e.g. we start storing a date of birth instead of an age), it's more obvious what things we need to change.
- Encapsulation - if we change the implementation of `Person` (e.g. we start storing a date of birth instead of an age), it's more obvious what things we need to change.
- Ease of documentation - it makes it easier to find all of the things related to a string (or a Person) if they're attached to that type.
</details>
{{</note>}}

Consider this free function called `drivers_license_check` which uses the Person class method `is_adult` outside of the class:

```python
def drivers_license_check(person: Person):
if person.is_adult() == True:
return 'Valid drivers license'

return 'This person is underage!'

print(drivers_license_check(imran)) # returns 'Valid drivers license'
```

{{<note type="exercise">}}
Change the `Person` class to take a date of birth (using [the standard library's `datetime.date` class](https://docs.python.org/3/library/datetime.html#datetime.date)) and store it in a field instead of `age`.

Update the `is_adult` method to act the same as before.
1. Add the `drivers_license_check` free function and the `is_adult` method into your code just like above, make sure your code currently gives the expected final print.
1. Change the `Person` class to take a date of birth (using [the standard library's `datetime.date` class](https://docs.python.org/3/library/datetime.html#datetime.date)) and store the `date of birth` in a field instead of `age` (it should be a `str`). Don't change anything else.
1. **Try to run your code**, how does this change break your code. What kind of error do you get? Is it helpful in identifying where your next change needs to be?
1. Update the `is_adult` method so the error is fixed. Using the `drivers_license_check` function check everything runs as expected, it should return "Valid drivers license". _You should not change `drivers_license_check`_.
{{</note>}}

{{<note type="Encapsulation in play 👀">}}
Take a moment to consider what we've done here. How has **encapsulation** helped us make changes to our class?

We've changed a property of Person, seen errors inform us about how that change affected a method on the class, and then amended that method so we were maintaining the behaviour of the class. The behaviour of `drivers_license_check` did not need to change - we can change the internal implementation of the class without affecting external code.

_Encapsulation is a widely known principle in object-oriented programming, consider reading around in online to find out more_

{{</note>}}
Loading