Everything you need to know but were afraid to ask about Data Classes by Casey Faist

This video features Casey Faist at DjangoCon US 2019 in San Diego, California, USA.

Everything you need to know but were afraid to ask about Data Classes by Casey Faist
0:26:14
Published October 25, 2019
1,807 views

DjangoCon 2019 - Everything you need to know but were afraid to ask about Data Classes by Casey Faist

You know you're curious. How are data classes different from other Python classes? Why can't I just use a dictionary? How do I even use these in my existing applications? Come on a cross-language comparative journey to discover just what are, and how best to use, Python 3.7's classiest new feature.

This talk was presented at: https://2019.djangocon.us/talks/everything-you-need-to-know-but-were-to/

LINKS:
Follow Casey Faist 👇
On Twitter: https://twitter.com/cfactoid
Official homepage: https://heroku.com/python

Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Intro music: "This Is How We Quirk It" by Avocado Junkie.
Video production by Confreaks TV.
Captions by White Coat Captioning.

Summary

Casey Faist explains data classes by tracing the problem they solve: Python developers often need a clear, flexible way to represent C-like records, without the immutability of named tuples or the boilerplate of manually written classes. A data class uses a decorator and annotated fields to generate initialization, representation, equality, and other comparison methods, while allowing options such as defaults, immutability with `frozen`, and custom behavior. Faist shows how this reduces repetitive dunder methods, contrasts data classes with tuples, named tuples, and the `attrs` project, and describes them as useful state containers and event objects rather than classes intended for heavy data transformation.

Key takeaways

  • Data classes address the need for readable, flexible record-like objects that are more capable than named tuples and less repetitive than hand-written classes.
  • The `@dataclass` decorator can generate `__init__`, `__repr__`, equality, and optional ordering methods from annotated fields.
  • Data classes are mutable by default, but options such as `frozen` can make them immutable; Python 3.6 users can use the backport, while they are in the standard library from Python 3.7.
  • Type annotations on data-class fields are not enforced by Python itself, but tools such as mypy can use them for static checking.
  • Inheritance and field overriding have specific behavior, and features such as `__slots__` and some customization must be added deliberately.
  • Data classes work well for passing application state or representing events, but are less suitable for objects whose main purpose is complex data transformation.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction to Data Classes The talk frames why data classes can be difficult to understand and sets up the journey toward using them.
  2. 2:36 The C-Struct Problem The speaker traces data classes to the need for a convenient, flexible way to define C-like structures in Python.
  3. 9:38 The Manual Wallet Class A wallet example illustrates the boilerplate involved in initialization, comparison, hashing, and representation methods.
  4. 13:19 Data Class Features The speaker introduces data classes as mutable named tuples with defaults and automatically generated methods.
  5. 15:10 Tuples, Named Tuples, and Data Classes The talk compares ordinary tuples and named tuples with data classes and explains when each is appropriate.
  6. 16:44 Defining a Data Class The speaker builds the wallet example as a data class using fields, type annotations, and a regular method.
  7. 18:22 Generated Code and Methods An inspection tool reveals the initialization, representation, equality, and comparison code generated by the decorator.
  8. 19:07 Inheritance and Slots The speaker explains data-class inheritance behavior and introduces slots as an optional memory optimization.
  9. 21:25 Data Class Use Cases The talk identifies data classes as useful state objects and demonstrates their use as an event system in a game engine.
  10. 24:35 Questions The speaker answers questions about type annotations and whether data classes enforce them.

Transcript

3,603 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:15

Speaker 1: So, real quick, it me. Um, Casey faced, first of her name, mother of sinks, at Heroku. If you have uh questions, compliments, or complaints about deploying to Heroku, I am all yours Come and find me. Um another pastime is making friends with designers and conning them into making the most adorable snake logos. So I'm open to questions on both of these subjects after the talk. Okay. This talk is labeled Everything You Want to Know, but we're afraid to ask about data classes. So I'm gonna set the scene. We're gonna go on a little journey together to build the empathy for what exactly that means.

1:01

Speaker 1: So you're at your local Python meetup. You have pizza. You're sitting there listening to a talk or lightning talk or working on a project and somebody rolls up to you. Data classes, they say. Data classes, they say somebody else comes over and is like, oh, they're awesome. Have you used them? Have you tried them? And if you're anything like me at this point in the story, you have like this piece of pizza just hanging out of your mouth and you're like, uh? What? Um Data classes are what I've seen at meetups and kind of in talking to people. They're um they're not a complicated feature. They're not that they're not a complex feature. But they're difficult to wrap your head around without the proper context.

1:48

Speaker 1: Like what are they for? You know, where did they come from? Why? Is it a class? Is it what are we what are we dealing with here? Um, and I don't think I was the only one who kind of noticed or felt a little surprised. So let's let's kind of unwrap unwrap the story a little bit. At this stage, you're at the perfect step for your journey towards data class knowledge because we now have recognized that we don't know what they are. Excellent first step. So With a subject like this, there all the Python features that roll out, they have PEPs, they have blog posts, they have

2:36

Speaker 1: context and Twitter discussions and all of this content and kind of sifting through. How do you sift through? What do you what are you looking for to kind of get that context? For me, when I try to do this, it always starts with somebody had a question or a pain point. Somebody needed something, and that's where this came from. So after a little bit of digging I ended up finding a stack overflow post. Surprise, surprise. Um that's four years old. It's got 400k views. It's been going on for years. There's just tons of content. And the question is this Is there a way to conveniently define C-like structures in Python? That's it.

3:21

Speaker 1: The field type has been available since Python 2. 7, but this discussion went on to talk about all the weird and kind of wonky and just not quite right ways that people have solved this problem. In the past. Um my experience with C is somewhat limited, but looking at this thread is really informative for me as a Python developer, trying to wrap my head around what this feature is. Previously the named tuple was considered one of the the best ways to address this specific need, or a good way. I know we only have one way of doing things in Python, but um this is not it's not the only thing you could want. It's a little bit inflexible, it's immutable. There are different different

4:06

Speaker 1: needs you could have of a of a data store like a C strict. And when this post back last October, as of last October This post was updated with, hey, this is the answer. If you're looking for a C struct in Python, a data class is that. It provides you flexibility. It syncs with the typing module. It plays nicely with a lot of the new features kind of rolling out around attributions and all that stuff. So try this one. Okay. Some context. Is there more? I don't have the C background, so that wasn't quite enough for me to sink my teeth into

4:57

Speaker 1: this. Indeed, there are more layers to this problem. Because another thing that was discussed as part of the uh part of the data class's release was wanting JavaScript dot notation. I know blasphemy, but right now, if you want to use a dictionary, you want to sign something, you create your dictionary, then you have to use this bracket quote syntax, right? If you are familiar or have used pandas, you know that they kind of do some magic to get around this and use dot notation. And it's kind of nice.

5:43

Speaker 1: It's just like a nice to have, right? the equivalent in JavaScript, I know she'll avert your eyes if necessary, you get that object. cat gives you directly what you need. It's just nice. Right, we can have nice things, right? Another important piece of context and the closest down this chain is the Adders project. Who's familiar with the Adders project? Cool. Alright. This is means I can share something exciting. That's awesome. So the Adders project, um provides a way to boilerplate your classes. So you have a regular old class, you've defined it, and you would like some boilerplating around

6:34

Speaker 1: equal or um hashing or X number of things dunder boilerplating essentially. And um I actually found when I looked at their documentation they have a really good um discussion on the why not page of that website. So if you're interested, go there. And they actually talk about like why would you not use a tuple or a name tuple instead of the Adder project. And because data classes are based on the implementation is based on the Adder 's project, the reasoning kind of transfers. So why would you use a data class instead of a name tuple? Some of the reasons are readability,

7:20

Speaker 1: sharing code, the clarity, the explicitness. So if you're curious about that, go check it out Um the first time I gave this talk, I realized that I didn't actually describe what a dunder method is, even though this is a central point to what a data class is giving to you. And I wanted to take a step back and like clarify this. So a dunder method is, and I didn't hear it pronounced. out loud until far too late. So it is dunder, which is very fun. You have a method that is on top of a class and you have two underscores

8:06

Speaker 1: So if you ever write if name equals main with the two underscores on each side, those are dunders. And what dunders do in Python, very high level, this is not a dunders talk But they provide some of that magic that lets us type at a high level and get compiled down to C. and massive errors do not occur. Like it knows roughly what to do if you try to compare, if you try to concatenate, for example, um a string and an integer, it will know to Make them both strings and put them together and give you a new string back with what would be that int, right? That happens because of Dunder magic. It's a deep dive, but um it's important for this because

8:52

Speaker 1: in some cases you end up writing if you hit these specific bugs that we're gonna talk about You hit them and they're very frustrating and they're small. But oh they're just they're it could be better. And now it can be very easily Oh, just an example. Name equal reper. Those equal and reper are going to be important here in a second. Okay, so we've gone through some layers. We have just a little bit of context on where this has come at us from. Now let's put it together. Why why not the other tools in our current Python toolkit?

9:38

Speaker 1: What does this add to our current set? This is all cool, but what does that what does it do for us? Before data classes. An example. So I would like to make a wallet. I 'd like to make a class wallet. And I need it to do a few things. Things that you would expect a wallet to be able to do. I want it to be a class so it can inherit. It can use any kind of special class things that I need to do. It contains coins. It has qualities that likely won't change

10:24

Speaker 1: and it has a total amount. I would like my wallet, my very special wallet, to tell me exactly how much money is in it at one time So let's write this. What would this look like as a class? Starting off. Kind of checks out. You need to define an initialization. You need to set all of these attributes on your class. And then we have a custom function for the amount in the wallet. And that is just, it's a long line, but it's just nickels times five, dimes times ten, pennies times one, because explicit and then divided by 100 because dollars, not cents. So this is where you might start. But after a while, you might hit some problems

11:15

Speaker 1: For example, if you are working with a class and you want to be able to compare wallets. So you have Casey's wallet over here and Veronica's wallet over here. You'd like to be able to compare. Do they have the same number of dimes? They have the same number of pennies It is occasionally helpful, because of hashing, we'll get to this in a second, to actually define your own equals. This is another one of those magic classes, and Python allows you to reach down and make your own magic dunder methods that enforce a specific way for this to happen. And essentially what this does, it allows you to say, mywallet. nickels. Is otherwallet.

12:00

Speaker 1: nickels, and it'll give you a Boolean in return. Okay, that's allowing that to happen. This gets a little bit tricky though because remember this is mutable. Name tuples, using a tuple for this is immutable. And because it's mutable, our hash could potentially change. So our equals can get wonky. This can lead to uh variables that look the same but are not because their pointers aren't in in different locations. Is your hash is an integer. So because of that you might find that it's necessary for this class to enforce this behavior. You keep hitting this bug with a specific part of your API over here and you just need it to behave reliably.

12:47

Speaker 1: So let's add that. And also I the text is getting small. I apologize. But When you call the values of your um, if you're trying to print one of your values, so you want to know how many pennies are in your wallet. There is a behavior with the repper function, not the Dunder repper, but the regular repper function where it will give you back Double quotes. So you'll get one for pennies. Say we have one penny in our wallet. You'll get quotes and then double quotes. And for parsing, this is kind of annoying, right? It's not the end of the world, but you'll find yourself across your code base just like countering for that.

13:33

Speaker 1: So say you want to cut it off at the source And you define your own overwriting uh Dunder Repper method. These are niceties. Um we just went through a lot of work for like things that are just kind of nice and just kind of um you know Nice to have and like nice to nice to use. C Python data classes. So this is the PEP, and I'm bringing this up just because I found it really helpful actually when when going through this. There's a lot of text up there, but the key points. R. It's a mutable name tuple

14:19

Speaker 1: with defaults. You create a data class the same way you'd create a regular class and add a decorator. This decorator, it's literally called just at data classes, provides generated, auto-generated methods. So that's code you don't have to write. It initializes your class for you. It overwrites that wrapper, so you'll get printed lovely strings out, and you can use that dot notation more. And you automatically get those comparison methods and optionally more. You can optionally freeze your data class and make it immutable. You can optionally, there's there's a lot actually of different options you can pass in in that data class's parameters list.

15:10

Speaker 1: Taking a big step back. A normal tuple looks like this. You use the parentheses, and then you get something that you can reference by index. Immutable, except when it's not, as discussed by Al Sweiger. Another talk I recommend. A named tuple kind of similar to what we're functionally, the shape of it is somewhat similar. You can create a name tuple called wallet. You can give it a name, which is that first parameter, and then you can tell it that it has named categories within that tuple. And then when you use the wallet kind, because it's an inheritance pattern here, you get a my purse

15:56

Speaker 1: and you can set dimes. And you can see that that's three. So that gives you that dot notation and the categories. And it's simpler. It's a simpler way to kind of get at that function, right? And there are some defaults that allow you to kind of play with what the the name tuple can do for you. There are a lot of cases where this is the right choice. But you need more flexibility, you need more control, you need it to be explicit Then there are data classes. So let's let's make one. Let's make a data class here. We start the same way we'd start any class definition with class. my wallet, my generic container, so we can inherit, and doc strings to describe my class.

16:44

Speaker 1: The next step, you add that data class decorator, and then You can give it directly what your fields are going to be. In data classes we call these attributes fields. You'll see in a little bit they expand to have their own kind of special data class fields um category. And you can use the class attributes, function attributes, and the type in attribute uh a little l attributes um from the get go much quicker we have initiated our class done Next, I'd like that custom function.

17:33

Speaker 1: So we're going to define the mountain wallet. It's the same function. We just stick it in. Completely valid. Um, this is actually available in three six as well. Um You can backport it using from data classes import data class. It's in standard lib from 3. 7 on. So if you're pre-3. 7, uh this is what uh you'll need to do and it'll work exactly the same. Now, one thing that the Standard Library does not provide is a way to look at what this generates. But there is a handy package that will, if you start playing around with data classes and you're like, what is this generating on the back end?

18:22

Speaker 1: What is the code actually going to run? There's a package by Damla Altun. And this package will take your data class and inspect it and print out the code that it's generating on your behalf that you don't have to think about. And I apologize for this next slide, but it's a lot. But look at all this code we didn't have to write. Look at this. We have our cleaned up repper. We have equals. We can optionally have greater than, less than, equal to, all those other comparisons.

19:07

Speaker 1: Yeah, it's this is a lot of work done for us. This is nice this is nice thing. We have a nice thing here. There are always some gotchas, and this is no exception. Inheritance is just a little bit different than you might expect. So if you're going through you're making data classes and you have data classes inheriting from and data from data classes. It might be just a little bit different. This is from the abstract, but to highlight, it starts at your target object and it goes backwards And the way that it inherits the different fields, these attributes for your classes,

19:52

Speaker 1: it will overwrite with the Newest. Yes, it'll override the base class fields, which may or may not be what you want. Be aware. In most cases, that's that's not going to, you know, it's it's an edge case thing to remember if you start seeing seeing something weird This is an example of that behavior. This is directly from the PEP. You can go, I just reformatted it so it was obviously easier to see with a bit dark background. But um Essentially it will overwrite that later integers if you're reusing. Just be aware.

20:39

Speaker 1: A cool thing though that this allows you to do, but does not automatically provide, is the Dunder slots method, which is memory optimization. This allows you to um Very easily because we're writing so much less code. You're gonna just stick it in there. It's still explicit what the class is trying to accomplish, but it allows you to say, hey data class, take these data fields. um and don't always load them into RAM, stick them down and you know, only call them up when when needed. It it optimizes at that level. I'm not um I'm not a slots expert, but It is I don't know it looks a lot cleaner to me than other implementations I've seen.

21:25

Speaker 1: I think that's a strength here. But you have to do it yourself Cool. How do we use them? So data classes are not ideal if you have a class that needs to do lots of like transformations and uh a lot of heavy lifting on the data that you're passing it. This is Essentially a state object. Do you have a piece of information about what should happen or what the current state of an API call is or what the current state of X process in your application, this is a good way to kind of pass that around and either mutably update it or immutably track it.

22:12

Speaker 1: Um it's customizable, so name tuples, there's an argument to use them in a similar way, but um you can get a lot more fine-grained with how you're using these in your applications. And they're a time saver. It's a lot of code that you just get for fun We study. Alright. We have just five minutes, so I'm gonna bring this up But Pursuit Pipear is a an open source game engine that is using data classes as the event system. So they have a game loop.

22:58

Speaker 1: They go through and each data class creates an event in an action that should happen in that game loop. And let me see if I can make this big enough to see That is not bigger. Ah! Okay, it's bigger now. Too much. Okay. Do do do to do. There we go. Okay, so you can see this is compatible with 3. 6. It's very important. But you come down here. So in this case, a button was pressed. Button. It's a mouse button. It's got a position. It's got a scene on which it's a base. When the button's released.

23:45

Speaker 1: Same thing, updates start scene So these are all, if you're not familiar with game development, these are processes that might take a little bit more orchestration. There might be JavaScript has this idea of listeners that you're listening for events and things like that. like that but this allows you to kind of have an active system so one cool way to use data classes in your project. No my present okay, no we're good. I was worried the presentation was gonna be huge too. Alright. Go get classy, y'all.

24:35

Speaker 2: In the definition for the type classes, you are there there is a uh the syntax you were showing has type annotations.

24:41

Speaker 1: Yes.

24:42

Speaker 2: Is the type annotation enforced in any way or is it just there for documentation purposes or what role does that play?

24:49

Speaker 1: Gotcha. So it is not enforced. This is concurrent with um static typing in Python, the plan is not that core Python is going to enforce this ever. And this does not conflict with that. It allows you to um use a static typing enforcing system like my pi or something similar to um In this in this case you could like enforce it on your data classes to have an extra layer of validation, make sure the data that you're passing around is correctly typed, giving you the output you're expecting, all that stuff. So not enforced Optional, also nice.

25:28

Speaker 2: Optional in the sense of you don't have to annotate at all or that you you have to annotate with any I honestly don't care, it's just gonna be stuff.

25:36

Speaker 1: You know, I haven't written any without annotations. So let me do that and I'll let you know if it blows up.

25:47

Speaker 3: Any more questions? All right. Ms. Casey will also be available outside in the hallway if you wanted to have longer conversations. Let's give her another round of applause.

Questions this talk answers

What are Python data classes, and what do they generate automatically?

A data class is a concise class-based data container, similar to a mutable named tuple but with defaults and more flexibility. Its decorator can generate initialization, a readable representation, equality and comparison methods, while also offering options such as immutability.

Discussed at 13:33

When should I use a data class instead of a named tuple?

Use a data class when you need more flexibility, control, or explicitness than a named tuple provides. Data classes work well for passing around application state that may be updated mutably or tracked immutably, rather than for classes doing extensive data transformation.

Discussed at 15:56

How do I define a Python data class?

Define a normal class, add the `@dataclass` decorator, and declare its fields with attributes and type annotations. You can then add regular methods, such as a custom method for calculating a wallet’s total.

Discussed at 16:44

How does inheritance work with Python data classes?

When data classes inherit from other data classes, inherited fields are collected starting from the target class and moving backward through the hierarchy. A field in a newer or more derived class overrides a base-class field with the same name.

Discussed at 19:07

Are type annotations in Python data classes enforced at runtime?

No. Data class annotations are not enforced by Python itself; they can instead be checked with a static type-checking tool such as mypy.

Discussed at 24:49

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon US