1 of 21

Workshop 11: Dictionaries and Dataclasses

2 of 21

Topics

  • Dictionaries
    • What are they?
    • Syntax
    • Examples
  • Data classes
    • What are they and why use them?
    • Mutability
    • Examples

3 of 21

Dictionaries

First: What is a Dictionary? �How are they different from lists?

A dictionary is:

  • A collection of key-value pairs
    • The key and value are stored together
    • You use the key to get the value
  • Unordered (unlike a list)
    • (and unlike something like the Oxford English Dictionary :D)

number_translator =

{1: "bir", 2: "iki", 3: "üç"}

1

2

3

"bir"

"iki"

"üç"

4 of 21

When to use Dictionaries?

Maceo has a lot of friends but is incredibly forgetful. He wants to be able to wish people a happy happy on their birthday, instead of too late or too early. �Every day, Maceo puts in the date to find out who’s birthday it is.

What kind of data structure would we want to use here?

What would our keys and values be?

birthdays = {} # str -> list[str]

birthdays["Jul 18"] = ["Isabel", "Tim", "Sohum"]

birthdays["Jul 25"] = ["Harshini"]

birthdays["Jul 16"] = ["Aidan"]

birthdays["Jun 20"] = ["Monica"]

birthdays["Nov 19"] = ["Darryl"]

birthdays["Nov 21"] = ["Ben"]

today = "Nov 21"

birthdays[today] #returns "Ben"

5 of 21

Dataclasses

This is how we define our own custom data type in Python (analogous to a data definition in Pyret)

Let’s say we want to make a custom data type to represent a Candy Store ...

In Pyret …

data Candy:

| candy(name :: String, flavor :: String)

end

data Store:

| store(owner :: String, candies :: List<Candy>)

end

In Python …

@dataclass

class Candy:

name : str

flavor : str

@dataclass

class Store:

owner : str

candies : list

6 of 21

Dataclasses

In Pyret, having two constructors is OK! But in Python… each data class has only one constructor.

We access data inside of a data class by using a .

Ex: To get to my Truck’s brand: my_truck.brand

More specifics...

data Vehicle:

| car(brand :: String, hybrid :: Boolean)

| truck(brand :: String)

end

@dataclass

class Car:

brand : str

hybrid : bool

@dataclass

class Truck:

brand : str

@dataclass

class Truck:

brand : str

my_truck = Truck("Ford")

7 of 21

Dataclasses are mutable...

What does that mean?

Each instance of a data class (or specific piece of data) can change!

So… what can we do with sour_candy? Will it always have the name Sour Patch Kids and flavor sour?

How can this be helpful? How can it be tricky?

from dataclasses import dataclass

@dataclass

class Candy:

name : str

flavor : str

@dataclass

class Store:

owner : str

candies : list

sour_candy = Candy("Sour Patch Kids", "sour")

8 of 21

Example Scenarios:

Let’s write a few functions to change our Candy Stores:

update the owner

add candies to the store

remove candies from the store

update the flavor of one of our candies

Do we need these functions to return the updated stores? If so, why? If not, what will happen inside of these functions?

9 of 21

What if the store comes under new management?

10 of 21

What if the store comes under new management?

def update_owner(our_store : Store, new_owner : str):

"""changes owner of store to new owner"""

our_store.owner = new_owner

11 of 21

What if our store grows?

12 of 21

What if our store grows?

def add_candies(our_store : Store, new_candies : list):

"""adds new candies to the store"""

for current_candy in new_candies:

our_store.candies.append(current_candy)

13 of 21

What if some of our candies go bad and we need to get rid of them?

14 of 21

What if some of our candies go bad and we need to get rid of them?

def remove_candies(our_store : Store, to_sell : list):

"""removes candies from the list of candies in the store"""

for current_candy in to_sell:

if current_candy in our_store.candies:

our_store.candies.remove(current_candy)

15 of 21

And the most pressing question … what if the flavor of a candy in the store changes??

16 of 21

And the most pressing question … what if the flavor of a candy in the store changes??

def update_new_flavor(our_store : Store, name : str, new_flav : str):

"""updates the flavor of one of the candies"""

for our_candy in our_store.candies:

if our_candy.name == name:

our_candy.flavor = new_flav

17 of 21

How might we go about testing these functions?

Since our functions are not returning anything, we can’t test that the output of our function is equal to something …

What could we do instead??

18 of 21

How might we go about testing these functions?

Since our functions are not returning anything, we can’t test that the output of our function is equal to something …

What could we do instead??

Instead, we can test that the fields of our data class have changed properly (don’t forget to also ensure that the fields that shouldn’t change stay the same!!)

19 of 21

Let’s look at an example of this

If we wanted to write a function to test the update_owner function we wrote earlier, we might do something like this …

20 of 21

Let’s look at an example of this

If we wanted to write a function to test the update_owner function we wrote earlier, we might do something like this …

def test_owner_update():

store1 = Store("Willie Wonka", [Candy("gum", "blueberry"), Candy("egg", "chocolate")])

update_owner(store1, "Charlie")

test("testing change of ownership", store1.owner, "Charlie")

test("hope candies didn’t get stolen", store1.candies, [Candy("gum", "blueberry"), Candy("egg", "chocolate")])

21 of 21

Questions??