What advice would you give someone just getting into Python?
What did you learn over time through hard work and a few tears that would have really helped you?
It's a fun game to play, and we have Jason McDonald on the podcast to give us his take.
This is Talk Python to Me, episode 411, recorded March 2nd, 2023.
Welcome to Talk Python to Me, a weekly podcast on Python.
This is your host, Michael Kennedy.
Follow me on Mastodon, where I'm @mkennedy, and follow the podcast using @talkpython, both on fosstodon.org.
Be careful with impersonating accounts on other instances.
There are many.
Keep up with the show and listen to over seven years of past episodes at talkpython.fm.
We've started streaming most of our episodes live on YouTube.
Subscribe to our YouTube channel over at talkpython.fm/youtube to get notified about upcoming shows and be part of that episode.
This episode is brought to you by Sentry and us over at Talk Python Training.
Please check out what we're both offering during our segments.
It really helps support the show.
Jason, welcome to Talk Python to Me.
Thank you for having me.
Yeah, it's great to have you here on the show.
I'm looking forward to talking about a bunch of things I wish people taught you about Python sooner.
I think there are many, many things that fall into that category.
And part of that, I guess it's true with all languages, but I think it's maybe a little extra true in Python because getting started is so easy.
But getting really good at it takes a lifetime sort of thing, you know, and so there's like this big mismatch of how accessible it feels.
But then what you can get out of it if you go further.
Right.
So I think that we'll touch on that going further part, maybe.
Absolutely.
Yeah, for sure.
Now, before we get into all those things, let's start with your story.
How did you get into programming in Python?
Well, I got into programming by a route that I don't recommend to anybody.
I fell down a staircase, hit my head on the banister and ended all hopes of ever becoming a doctor.
Oh, so went from 4.0 straight A student, college level reading in high school to family pre-game material.
Two extra years to climb back up where I was academically a bit longer after that for other things.
But somewhere along the way, I discovered that I had a natural inclination towards an attack for coding and got into that because I wanted to make games.
I wanted to make educational games.
They had been part of my recovery.
So I'm like, I want to make games.
So I learned how to code.
And so I taught myself BB.net and then felt very constrained by the .NET ecosystem and broke out into Python.
Found my home, stayed put until I wound up going into a bunch of other languages.
I used Flash for quite some time.
I used C++.
I'm not coming out in whatever, Sandy, but Python is definitely near to dear to my heart.
Yeah, absolutely.
Well, if you started out in BB.net, I think it's a little less true these days.
But in the earlier days, that was a really different ecosystem than Python, right?
There was like, well, what is the framework for this thing that Microsoft recommends?
Like, how do I do the web?
I do ASP.net because that's what they provide me.
How do I do database?
I do this because that's what they provide me.
And you come into Python, you're like, wait, there's a thousand different ways to do this.
And I have to pick and decide and I can mix and match.
And it's both amazing and like.
Terrifying.
Yeah, it's terrifying.
Like, what do I make the wrong choice?
I've how do I decide?
I have no basis, right?
I'm both new.
So I have no basis for deciding.
But I also, I've got to decide to get started.
So where do we go, right?
Like, it's a different world.
It is where I first started running into trouble with the paradox that I wound up addressing in my book on back of that.
But the paradox that a lot of people find themselves in is like, I know all this stuff, but I don't know enough to do this thing.
And so you wind up with this weird little gap in a lot of training material because you have like the absolute basic elementary.
And the examples there are almost insultingly simplistic.
And it's like, oh, you just have to type this stuff into this file and you run the file and look at that.
You have a hello world.
Aren't you smart?
And then you go all the way to the extreme and it's something like super complicated.
You don't understand any of it.
And nobody sat and wrote down.
Here's how you structure your multi-Python project in a way that's going to, at least when I started, that wasn't documented anywhere.
Like no one had ever written it down.
So it's just like, what do I do now?
Yeah, it's really tricky.
A lot of the tutorials and stuff are like, you just put all the stuff into one file and then it's magic.
But you should never, ever do that.
Well, maybe in simple cases, but in realistic apps, like no realistic app belongs in one file.
Just like you wouldn't have one function or just like no functions, right?
You're like, not really.
That's not how you do it.
But so there is this, even when you're sort of hunting through tutorials, there is this mismatch of the simple way to show it, the simplest way to show it.
And then like maybe how you should.
Yeah.
Yeah, exactly.
It's the example I've given is that there's two types of book slash article slash tutorial.
out there.
There's here's basics of variables, voice and curls.
And then the next level after that is here's how to program a natural language recognition, OCR, AI, machine learning on a Raspberry Pi, your grandmother's base or running on a potato battery.
And there's nothing in between.
Yeah.
Yeah, that's true.
It does make it quite challenging.
All right.
How about now?
What are you up to?
These days?
Oh, gosh.
I'm actually kind of veering these days more into actually more into kind of the facilitation business analyst side of things more.
Interestingly, the last last couple of years, that's become a point of particular interest.
But, you know, in terms of coding, I'm working on I'm working on some interesting projects that, you know, at work building, you know, an API for an internal project.
And that's been that's been fun getting to get my hands dirty with FastAPI and SQLAlchemy.
And that's been that's been fairly enjoyable.
So that's been the majority of my programming bandwidth lately.
But it's fun.
It's it's the sort of project you hope you're going to get at work because it's an interesting topic.
You have enough love leeway.
That's where we have enough leeway to do things right and do things to some degree your way if you can justify it well enough.
And, you know, other tech lead on it.
So I'm able to make sure, like, you know, we're making good decisions early and paying down technical debt and building something that's really, really cool.
So it's it's a fun project.
I'm enjoying it.
Yeah.
Those kind of projects, they're they're fun, but they're also important.
Right.
Like if your work, if you work, if you work at a place where they say, you know, you need to use 10 year old technology and there's no opportunity to try new things like FastAPI and other others.
What you end up with is a whole bunch of people who that work there who don't want to learn any new technologies and don't are not deeply passionate about programming.
And it's kind of the self-fulfilling prophecy.
And I think it's cool that the place you're working at allows that.
Right.
Because I think it's it's one of the signs of a healthy tech, not necessarily a startup or whatever, but all these companies have like small software teams within it.
It's a sign of a healthy small software team, even if it's just building internal software.
Oh, definitely.
Yeah.
And, you know, and there's a place for older technologies.
Well, it's good to be well-versed in both and be able to move between it.
But I mean, yeah, if you're if you're just stuck in the past, then you either, like you said, get a lot of people that are afraid of change or are really wanting that change.
They're dissatisfied with the fact that you can't get it.
But on the other hand, if you just if you're only new technology, you'll just spontaneously combust because you can't.
You know, there's just there's so much constant invention and reinvention and research and unexpected bugs and blockers and whatever.
It's like sometimes the nice thing about a 10 year old library is that there's less bugs because it's years old.
Or you can find documentation about issues or whatever.
You know, they have a term.
It's like a term in the software industry about this constant churn that's like going too quick and especially not appreciating stuff that's been around.
It's called JavaScript.
You're not wrong.
JavaScript is the embodiment of shiny object syndrome.
It is.
I mean, I'm not bashing on JavaScript per se, but the life cycle of some of these frameworks is just so brief.
You know, it's nuts.
Everybody wanted to use Dino for about 10 minutes.
I remember that.
There was a thousand articles a minute on Dino.
And now it's now we've moved on to other things.
And it's funny because you get a new technology like, OK, the topic of the minute, ChatGPT.
And everybody on LinkedIn is an expert in ChatGPT.
You know, have you noticed that?
Every other post is about ChatGPT.
And it's like, well, it's interesting, but it's just a bit early.
And the early adopters have a lot of egg.
They're still trying to wash off their face in the last six fans.
So it's like, let's see what happens.
But chances are, this is just an early hiccup.
Yeah, for sure.
So you mentioned FastAPI and SQLAlchemy.
And Yasser out there says SQLAlchemy is the best library.
SQLAlchemy is pretty great.
And it's cool to see Mike Bayer and crew updated to 2.0 and add async and await to it and stuff.
But there's also, especially when you're in Pydantic world, say with FastAPI,
there's a SQL model also from Sebastian Ramirez that says,
hey, we are already doing Pydantic for the API or front-end exchange.
Like, what about making that your database?
Did you all evaluate or consider a SQL model?
I did.
It's one of those things like, this would be great.
And I think we can move towards that later.
But sometimes when you're paying down technical debt, you have to make a distinction between,
do I want to make the monthly payment as it were more than a monthly payment?
Or do I want to try and pay it all off in one thing?
And sometimes when you're having to budget that time, you know, for us, it made the most sense to migrate from the 1.0 that we were working with earlier on to using, you know, SQLAlchemy 2.
And so there was a relatively smaller set of changes that we needed to make.
I see.
Moving to something like a SQL model would be great, but it would also require us to rewrite some major things.
And so that's one of those things is like, well, this might be a good option.
I mean, it looks really cool, but let's defer that for right now so that we can actually get some stuff done.
It's a constant balancing act.
Like, do we pay down technical debt or build new features?
And the answer is yes.
You have to do both, but you have to keep it in balance.
Pay down the credit card, but also make sure you're buying your groceries, you know?
Yeah, absolutely.
I feel like if you wanted to go that way, the change is pretty straightforward.
But if you already have it all set up in the other way, again, it's like, eh, what is the value?
Just one thing that kind of timely as of a couple days ago, let's see, as of two days ago, the Pydantic folks, Samuel Colvin and Terrence Dorsey,
posted, hey, we're excited to announce the first alpha of Pydantic v2.
And I had Samuel on like a year ago to talk about what they were doing there.
Wow.
But the big news is, the headline news in here is, they rewrote much of the core of Pydantic in Rust.
And now Pydantic version 2 is 5 to 50 times faster.
Nice.
That's cool, right?
That's gorgeous.
Thank you.
This has just put more work on my plate.
That's okay.
This will be good.
I'll have to bring this up at work.
It's like, hey, we should look at this later.
Yeah.
I mean, I don't think that there's any, I don't think anything will, obviously nothing will change for your SQLAlchemy side.
But even for FastAPI, I'm sure that this is going to get rolled in in a smooth way.
But it does mean that all that data exchange should just go way, way faster, which for basically no effort on users of things like FastAPI and SQL Model and Beanie and all those.
So that's cool news.
Yeah, definitely.
That is neat.
Other than the fact that if you do a lot of work directly in Pydantic models, there might be a few small breaking changes.
But let's talk about our things.
The things we wish someone had explained to us sooner.
Yes.
And I guess we can start with this.
Like these things were motivated.
We'll come back and talk a bit more about this at the end.
But they were motivated.
This whole conversation was motivated.
This big, long book that you wrote here, Dead Simple Python.
Give people the elevator pitch and then we'll get to all things.
Yeah, exactly.
So Dead Simple Python is the book I wish I had.
And yeah, it's, I get teased a lot.
It's like dead simple and it's that thick.
But it's dead simple in the same way that Python is obvious.
It's obvious retrospective.
Things are dead simple.
Once you understand it, you look back and go, oh, okay.
That clicked.
That just makes sense.
A lot of things in Python are like that.
You look into it.
It's like, that looks really complicated.
How is this ASIC supposed to work?
And then you look back and go, oh, that's all?
Okay.
No, that's.
Yeah, yeah, exactly.
Why was I afraid?
Why did I put learning, put off learning this for six months?
Exactly.
It's, you know, things look more intimidating than they are in practice.
But basically this is the book for those persons who already know another programming language,
or maybe they've even been working with Python for a while.
Like they're familiar with the basics.
They do not want to have their hand held through the, you know, this is what a variable is.
And this is what a function is.
They've been through this probably more times than they want to admit to.
And they want to get into what's different about Python and how to use Python as Python.
I think that's kind of the core of the book is there's a big difference between Python code
that works and Python code that makes the most of the language and its patterns, what we would
refer to as Pythonic code.
And there's a big difference between the two because it's very tempting to come in and write Python as
BB dot that in my case, because that's where I started or write Python as C or write Python as Java or write Python as Ruby.
And we wind up holding ourselves back and having really convoluted and hard to maintain code because we don't understand the patterns.
So the approach I took with the book is let me explain why we do things this way.
Let's go all the way down into the internals and how this actually works under the hood.
So by the time you get to the forward with statements, you know, the meta class or whatever, you're like, oh, I understand what this is, why it's there, how it's used and when not to use it.
Yeah, that's a great service.
So you're saying if I created a class called like user factory and it implemented I user factory and then I would use dependency injection.
I might be doing that wrong.
You might be writing C# and Python.
Yeah, indeed.
This portion of Talk Python to Me is brought to you by Sentry.
Is your Python application fast or does it sometimes suffer from slowdowns and unexpected latency?
Does this usually only happen in production?
It's really tough to track down the problems at that point, isn't it?
If you've looked at APM application performance monitoring products before, they may have felt out of place for software teams.
Many of them are more focused on legacy problems made for ops and infrastructure teams to keep their infrastructure and services up and running.
Sentry has just launched their new APM service.
And Sentry's approach to application monitoring is focused on being actionable, affordable and actually built for developers.
Whether it's a slow running query or latent payment endpoint that's at risk of timing out and causing sales to tank,
Sentry removes the complexity and does the analysis for you,
surfacing the most critical performance issues so you can address them immediately.
Most legacy APM tools focus on an ingest everything approach, resulting in high storage costs, noisy environments,
and an enormous amount of telemetry data most developers will never need to analyze.
Sentry has taken a different approach, building the most affordable APM solution in the market.
They've removed the noise and extract the maximum value out of your performance data while passing the savings directly onto you,
especially for Talk Python listeners who use the code Talk Python.
So get started at talkpython.fm/sentry and be sure to use their code Talk Python all lowercase
so you let them know that you heard about them from us.
My thanks to Sentry for keeping this podcast going strong.
So I like the idea of it.
Let's talk.
Let's go through the topics.
We both contributed a little bit.
Mostly, I'll throw in a few if we got extra time.
Yeah, yeah.
So the first one has to do with this, I guess, like really early stage writing code, like how do I create variables?
What are the deals with types and variables, but also types and objects?
Tell us about that.
Yeah, exactly.
How names and values actually work.
And this is something that I had to learn early, but I didn't fully understand.
So when I first logged into the Python IRC room back when I learned this language, you know, back in the Cretaceous period, I logged in and I, you know, I'm coming from a strongly typed background.
So I'm saying, okay, so how do I declare a data type on a variable?
Because I'm used to VB.
I'm like, yeah, dim in the name of the variable, the type equals whatever.
How do I declare a type?
It was before type hints existed.
And one of the guys in the room goes, you're a data type.
And I'm like, sorry, what?
And he goes on to explain, well, it doesn't quite work this way in Python, but I didn't realize just how different it was.
So because I just sort of had this vague, like, oh, you just have the name equals the value.
Cool.
Type still seems kind of important.
Maybe I should start doing Hungarian notation.
Don't do that.
By the way.
S Z username.
No.
Exactly.
Int age or whatever.
Yeah.
And I thought I was being super clever and I wasn't.
But eventually someone explained to me, I think it was actually Ned Batchelder.
Someone gave me the link to Ned Batchel.
And Batchelder's talk on this is that Python's variables work very differently than other languages.
So much so that some smart elics like to say, well, Python doesn't really have variables.
Yes, it does.
But they're variables in a different sense.
You have names, which are just like they're like labels and you can call it whatever you want.
But a name has a scope.
You know, so the name exists in the context of your whole program or in the context of your function or whatever.
And a name can be bound to whatever you want.
Imagine drawing, you know, basically having this label on a corkboard, thing like a conspiracy theorist corkboard.
You know, you have the tack in a label and you put a little string around it and you tie that string to this other thing over here.
And that's your value.
Your value has a tie.
It has.
It is one thing.
It's a Boolean or it is an integer or it is a string or it is a list.
We've all seen the error.
You cannot combine integer and string with plus operation.
Like they know what they are, right?
Exactly.
Those values know what they are.
And that's something that throws people off coming from JavaScript where everything's weakly typed.
It'll just try to coerce them together.
Python says, hey, these two values are different.
I'm not going to try to figure this out.
That's because it's working with the values.
The values have type, but the values don't have scope.
And this is where people get thrown off because if you say, you know, let's say I'm going to use the example here.
X equals spam.
If you were to say Z equals X, you're binding Z to the same value as X.
And then if you decide to change Z, that works.
X is unchanged.
Z is changed.
You might think, oh, cool.
Okay.
This is just, it just made a copy.
Not exactly.
Because we have this whole thing about mutable and immutable types.
Some things can't be changed.
Boolean, an integer, a string, a tuple.
They're not changed.
The value is never modified in memory.
A new thing is made instead.
But then some, some, and by some, I mean most.
Sorry about my clock.
Most types in Python, like lists and dictionaries and the objects that you're creating with your fancy little classes are mutable.
So if you create a list or a dictionary and you bind that to two different names, you modify, you add a value to that list.
It doesn't matter which of the names you use, you're modifying the value in memory, in place.
And now that is accessible from either of those names.
Yeah.
And that can be confusing, especially when you start to understand that functions don't pass by value.
They don't pass by reference.
They just bind the value to the name of that argument.
Pass by assignment is what we usually call it.
So you're just having one more handle, one more alias pointing to that value in memory.
So if you change on that argument, you're just changing your spot number.
Many languages have values that are like local and copied types of values.
So you mentioned VB.net, which like the integer and float numbers and things like that.
If you assign one, it's a copy of a thing.
Whereas Python, literally everything is a pointer, right?
This corkboard conspiracy theory thing, right?
There's no concept of, oh, I created this on the stack.
And when the function returns, it's gone.
It's everything is a, you know, even like a, just an integer is a pi long star down in the runtime.
Right.
That's a bit of a mental thing people got to get used to just like how much, because when
you come from a language like C++ or something, pointers and these reference type type of ideas
are like right in your face, right?
Like you see this, like I just said, pi long star, like the star means it points at things
without the star.
It's on the stack, right?
It's a, you are way down in those details.
Whereas in Python, you never see pointers.
Like you could imagine, like there's no such, like we don't even have pointers in Python.
Right.
But the irony is like literally everything is basically managed in that on the heap reference
type style of work.
So that's certainly something I wish I knew.
Yeah.
And, and of course, then people come from C++ and say like, oh, cool, that's easy.
So it's all pointers.
Well, once again, Python being Python, not quite because it's actually technically called a
reference, not a pointer.
And that's important because a Python reference or a Python value has two things.
It has, yes, it does have a pointer, at least we're talking about CPython, but it also has
a reference counter, which is a self and integer.
And that's adding overhead.
So if you ever do actually go down and look at the sizes of things, you will notice that
your base value has this additional overhead.
Yeah.
two numbers.
One of them is your pointer and the other one is your, the other one is your, your reference
counter because it has a reference counter garbage collection.
Yeah.
Often a lot more.
I think trying to, I guess I could check, but either an integer or a single character.
I can't remember which is 28 bytes.
Right.
And that's in C, the single character would be one or two bytes and the integer would be
two, four or eight.
Right.
And so there's just a big difference in the way you got a sort of like the implications
of that.
So related to that, kind of moving on to the next, before we move on, the other thing that
you mentioned, and I think it's also a really big deal in this area is the scope of a variable.
And so in some languages, you have very fine grained control of the lifetime of a variable
by where you declare it.
So for example, if you declare a variable in a C++ or VB.net or whatever in a if block,
when you leave the if block, the thing goes away.
Like if there's curly braces around it in a lot of languages, like that is the lifetime of
the variable innermost bracket of curly braces, like outside of a loop, the thing goes away
in Python.
We don't have that.
And that's a, you know, kind of the variable persists and lives after afterwards.
Right.
Which is interesting.
Yeah.
difference.
Yeah.
Because the garbage collection is something you're never supposed to have to deal with.
So even if you call delete on something, it's not necessarily guaranteed to be cleaned up.
And when you expect it, like the language is going to handle things when it, when it sees
fit for whatever reason.
So yeah, there's only to my knowledge, there's only three, there's only actually three scopes
in Python.
There's global level.
There's module level and there's, I don't even, that gets a little pedantic and then you
have function level classes are not their own skull.
Classes actually do not, a class exists in the global level.
And whenever you have an attribute sitting on a class or sitting on an instance is actually
in a dictionary, unless using slots.
I'm not going into that.
Actually sitting in a dictionary that is a part of that object in memory.
Yep.
It's special, but it works out.
It's just something you got to kind of get used to.
Definitely.
All right.
Next one.
Ducks.
Quank.
Duck typing.
Yeah.
And, and it's, this is where I went.
Oh, haywire.
When I, when I was doing that, that Hungarian notation, I mentioned it.
I'm thinking, I'm being so clever.
I'm just putting the, the name of the, or the type in the name.
And then I know it.
But unfortunately you're coming from a stronger type language.
You're actually, it's easy to underestimate just how much strong typing can actually hold
us back in some cases, because we started thinking about what a thing is rather than
what it can do.
And we see this in Java.
Like you have to have these complex inheritance patterns because you want to be able to say,
well, this thing is, it is this matches this interface.
You know, it, it does this particular thing.
Python's take is I really don't care what it is.
As long as I can interact with it, according to a particular kind of set of assumptions,
then I don't care what it's really doing under God.
And this can be really beneficial because it allows you to create new things that act like
integers, even though they're not integers or new things that act like Booleans that aren't
Booleans, new things that act like strings that aren't strings.
Python doesn't care.
It's just interfacing with those objects on the basis of how it can use them and what certain
things are going to return.
That's all it cares about.
So the concept of the duck typing being, it doesn't care whether it's a duck or a picture
of a duck or a statue of a duck or a moose in a duck suit.
As long as it can interact with it in the way it needs, it's good.
Yeah.
And that's very free.
And talks like a duck and quacks like a duck.
And it's probably a duck.
Yeah, exactly.
And it's very freeing once you get that because you don't have to muck about with all this weird
inheritance anymore.
It's like, well, it, it does what I need.
And that's the important part.
There is certainly less inheritance and less complex hierarchies in Python.
Not, not that there's not an important place for object-oriented programming and classes.
They're just often simpler.
A lot more straightforward, definitely.
And it frees us up to then the saying I coined a couple of years ago during a conference talk was,
was that you need to define objects by their constituent data.
So when you're writing a class or you're designing an object, you're thinking about what is it storing?
Like what, what sort of state, if you think in a functional way, or what sort of data am I really putting in here?
And you can design based off of that.
And then you can think about, well, how do I want it?
How does the system need to interact with that data?
But you don't really have to sit there and think about encapsulating behavior because that's not really the point.
The point is you can think about, like with the example I use is, we always see the example of,
I'm going to create a class called cat and a class called dog and they inherit from animal.
And that's actually a really lousy example we need to stop using because that's starting from the concept of behavior.
Well, it barks, it meows, it eats, whatever.
And in real systems, what we think about is I have a, you know, someone designing a game.
I have a monster.
Okay.
What is a monster?
Well, it's, it's a bad guy.
No, no, no, no, not what it does.
What is it?
Well, it's, that's a collection of statistics about the character, you know, like the, how, how much strength it has and how much health it has.
Okay.
That is not a monster.
That's an, that's an entity.
That's a, you know, that you don't have to put what it's doing into the, into that code.
It is this collection of information.
And when you start, when you shift your thinking to a data first mindset about object oriented, then you find that you can actually use it with functional paradigms that are sometimes a lot easier to reason about.
Yeah.
The, the grouping together of data is an important part.
I would say the most extreme example of duck typing in Python, maybe a couple of areas.
And they all seem to involve, revolve around the, the magic methods or the Python data types, like the Dunder methods.
Right.
I love those.
I could.
Yeah, I know.
So I could have a function, which I could call, or I could have a class that implements Dunder call is a callable, but it's actually an instance of an object.
Right.
And, but as far as Python is concerned, it doesn't matter.
It can call it and pass parameters to the thing it called.
Right.
And there's no real type that disambiguates those two things.
Like one is a function type.
One is a, you know, a class with a particular method on it.
Right.
But they work just fine.
And that's a pretty extreme example.
You know, another one would be, I have a list I can iterate, or I have something that implements the iterator pattern through the Dunder methods.
Right.
Like the Dunder iter and so on.
Right.
Well, yeah.
One of the classes I, one of the examples of the book I gave was like a, a cafe queue.
So a queue of customers in a line.
And yeah, I wrote it as inevitable, but there was all this additional stuff in there.
It wasn't just a basic list.
There was more to it than that.
And, but because I implemented as an iterable, I didn't care.
I could use it like I could use a list.
Yeah.
But it was able to do this additional work with data behind the scenes that made it just easier to use the, to reason about this line of customers.
And then you get generator methods with the yield keyword and yield from, and like all sorts of stuff.
You, you get pie test fixtures that allow you to set up and tear down without actually writing.
Try finally blocks and all these things.
A lot, a lot of interesting places.
While I was thinking about this conversation and this topic in particular, I ran across an article called static duck typing in Python with protocols.
How's that to like crash two ideas together?
Yeah.
It's well, when we look at data types, it's like the way Python even does its data types is yeah.
You can say that you want to say, okay, this is a list and you can type it as a list, but you could also just actually type it as an iterable.
And basically tell Python what you care about.
Do you really care that it's a list?
Probably not.
Yeah.
Do you care that it's an iterable that you can, that you can work with similar to a list?
They're like, how are you going to use it?
That's what you ask yourself.
Then you base your annotations around that.
And so you can say, well, I care measurement like I want it to, I want it to be usable as a measurement.
I don't really care what it is.
It doesn't need to be this particular type of class.
And so that's moving from like we're talking about moving from Java thinking into Python thinking is, well, it's not what it is.
It's what it does.
Yeah.
A lot of the types people put down are like, I need to treat it like some duck typing situation.
So it needs to have these attributes about it.
Let's try to put that aspect of it into the formal static typing rather than earlier.
Yeah.
Okay.
Maybe a quick audience question or a bit of feedback here before we move on to the next one.
It's relevant.
Nick says, early in my Python journey, I wish I had gotten better understanding of all the intricacies, differences, and use cases for various data structures like set versus list.
Right.
Yeah.
Versus dictionary.
And those are certainly, even if you've done programming before, maybe that environment language didn't deeply embrace these data structures.
So certainly those are table stakes for being good at Python.
Absolutely.
Use cases hits the nail on the head because it's like, what's the difference between them?
Well, you know, it's a collection.
And so maybe that's all you care about.
But I find myself using all of them in different cases because it's like, all right, I never need to add or remove things from it.
I'd probably a tuple.
Or I need to, or I need to be able to hash it, use it as an immutable.
That's a tuple.
Or I need to be able to see what's in group A and not group B.
That's set.
Or I care about the order.
That's a list.
Or I want to be able to associate two things.
That's a dictionary.
So knowing your different data structures is helpful.
And, you know, it's tricky because Python's data structures aren't really all that unique, but they have different names than maybe are used to whatever languages.
You know, you come across lists and what is that?
Well, it's a C++ vector or a JavaScript array.
You know, it's like it's, it's, which is where it's helpful to come back to Python kind of with a fresh mind.
It's like, what is this?
Not assuming that things have the same names, but being prepared to get a different vocabulary.
This portion of Talk Python to Me is brought to you by us over at Talk Python Training with our courses.
And I want to tell you about a brand new one that I'm super excited about.
Python web apps that fly with CDNs.
If you have a Python web app, you want it to go super fast.
Static resources turn out to be a huge portion of that equation.
Leveraging a CDN could save you up to 75% of your server load and make your app way faster for users.
And this course is a step-by-step guide on how to do it.
And using a CDN to make your Python apps faster is way easier than you think.
So if you've got a Python web app and you would like to have it scaled out globally,
if you'd like to have your users have a much better experience and maybe even save some money on server hosting and bandwidth,
check out this course over at talkpython.fm/courses.
It'll be right up there at the top.
And of course, the link will be in your show notes.
Thank you to everyone who's taken one of our courses.
It really helps support the podcast.
Now back to the show.
Iteration, iterables, iterators, living.
Yes.
I was working in some code last year and I came across something that just, I was, I cringed and then I laughed out loud because this was written.
So this code was written by who I best guess university data scientists.
So, you know, to their favor, this is not a software engineer.
What they had written and they could tell it had been refactored a few times, but I am not kidding.
They had said, all right, so I have this thing that it's iterable.
It was already something that we could use in a loop.
And we'll come back to what that is for anyone who's curious.
But we had this iterable.
I'm going to convert that into a list.
By the way, we're talking about, in this case, tens of thousands of items in that list.
I'm going to convert that into a list.
Then on the next line, they say for index, comma, value in enumerate.
Enumerate's a thing that lets you get the indexes as well.
Enumerate that list.
And then it says, you know, and then it accessed the value again inside the loop by going to the original list and accessing using the index.
And I just sat there and I kind of half laugh, half cry.
It's just like there is so much.
This is so convoluted.
And all I had to do was take all that logic out and just say for item in original iterable.
And then item was the thing.
But what had happened was that they didn't understand what was really happening in a for loop.
They were still thinking C or C++.
And the thing is that Python has these two magic methods called iter and next.
Itter just says, give me the thing I can use that has the next operator that knows where it is in the list.
I'm going to leave it at that.
And the next just says, give me the next item.
Yeah.
That's all it is.
Just give me the next item.
All a for loop ever does is it grabs that, is it calls that next.
It's like, give me the next thing.
All right, good.
I'm done.
Give me the next thing.
You can write entire classes that behave like this, even generate the value at that moment.
It's called lazy iteration.
You can actually generate the next value when it's wanted, not a moment sooner, which means you only ever have one value in memory sometimes.
Yeah.
And then you can just put that in the for loop.
You come up with any arbitrary name for when you access it for each thing in iterable, name a thing, whatever you want, and then use it directly in your code.
And I wish someone had told me that a lot sooner, discovered that about three years into my journey.
I'm like, oh my gosh, I've been making lists of everything and I've been doing it.
It's like, what a waste.
Yeah.
And, you know, the yield keyword, obviously, is a super easy way to do that lazy iteration that you talk about.
But it also is really important for things like databases, right?
Yeah.
If you talk SQLAlchemy, right?
I'm going to do a query.
I could call the all method and turn it into an in-memory list, or I could, if I don't need that, I could just loop over it, right?
And kind of read it cursor style, potentially stop talking to the database halfway through.
Exactly.
If I want to need it, I don't need the rest of the data.
Exactly.
Yeah, exactly.
Yeah, interesting.
Cool.
Yeah, I think iteration is quite, it's settled because there's only two types of loops in Python.
And usually we're using the for loops.
But the variations, there's just so many of them, right?
You can have your for loop that just goes over a thing.
But what if you need the index like you described?
Not for this improper use.
But sometimes you actually still do need the index.
So you could do enumerate of that thing and get a tuple.
But you could take it to another level, do tuple unpacking and say like index comma value in enumerate of things.
Right.
And you get it back.
And you can unpack a dictionary too.
You can unpack a tuple with that in the dictionary.
There's this whole library and it built into Python called iter tools.
And if you ever have, you know, 10 minutes over coffee, just pull up the documentation for iter tools and just kind of scroll through.
There's some neat stuff.
Like you can, you can like find every possible combination of three different lists.
I just give it three different lists.
And this is one that will take all three of them.
Just give you every single possible combination of it.
I actually use it in the book.
And it's just, there's all these neat things you can do with iterables.
Some of them don't even have to stop.
There are infinite iterables where you can just say, I want to get this sequence repeating infinitely.
One, two, three.
And two lines of code.
And you can have that repeating forever.
And if you're using an old fashioned while loop or an old fashioned C++ style for loop, that would be multiple lines of code.
Like, you know, you have to keep track of the state and all that stuff.
Yeah.
I think it's two lines.
Yeah.
It's amazing.
Yeah.
So there's, there's a lot of depth, even though it seems so simple when you first look at it there.
Closely related to that are comprehensions.
Yeah.
Like a list comprehension, right?
People often see a list comprehension or a set comprehension and go, especially a list comprehension, they're like, oh, that's the same thing as a for loop.
Where I create, before the for loop, I create a list.
And then in the for loop, I add the thing to the list.
And these are the same.
May have the same outcome, but they're not actually the same thing.
Like the runtime performance is not the same.
The bytecode implementation of those two things is not the same.
This is like a nonstop debate I have on YouTube whenever I put a video and mention something about list comprehension.
They're like, that's just a for loop.
It's not actually just a for loop.
It's like a kind of accomplishing the same thing.
But we now have for potentially 312, Carl Meyer put out a PEP 709 called inlined list comprehensions, which are supposed to take some of the mostly invisible behind the scene implementation and make comprehensions two times faster.
I believe they're already faster than this looping story I talked about, but this would make them faster still, which is pretty cool.
Wow.
How does this how does this syntax work?
I'm curious.
It's just like it's just an internal change.
Okay.
So the syntax is unchanged.
Yeah.
So the way here's the magic of list comprehensions.
That's funky that people maybe aren't aware of is it.
It used to actually create a list comprehension object that implemented the loop in a function.
And then it called that function as if you'd like nested a function to create the isolation of the list comprehension.
So that created a stack frame.
I created an object.
Then it did function in direction and all those things made it slower than it needs to be.
So now they create a new load fast and clear byte code operation that like stores the state of the function, then does the list comprehension and then pops it back off the stack to unwind it.
So anyway, comprehensions are interesting.
I just wanted to this is like kind of a brand new thing.
That's cool.
That's coming along.
That might be interesting for people.
Well, I think it's worth mentioning then that what a list comprehension really is.
So like if I unpack it because people think, oh, people use that term very loosely.
A lot of times they want the list comprehension.
But what is a list comprehension?
And if I unpack that, what it really is, is it is a generator expression that is being unpacked into a list.
So what's a generator expression?
Generator expression is a type of generator.
What's a generator?
A generator is a function or type of function that is itself iterable.
So that's where this is.
That's where this is critical is that a generator you can you can write a generator that what when call it that it has.
You mentioned that earlier, this yield keyword.
And so you can do whatever you want in this function.
You can write it however much logic you want.
Every time you hit the yield statement, it will return that value.
And then it's going to wait for the next call.
Then the next iteration of that, like the next call to next on that generator.
And then it's going to run through again until it hits a yield.
And then it's going to stop, turn that value and wait again.
And you can do some incredible things with these.
But once you understand the generator, which are incredibly powerful tools,
then you can write a generator expression, which is I discovered later.
A generator expression is to a generator as a lambda is to a function.
It's an anonymous generator.
That's all it is.
A lambda is just a very small inline anonymous function.
Has the logic.
It can take in some values, return some values.
It's a lot simpler, a lot shorter.
And you do it right in place.
So you're not having to go off somewhere and write a different function and come back.
That's why we have anonymous functions is so that we can just do it like,
like you see there at the bottom, reduce and just some some rules right there instead of writing a separate function.
Generate expression, same sort of thing.
We're doing writing some generator logic.
It's shorter.
It's creating a generator, but it's just a lot smaller.
And if you just wrap it in in parentheses, you can use it as a generator.
But when you change those parentheses to be square brackets, what happens is you're running that generator and then you're loading all the values into the list.
You do it in in curly braces.
You're creating a set.
Or if you're if you're doing the same thing, but you are the return value in each step is a key value pair.
Then you're going to create a dictionary.
But all you're really doing is unpacking that generator into a data structure.
So sometimes you want to think when you're mucking about with these, do you really want the data structure when you're done or do you just want to go over the values?
Maybe you just write a generator expression.
Yeah.
The difference is super subtle.
Like, do you have square brackets or do you have parentheses around the expression?
But the effect or consequence of that is very significant at runtime, right?
It's kind of like that example you talked about putting everything in a list and looping over it or just looping over it.
Right.
Exactly.
And because these are these are these one liners and we as coders tend to get a text of these one liners.
It's like, oh, look at all the cool things I could do.
I have a couple of examples in the book.
Some real list comprehensions I've come across or some colleagues of mine have come across where it's like it's multiple lines.
And that's a bad list comprehension.
That's a bad generator expression.
Just like you don't want to have a multi-line lambda at that point.
Write a function.
Generator expressions and lambdas are supposed to be these really small, short, in place bits of logic.
And if it's getting complicated, write the full size thing.
It's a lot easier to debug.
If nothing else is usually to debug, but it's also a lot easier to read.
It is.
Just why we're kind of ranching on these things.
Like, I wish there was a way to specify a sort in these list comprehensions, generate expressions, and maybe not the generators because that gets tricky.
But so often I find myself I'm doing a list comprehension and then I got to turn around and sort it.
Like if it could just just add that in there, that would make me happy.
All right.
Moving on.
Packaging.
Yes.
Deciding on packaging and all that right up front.
Actually, before we get to that, I guess the other one that you threw in here is virtual environments.
Indeed.
Yeah.
Virtual environments.
Use them.
They're the only.
I avoided them for years because I thought, oh, these are scary.
I don't understand them.
They're not scary.
I'm actually going to.
I'm actually going to hijack something.
I learned from one of my guests on my own podcast recently is what containers actually are.
By the way, Docker containers.
This is where this is relevant.
You know what a Docker container actually is?
Is it's the folder on your operating system?
And it's just changing the route.
So when you're working inside of it, it's changing the route.
There's a bunch of magic logic around that.
But that's all it is.
It's like it's pretending that that folder is the route and it's running things accordingly.
That's all a virtual environment really is doing either.
So there's not a lot of difference between.
It lies to Python about the path.
Yeah.
There's not a lot of difference between the virtual environment and the Docker image.
Like they're the same fundamental concept.
So all you're doing is you're creating this little spot on your, this little folder.
And you're saying, these are my Python packages.
And unless you tell it otherwise, don't go looking anywhere else.
These are the ones I want to use.
And they're super easy to create.
It's like one line to create a virtual environment.
And it lets you maintain exactly the packages you want for that particular situation.
And it's great.
It is.
Yeah.
It's once you get used to it, it's great.
I remember it being frustrating at the beginning.
You're like, ah, I don't remember exactly.
How do I create the environment?
How do I activate it?
How do I know?
Do I really need this?
And yeah, I don't, I don't start any projects without having them as just straight up using
them right away.
It's like, absolutely.
Yeah.
I probably have created them for projects that don't even have external dependencies because I was just
like on autopilot.
Like, oh, actually I didn't need that, but whatever.
It's still good to have.
Yeah.
I would absolutely control which Python version you're using.
And then you can install whatever packages you want.
And you don't not worry about messing up your system or user scope packages.
And I had a coworker recently say, I dream of the day when I can actually remember how to
activate this, but it's actually really easy.
So once you, so assuming you know the name of the folder, so you can pick the name of the
folder you use for your virtual environment.
Traditionally, it's called like them, V-E-N-B.
And it's, here's the thing to remember.
You don't actually have to activate your virtual environment.
Put that out of your mind for a moment.
You can run things directly in the virtual environment.
So in Unix systems like Linux and Mac, there's a bin folder.
Vim slash bin slash, and then whatever Python, whatever Python executable you want to run.
It could be Python.
It could be pip.
It could be your own package.
You can actually just navigate into that bin folder.
And so I can say Vim, bin, pip, and I've got the pip.
And that knows to only work within its virtual environment.
It's not going to break out of that container.
Once you understand that, here's where they activate script is.
Vim, bin, activate.
It lives in the bin file.
And if you're on Windows, the only thing you're changing is you're going from bin to capital S scripts.
And then the things have an ending of like either .bat or .ps1, depending on what show you're using.
But that's the structure of a virtual environment.
And once you understand that, they're a little overscuried.
Yeah, my RC file for my user profile on my Mac is just full of here's the Python executable in some virtual environment.
And here's the script and commands to send to it for like little aliases and stuff.
And like none of those activate the virtual environments.
They just run the Python in the virtual environment to make all the magic happen.
Another thing I would recommend is people think about creating aliases that make virtual environments happier and easier to use.
So, for example, and you could do this in PowerShell on Windows.
You can obviously do it in your RC files on Mac and Linux.
So, for example, I have vnv, which is for venv, but I don't want it to accidentally change the directory.
And if you haven't, like whatever.
Like I just have a short version, so I know it's something slightly different.
But it'll go run the virtual environment.
And it'll always remember to add --prompt equals dot, which will make the virtual environment not just be called venv,
but it'll be called the name of the project that it's in.
So, for example, if it's in like talk Python, like the virtual environment, when I activate it, we'll say it's the virtual environment talk Python.
Right.
And then it activates it.
And then I think I think this is almost a bug in Python that most of the time I would say like 49 weeks out of the year.
When I create a virtual environment, the first thing I hear is warning your pip is out of date.
You know, like, OK, well, I'm starting a new project.
How about we start with a new one?
Let's just go with that right now.
Right.
And so my just that is I always want the new one.
I don't ever want the old one because this is brand new.
So I want the new one.
Why don't we fix that?
So my alias then says pip install dash you pip setup tools, wheels, pip-tools, et cetera.
And just ditches that output.
So like, yeah, right.
Immediately, like I have when I create a virtual environment, it's immediately active and it's immediately named the project and it's immediately up to date.
So I don't see those silly warnings, at least for a while.
And all I got to remember is type V and V.
Boom.
And that's it.
Right.
So if you get into this, if you do a few little steps.
I do this too.
Yeah.
I imagine you did.
And so it's I just strongly encourage people to somehow either do this in PowerShell or do this in their terminal setup because like that big, long thing to type, that's a hassle.
Three letters is not a hassle.
And they just makes it.
There's something about lowering the friction that makes it really nice.
And there's tools you can also install that do it.
But like I recommend just writing your own aliases too, because everyone's got a sub of a different workflow instead of perfect anyway.
That's why Vim has so many options.
So writing this yourself, it's super easy and it teaches you a bit about how it's working so that you're not surprised at the results.
Yeah.
Indeed.
Okay.
Now packaging.
Yes.
And I'm going to keep this one super short because it's a deep, deep, deep, deep, deep, deep, deep rabbit hole.
Figure out your packaging day one.
Like you get, I will figure out packaging before I even figure out what I'm, you know, how I'm building stuff.
I am just going to set up a basic hello world and then I'm going to get the packaging working.
Because I actually, and I learned this the hard way.
I built a project once.
It took me one month.
I was very happy with it.
It was gorgeous.
It was a game.
You can actually find it online now.
I was very happy with myself.
And then I spent the next two years trying to package the dark bag.
Turned out it was impossible to package because of certain assumptions that one of the libraries I was using made.
Had I actually tried to package it from the beginning, I could have saved myself a heck of a lot of time and just use a different library.
Yeah.
So you figure it out from day one.
And then the benefit you get to this is that then you are constantly, especially if you're doing out of place testing, which I strongly recommend.
Run your tests.
Your tests should not be in your source folder.
Put them separate.
That's going to force you to actually install your package into a virtual environment and then run the test against that package.
It is not as scary as it sounds.
I have a whole, by the way, if you go to my website, code mouse92.com, I've got a whole talk on this called Escape in the Gargo Cult.
And I explain how these patterns work.
But when you do it that way, that means from day one and ever onward, you are testing your packaging.
You're testing your assumptions about the working directories and all that.
And you are testing it in the same way that it's going to be used on someone else's computer.
So you never again have to have the thing of, well, it works on my machine because you are, all of your tests are depending on actually installing your package.
So if something breaks, you bring in a dependency that just doesn't work with your packaging scheme, you know, right away.
And you can make a decision about it.
Yeah, that's good advice.
There's a lot of depth to the packaging thing.
I had a whole panel on it.
We're in Talk Python not long ago.
Definitely a lot there.
Oh, yeah.
Deep rabbit hole.
But you know what?
Don't copy it.
Don't don't copy pasta.
Don't copy paste.
Find out what the things you're using are doing because.
All right.
Last one.
Yeah.
Currency.
Yes.
Which for years I heard concurrency, asynchrony, threading, parallelism, multiprocessing.
I heard those all used as as synonyms for one another.
They're not completely different things.
So and actually I have to thank the Go language for that for for teaching me this.
Thank you, Rob Pike, for his talk.
Concurrency and parallelism are not the same thing.
But here's the thing to understand.
Concurrency is doing.
You're switching between tasks.
You're checking your phone and you're talking to your friend.
Are you doing both at once?
No, you're not reading stuff on Twitter and listening to what your friend says.
You're actually switching between the two contexts rapidly.
You're not really multitasking.
You're just changing your focus fast enough that no one ever notices the difference.
That's concurrency.
So you have one CPU or potentially potentially multiple or involved in this where people get
confused.
Python's nice because you're locked to one CPU for concurrency.
So it's I'm gonna work on this.
Then I'll work on this.
It doesn't make your code faster.
It'll never make your code faster.
Just like multitasking will not make you get your work day faster.
What it's going to do is it's going to slow you down.
But it doesn't look like it.
In some cases, it'll make your code look faster because while you're waiting on the user to
type in their answer, this example I use in the book, while you're waiting on the user
to type in their answer to a question, you're doing some math in the background.
You're IO blocked.
You're waiting on something over which you have no control.
You can do some other work while you wait.
That's concurrency.
And you can do that with a synchrony where Python's handling that multitasking, or you can do
it with threading where the operating system is handling that multitasking.
That's different than parallelism, which is where you're actually using different CPU cores and you're
doing two things at the same time because you have two different CPUs, two different cores
working in parallel.
And they're not depending on one another.
They're not waiting.
So you're doing multiple tasks at once.
Both of them have overhead.
Near one is a silver bullet for performance.
You're really going to have to think a lot about it.
But I actually like the GIL for this reason.
Global interpreter lock, the much aligned thing that stops us from getting free multiprocess,
free parallelism, free multiprocessing with every thread.
Because this forces us to actually stop and think, which thing do I need?
Do I need concurrency or do I need parallelism?
Because you can get both in Python, but the GIL makes sure that you have to make a decision.
Because if you want multiprocessing, you're going to have to create different processes,
spin them off, manage them separately.
There's more overhead, but you're going to have to address all of those things of how do
we share resources, which is a really snarly topic.
And Python doesn't let you pretend that that's not going to be an issue.
It makes you think about that.
It does make you think about it.
While you can't get more done, you can't make an individual thing go faster by using concurrency
on it.
You can start a bunch of things that run outside of your control and outside of your program
and then get them back, right?
Like I could issue a web request, a three API endpoints, all of them, and then start gathering
up the answers as the first one comes back, right?
Like you have to think about, I think the main thing to think about here is where am I waiting
on something else when you're looking for the async and await type of benefits?
Like am I waiting on a thing?
Could I be doing something while I'm waiting?
Async and await amount makes it super easy to do stuff while you're waiting.
But your program itself is still constrained by the GIL.
And often with async and await, there's not even a second thread involved, right?
It's just slicing up, time slicing up the current thread.
Yeah.
Which I mean, technically is all that's going on with the threads anyway.
Like when you have multiple threads, the operating system is now just doing the slicing up.
You're still constrained to one process.
Then you need a separate process if you want to do multiple things.
Yeah.
But like my machine has 10 cores.
It will go across the cores according to the OS.
But the effect in Python because of the GIL means it doesn't actually make any difference.
It's still the same, right?
Right.
But if you get multi-processing them for sure.
So yeah, super, super interesting.
I think one of the things people tell, I think people say two things.
One, asynchronous programming is so hard you should never try it.
I hear that all the time.
It's so complicated.
It's so hard.
You're going to end up with race conditions.
Just don't try it.
Mere mortals don't dare.
They only regret.
Maybe if you're doing like signaled events in C++ across, you know, hardware notification.
Like there are scenarios that are super, super tricky.
Using async and await generally don't fall into that.
Like you write regular code without changing the structure.
You don't have to have callbacks and signals and locks.
You just write regular code where you have the async stuff you put away.
It's kind of the end of the story.
And it's not necessarily as simple as straight up regular programming, but it's, you know,
it's like 10% more.
It's not 10x more.
Yeah.
I think a lot of people for some, I don't understand why, but a lot of people, I think
they want to feel like I've conquered this dragon.
And if I'm going to tell other people how to do it or what to do, the first thing I'm
going to tell them is how hard it is and how daunting it is.
And like, yet I've conquered it and now I will help you.
Right.
That doesn't do people a favor.
Right.
They want to feel good about themselves.
Yeah.
I guess so.
But I feel like threading and parallelism lands deeply in that space where people are like,
oh, it's super hard.
And it's not, it really isn't.
But where it's hard is you, you have to be prepared to really think about why do I need
it?
And if I can briefly say, I have two entire chapters in this book on that topic.
Yeah.
Cool.
And I pick one example and it has nothing to do with waiting for a webpage to download
because that is both overused and something that most of us don't need.
And I apply synchrony.
I apply threading.
I apply parallelism.
And you actually see all those little concerns of how do you handle deadlocking, live locking?
How do you handle starvation?
How do you handle the producer consumer problem?
How do you handle the fact that, huh, I just added 15 multi, multi processing workers.
And now this thing is like way slower.
Why?
I go into all of that.
And I think that's one thing I wanted to say is when I was writing this book, that was the
thing that was my chief delight was coming up with examples for all of the topics from the,
from the simplest, all the way to the most complicated, where I did not hold back on the
relevant complexity.
My list comprehensions were the starliest list comprehensions you're ever going to find in
this tutorial.
You know, my, you know, I have, I go into recursion.
I go into, yes, I go into meta classes, whichever one's afraid of.
I go into all of these things.
I dig into them, but the complexity is always relevant.
So you're only going to get the complexity from the topic and you're going to understand
where all these bits and pieces interplay.
But the, in the, the complexity coming in the old, from other irrelevant topics, I try to
keep to a minimum.
So you can really focus on understanding, not just the happy path of coding, not just the
happy path of like, okay, well you can async this.
Look how easy that was.
Well, it's not easy, but it's also not painfully hard.
You just have to be prepared to think through those different factors.
And so I focused a lot of time on really helping you learn how to think through those factors
as it relates to your code.
Excellent.
It sounds like a bit of the Einstein philosophy.
Simple as possible, but not simpler.
Right.
Exactly.
Yeah.
Cool.
I, you know, last thoughts on the async stuff.
Like I think a sprinkling of async and await makes massive difference for a lot of scenarios.
And that's pretty straightforward.
If you're going to try to write trio or some full on framework that deeply coordinates and
understands it.
Like, yeah, that's super, super hard.
Again, some of the signaling stories I talked about with like C code, that's super hard, but
this stuff people, it's pretty approachable.
So don't, don't forget to check it out.
All right.
Well, I think that's it for our things.
We wish we knew if we had more time, we could wish upon more things, but that's pretty much
definitely covers it.
Doesn't it?
Yeah, I think so.
Nice.
All right.
Well, before you get out of here, let me ask you the final two questions.
If you're going to write some code, what editor to use?
Yeah.
You know, for the longest time I was a VS Code guy.
I still love it, but I broke down and I bought PyCharm Professional and I love that because
I do a lot of refactoring and PyCharm saves my butt so much when it comes to refactoring
that I fell in love with it.
And I can't imagine working without it now.
I hear you.
So I'm spoiled.
It does surprise me how people, not just a PyCharm comment, but in general, there's
a lot of people who are like, oh, I don't really want to get like a new computer or I don't
really want to pay 20 bucks for this thing.
That would save you thousands of dollars worth of time as you go for it.
You know, like, so I'm glad.
Large on your tools.
Yeah.
As far as your budget will allow you.
And I've been on both ends of that spectrum.
And as your budget allows you, large on your tools because they're what you're going to
be spending most of the day using anyway.
And the last thing you want is something you're going to be getting splinters on.
So indeed.
And then notable PyPI package, something you're like, oh, this thing's awesome.
I just ran across X, Y, Z.
I guess I have two.
One would be, I think my favorite, and this has certainly gotten plenty of press, but I
love it.
It's the hypothesis testing library.
I love hypothesis.
Hypothesis is fantastic.
It will revolutionize the way you write tests.
It's not as scary as the documentation makes it look.
If you click on what you can generate and how, start there.
I go through the quick start guide and then go right to this.
And it's so cool because you just write these strategies.
I'm like, okay, this is what my data is kind of sort of supposed to look like.
And then your test is going to try and break your code.
I love hypothesis.
It's going to throw all sorts of garbage at you until it finds a way to break your code.
And then it's going to go, hey, here's the simplest thing I could find that blew up
your code.
Fix it.
Lovely.
Nice.
So much fun.
The other favorite package I have is one of my own actually branch detective.
I built this at two jobs ago.
And basically we had the problem where we had these diverging branches where we had no
choice, choice, but to completely diverge them.
And we were cherry picking back and forth.
And there was always this, which commits are in this one that aren't in this one.
When you're doing cherry picking, your hashes are complete garbage.
So I built this tool that allows you to see what's in one branch.
It's not in another branch, any branch arbitrarily.
You can specify day range.
It knows how to handle cherry picks.
It knows how to handle merge revisions or filter them out.
And you can even pass to the dash M flag and just get this nice markdown.
If you're using conventional commits, we'll just spit out this markdown.
Then you can then copy and paste into your PR and say, hey, here's, here's all.
Here's the summaries of the 26 commits I'm putting in here.
So I use this every single day and I've improved it for every project I'm on so far.
And I, I just, I love it.
It's my favorite.
One of my favorite things I've ever built.
So you kind of wrote set for Git, right?
Like you can say, what's in the set?
Subtract that set.
Show me those things.
Yeah.
Speaking of data structures.
Exactly.
Like what's here that's not there.
Yes.
Yeah.
Cool.
All right, Jason.
Well, thanks for being here.
Final call to action.
People will get their Python better.
What do you tell them?
Yes.
So dead simple Python is available from wherever you buy books, but especially if you go to
nostarch.com, you can get this comes in ebook or in physical book form.
And Hey, you know what?
I wrote it and I refer to it every single day because this is the book I wish I had had.
And my favorite little feature of this is that I have a thing in the back with every single
command for the debugger.
Also, every single dunder method is also documented in here, which even the Python docs didn't
do that.
This is the entire core language bar nothing explained for the existing coder.
So check this out.
Awesome.
Well, thanks for taking the time for being here and enjoy the chat.
Thank you so much.
It's been a pleasure.
You bet.
See you later.
This has been another episode of Talk Python to Me.
Thank you to our sponsors.
Be sure to check out what they're offering.
It really helps support the show.
Take some stress out of your life.
Get notified immediately about errors and performance issues in your web or mobile applications with
Sentry.
Just visit talkpython.fm/sentry and get started for free.
And be sure to use the promo code talkpython, all one word.
Want to level up your Python?
We have one of the largest catalogs of Python video courses over at Talk Python.
Our content ranges from true beginners to deeply advanced topics like memory and async.
And best of all, there's not a subscription in sight.
Check it out for yourself at training.talkpython.fm.
Be sure to subscribe to the show.
Open your favorite podcast app and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes, the Google Play feed at /play,
and the direct RSS feed at /rss on talkpython.fm.
We're live streaming most of our recordings these days.
If you want to be part of the show and have your comments featured on the air,
be sure to subscribe to our YouTube channel at talkpython.fm/youtube.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
I'll see you next time.