Hello and welcome to Python Bytes, where we deliver Python news and headlines directly to
your earbuds. This is episode 197, recorded August 26th, 2020. Brian, can you believe it's
the end of August? Even if I can't say it, it still is true?
No, I can't. I don't know where August went. I just don't.
I thought this whole pandemic thing would make the summer seem long and slow. It seems like it
just went faster.
Yeah, I've got like a Lego kit that I was planning on doing like the first week of summer vacation,
and it's still sitting here. So yeah, for sure. Yeah, there's a lot of things I want to get done
before the sun goes away and rain starts for six months straight. That's a Pacific Northwest problem,
but it's our problem. All right. Now this episode is brought to you by us as well. We'll tell you
more about the things that we're doing that we think you will appreciate later. Right now,
I want to talk about something that I think we might've covered before, but I don't know if we've
ever satisfactorily covered it. Maybe this time we'll get a little closer and that's AsyncIO.
Oh yeah, I think that's a new topic.
It's a totally new topic. Covered only less than GUIs. No. So there's a new, how should I put it,
a new compatibility-like layer library that allows you to work a little bit better with AsyncIO
and some of the other Async libraries that are not directly immediately the same as or built right on top of AsyncIO.
Curio from David Beasley and Trio from Nathaniel Smith. So there's an article that talks about this. I'm going to mention as part of this conversation.
And then say, Hey, Python has three well-known concurrency libraries built around Async and await syntax. AsyncIO, Curio, and Trio.
True. But where's Unsync, people? Unsync is the best of all four of those. I don't know where Unsync is.
Anyway, Unsync is not part of this conversation, but Unsync plays a role a little bit like this thing I'm going to mention today is AnyIO.
And it's a pretty clever name because the idea is that it provides structured concurrency primitives built on top of AsyncIO.
Okay.
Right? So one of the challenges with AsyncIO is you can kick off a bunch of tasks and then not wait for them.
And your program can exit or you can do other things.
And maybe you've seen runtime warnings like task such and such was never awaited.
You're like, Hmm, I wonder what that means.
Well, that probably means your program exited while it was halfway done or something like that. Right?
Or your thing returned a value before it waited for it to finish. Right?
And at the low level, something that's a little bit frustrating or annoying that you've got to deal with is that you've got to make sure that all the stuff you started on the Async event loop,
that you wait for that event loop to finish before your program completely shuts down or completely carries on.
And so that's basically the idea of this library.
It's a compatibility layer across those three types, those three different well-known concurrency libraries that provides this structured concurrency.
So you look at Wikipedia, they say structured concurrency is a programming paradigm aimed at improving the clarity, quality, and development time of a computer program by using a structured approach to concurrent programming.
The core concept is encapsulations of threads of execution by way of control flow constructs that have a clear entry and exit points.
In Python, this mostly manifests itself through this library as async with blocks or async context managers.
So you're like, I'm going to do some async work.
So let's create a width block, do all the work in there.
And then by the way, when you leave the width block, it's going to have made sure all the tasks that were started and the tasks started by those tasks and so on all finished.
Oh, that's nice.
Yeah, that's pretty cool.
So the way it works is you basically go anyio.createTaskGroup and then from the task group, you can spawn other subtasks and it will keep track of those.
If there's an exception, I believe it will cancel the other undone ones, the unfinished ones and so on.
So it's about saying we're just going to go through this thing and it's all going to run here and like it enters at the top and it exits at the bottom of the width block.
Okay.
That's pretty cool, right?
Yeah.
So I think that that's pretty neat.
Also has other primitives.
So that's like a real simple example.
Other example or other things it does include synchronization, primitives, locks.
So if you create a reentrant lock in Python, often called a critical section and things like C++ and whatnot, it's never, ever going to help you.
Well, maybe that's a little bit strong.
It's likely not going to help you because those mechanisms come from the operating system process level.
And what they do is they make sure two threads don't run at the same time.
Well, with asyncio, it's all a bunch of stuff that's being broken apart on a single thread, right?
It's all on the one, wherever the event loop.run is, run to complete or whatever, like wherever that's happening, that's the thread.
So like the thread locks don't matter.
It's all the same thread.
Like you're not going to block anything.
So having primitives that will kind of function like threads to protect data while stuff is happening, while it's in temporarily invalid states, that's pretty cool for asyncio.
Okay.
So you need it or you don't need it?
You probably need it.
I think people often don't really think too much about these invalid states that programs get into.
And you think, well, asyncio, it's going to be fine.
And a lot of times what you're doing with asyncio is kind of standalone.
Like I'm going to kick off this thing.
And when it comes back, I'm going to take the data and do something.
But if you're modifying shared data structures, you could still end up in some kind of event loop, erase condition.
It's not as bad as like true threading because you're not going to, it's, I don't believe it's like a plus equals, right?
Of something that actually might be multiple steps at the lower level runtime.
I don't think that it would get broken up to that fine grained.
But if you say like debit this account, this amount of money, or await, debit this account, this amount of money, await, put that amount into the other one.
And some other one is like reading in some kind of loop, like that level of higher order, like temporarily invalid state.
That could be a problem for asyncio and you want some kind of lock.
So this comes with that, it comes with streams, which are similar to queues, timeouts through things like move on after or fail after a certain amount of time and so on.
So it's pretty cool little library.
Yeah, that's nice.
Nice.
My vote still for unsync is the best of the four, even though it was unmentioned.
Isn't unsync built on those also?
It's a compatibility layer that takes asyncio, threading, and multiprocessing and turns them all into things that you can await.
Oh, yeah.
Yeah.
So don't you think there should be like a standard, like a, they should get together like some consortium and have a standard about this?
Yeah.
Well, they probably should, but we're still in the early stages of figuring out what the right API is.
That's right.
That's why they haven't done it.
There's something else that has, that could use some standards and that's in a lot of data science libraries.
There's an announcement that there's a new consortium for Python data API standards.
So there is one happening and it's happening actually quite fast.
They're getting started right away and there's activities to the announcements right away.
Then in September, I believe they're going to kick off some work on data frames or on, no, starting with arrays and then move on to data frames.
And so, okay, I'm getting ahead of myself.
There are little blurbs says, one of the unintended consequences of the advances in multiple frameworks for data science, machine learning, deep learning, and numerical computing is that there is fragmentation.
And in using the tools and then there are differences in common function signatures.
They have one example that shows what the, generally a mean function to get the average or mean, people are going to like flame me for calling average mean, but as a commoner, I kind of think of those the same thing.
But anyway, they show eight different, frameworks then, and some of them are common with other ones.
And so there's five different interfaces for over the eight frameworks for just the mean function for an array.
Yeah.
And what's crazy is like, they all are basically the same.
They're so, so similar, but they're not the same, not code wise the same, but they might as well be.
Yeah.
And so one of the issues is there's people are using more than one framework for different parts of their, maybe different parts of their data flow.
And sometimes you can kind of forget which one you're using and having a lot of these things common actually would just make life easier, I think.
So I think, I don't know how far they'll get with this, but I think it's a really, so they're not trying to make all of these, these frameworks look exactly the same, but with, commonalities in arrays and data frames.
Or, and they note that arrays are also called tensors.
So those are, trying to make some of those common is, I think a really good idea for some of the easy, simple stuff.
Uh, why not?
It seems like a great idea.
It seems like a huge challenge though.
Like who's going to give, whose function is going to be the one that's like, yeah, we're dropping this part of our API to make it look like everyone else's.
Right.
And that's why I think that they've, they've went through a lot of thought on how to go about with this process and try to convince people.
So they're working with, they're trying to kind of be in between the framework authors and maintainers and the community and try to do some, some review process for different APIs, put a proposal out, have feedback from both from, from the different projects and from the community to have, have more of a, you know, more input to try to make it.
It isn't just like one set of people saying, Hey, I think this should be this way.
Yeah, no, it's, it's a good idea.
It would be great if a lot of these applications or these frameworks may be renamed.
If it's the same function, if it's like, for instance, mean in this example, if it's spelled exactly the same, maybe it should be the same API.
And if you want a special version of it, maybe have a, have a underscore with an extra, you know, some reason why it's different.
Uh, you can have extra different functions.
Yeah.
It seems like you could find some pretty good common ground here.
It's a good idea.
And if they make it happen, you know, it'd just be easier to mix and match frameworks and use the best or different situations.
Cause I can certainly see you're like, Oh, I'm working with pandas here.
It would be great if I could do this on CUDA cores with QPy, but I don't really know that it's close, but it's not the same.
So I'm just going to keep stroking along here as opposed to change the import statement.
Now it runs there.
Yep.
I don't know if it's ever really going to be like, you can just swap out a different framework, but for some of the common stuff, it'd really be great.
And that's why one of the reasons why we're bringing it up is so that people can get on board and start being part of this review process if they care about it.
Yeah.
It also seems like there might be some room for like adaptive layers, like from QPy import pandas layer or something like that, where it basically, you talk to the, in terms of say a pandas API and it converts it to its internal.
It's like, Oh, these, these arguments are switched in order or this keyword is named differently or whatever.
And there's even things like differences.
And even if the API looks the same or it's very similar, the default might be like in some cases, the default might be none versus false or versus no value or things.
I don't know what no value means, but anyway.
Yep.
Cool.
That's a good one.
Now, also good is the things that we're working on.
Brian, you want to tell folks about our Patreon?
Actually, we've kind of silently announced it a while ago, but we've got 47 patrons now and it's set up for a monthly contribution and we, everything really appreciate people helping out because there are some expenses with the show.
So that's a really cool.
We'd love to see that grow.
I don't, we'd also like to hear from people about how we'd like to come up with some special thank you benefits for patrons.
And so I'd like to have ideas come from the community.
If you can come up with some ideas, we will think about it.
Yeah.
So, and I'm trying to figure out how to get to it.
So on our Python bytes.
If you're on any episode page, it's there on the right.
Okay.
If you go to an episode page.
Got it.
Yep.
Then it says on the right, I believe somewhere it says sponsors on, off the double check.
I believe it does.
Okay.
We'll double check.
It can for sure.
If it doesn't already.
And also, I want to just tell folks about a couple of things going on over at Talk Python
training.
We're doing a webcast on helping people move from using Excel for all their data analysis
to pandas, basically moving from Excel to the Python data science stack, which has all sorts
of cool benefits and really neat things you can do there.
So Chris Moffitt is going to come on and writing a course with us and he's going to do a webcast,
which I announced it like, well, yeah.
It's 15 hours ago and already has like 600 people signed up for it.
So it's free.
People can just come sign up.
It happens late September, September 29th.
I'll put the link at the extra section of the show notes so people can find it there.
And also the Python memory management course is out for early access.
A bunch of people are signing up and enjoying it.
So if you want to get to it soon, get to it early, people can check that out as well.
Very exciting.
So this next one I want to talk about has to do with manners.
What kind of developer are you?
Are you a polite developer?
You're talking to the framework.
Are you always checking in with it to see how it feels, what you're allowed to do?
Are you kind of a rebel?
You're just going to do what you like.
But every now and then you get smacked down by the framework with an exception.
I don't want to describe how a developer I am because I don't want the explicit tag on this episode.
So there's an article that talks about something I think is pretty fun and interesting to consider.
And it talks about the two types of error handling patterns or mechanisms that you might use when you're writing code.
And Python naturally leans towards one.
But there might be times you don't want to use it.
And that is it's the two patterns are it's easier to ask for forgiveness than permission.
That's one.
And the other one is look before you leap or please may I.
All right.
And with the look before you leap, it's a lot of checks, like something you might do in C code.
So you would say, I'm going to create a file.
Oh, does the folder exist?
If the folder doesn't exist, I'm going to need to create the folder.
And then I can put the file there.
Do I have permission to write the file?
Yes.
Okay.
Then I'll go ahead and write the file.
Right.
You're always checking if I can do this, if this is in the right state and so on.
That's the look before you leap style.
The ask for forgiveness style is just try with open this thing.
Oh, that didn't work.
Catch exception.
Right.
Except some IO error or something like that.
So there's reasons you might want to use both.
Python leans or nudges you towards the ask for forgiveness.
Try except version.
The reason is, let's say you're opening a file and it's a JSON file.
You might check first.
Does the file exist?
Yes.
Do I have permission to read it?
Yes.
Okay.
Open the file.
Well, guess what?
What if the file's malformed and you try to feed it over to like JSON load and you give
it the file pointer?
It's not going to say, sorry, it's malformed.
It's going to raise an exception.
It's not going to return it like a value, like malformed, constant, weird thing.
It's just going to throw an exception and say, you know, invalid thing on line seven or
whatever.
Right.
And so what that means is even if you wanted to do the look before you leap, you probably can't
test everything and you're going to end up in a situation where you're still going to
have to have the try except block anyway.
So maybe you should just always do that.
Right.
Maybe you should just go, well, we're going to have to have exception handling anyway.
That's just, we're going to do exception handling as much as possible and not do these tests.
So that's the, this article over here.
It's on the switwoski.com.
Oh yeah.
It's on Sebastian.
Widooski.
So yeah, it's his, I didn't realize that it was his article.
So it's, his article.
Anyway, he talks about like, what is the relative performance of these things and tries to talk
about it from a, well, sure.
It's cool to think of how it looks in code, but is one faster or one slower than the other?
Okay.
And this actually came up on talk Python as well.
And so I said, look, if we're going to come up with an example, let's have a class and a
base class.
And let's have the base class define an attribute.
And sometimes let's try to access the attribute.
And when you don't have the base class, it'll, or when you only have the base class, it'll
crash, right?
Cause it's in the derived class.
So let's say we have two ways to test.
We could either ask, does it have the attribute and then try to access it?
Or we could just right to access it.
And it says, well, look, if it, if it works all the time and you're not actually getting
errors and you're doing this, it's 30% slower to do the look before you leap.
Cause you're doing an extra test and basically the try accept block is more or less free.
Like it doesn't cost anything if there's not actually an error, but if you turn it around
and you say, no, it's not there.
All of a sudden it turns out the ask the, the try accept block is four times slower.
That's a lot slower.
Oh really?
Because the raising of the exception, figuring out this call stack, all that kind of stuff
is expensive.
So instead of just going, does it have the attribute?
You're going, well, let's do the whole call stack thing, every error, right?
And create an object and throw it and all that kind of stuff.
So it's a lot slower when there are errors.
And anyway, it's a, an interesting thing to consider if you care about performance and things
like parsing integers or parsing data that might sometimes fail, might not, you know, sometimes
it doesn't fail.
Yeah.
Okay.
Devil's advocate here.
His example doesn't have any activity in the ask for forgiveness.
If it isn't there.
That's the way I saw when I first read it as well.
There's two sections.
There's like one part where he says, let's do it with the attribute on the drive class
and let's do it again a second time by taking away the attribute and seeing what it's like.
Right.
But I mean, the code that if it, if it isn't exist, it just doesn't do anything.
Right.
Whereas in reality, you're still going to have to do something.
Yeah.
You got to do something.
Notify the user.
It's wrong.
Yeah.
Whatever.
Yeah.
Okay.
Yeah.
For sure.
That's a good point.
Like it's just basically a try except pass.
Yeah.
So what do you think about this?
So what I think is you're going to have to write the try except anyway, almost all the
time.
And you don't want both.
Like that doesn't seem good.
That seems like just extra complexity.
So when it makes sense, just go with ask for forgiveness.
Just embrace exceptions.
Right.
Remember you have a finally block that often can like get rid of a test as well.
You have multiple types of error except clauses are based on error type.
I think people should do a lot with that.
That said, if your goal is to like parse specific data, right?
Like I'm going to read this number I got off by off of the internet by web scraping and there's
a million records here.
I'm going to parse it.
If you want to do that a lot, a lot faster, that might make a lot of sense.
I actually have a gist example that I put up trying to compare the speed of these things
in a mixed case.
So like the cases we're looking at here are kind of strange because it's like, well,
there's, it's all errors or it's zero errors.
Right.
And then it doesn't really do anything, which are both weird.
So I have this one where it comes up with like a million records strings.
And most of the time they're number, they're legitimate numbers, like 4.2 as a string.
And then you can parse it.
And what I found was if you have more than 4% errors, I think it was four, like 4.5% or
something errors, Aaron's data, it's slower to use exceptions.
Okay.
The cutoff is 4% errors.
And I think if you have more than 4% errors, then the exceptions become more expensive.
That's right.
Anyway, it's something that people can run and get real numbers out of and play with it in
a slightly more concrete way.
But I don't know.
What do you think?
I think you start out by focusing on the code and making it easy and clear to understand
and then worry about this stuff.
Yeah.
So I don't actually put either.
I don't usually do the asking or the checking stuff.
And that is one of the things that's good about bringing this up is that is more common in
Python code is to not check stuff, just to, you know, to just go ahead and do it.
And then I write a lot of tests.
So I write a lot of tests around things.
Yeah.
And so either case checking for things or like, for instance, if it is, if it is input,
if I've got user input, I'm checking for things.
Yeah.
I'm going to do it checks ahead of time because I want, because the behavior of what happens
when it isn't there or when there's a problem, it isn't really a problem.
It needs to be designed into the system as to what behavior to do when something unexpected
happens.
But the, in normal code, like, well, what happens if there's not an attribute?
Well, you shouldn't be in that situation, right?
You shouldn't be in that situation.
And I usually push it up higher.
I don't have try accept blocks all over the place.
I have them around APIs that might not be trustworthy or around external systems or something.
I don't put try accept blocks around code that I'm calling on my own code.
Things like that.
Yeah.
I'm with you on that.
That makes a lot of sense.
The one time that I'll do the test, the look before you leave style, is if I think I can
fix it, right?
Does this directory not exist?
I'm going to write a file to it.
Well, I'm just going to make the directory.
Then I'm going to write to it, you know?
Those kinds of tests can get you out of trouble.
But if you're just going to say this didn't work, chances are, you know, you still need
the error handling and exception format anyway.
Yeah.
And you're probably going to throw an exception.
So, anyway.
Cool.
So, you probably should get your code right, test it, and then just stick it in GitHub.
Get in your repository.
Make sure it's all up to date, right?
Oh, I was wondering how you were going to do that transition.
So, yeah.
That's good.
I was following a discussion on Twitter, and I think, actually, I think Anthony Shaw may
have started it, but I can't remember.
But dealing with different, if you've got a lot of repositories, just sometimes you have
a lot of maintenance to do or a little, you know, some common things you're doing for
a whole bunch of repos.
And there's lots of different reasons why that might be the case or related tools or
maybe just your work.
You've got a lot of repos.
But there's a project that came up in this discussion that I hadn't really played with
before, and it's a project called My Repos.
And on the site, it says you've got a lot of version control repositories.
Sometimes you want to update them all at once or push out all your local changes.
You may use special command lines in some repos to implement specific workflows.
Well, the My Repos project provides an MR command, which is a tool to manage all your version
control repositories.
And the way it works is it's on directory structures.
So it's a, and I usually have all of my repos that I'm working with under a common, like,
projects directory or something so that I know where to look.
And so I'm already set up for something like this might work.
And you go into, into one of your repos and you type, if you have this installed, you type
MR register.
And it registers this under, registers that repo for common commands.
And then whether you're in a parent directory or one of the specific directories and type
a command, like for instance, if you say MR status, it'll do status on all of the repos
that you care about or update or diff or something like that.
And then you can build up even more complex commands yourself to do more complicated things.
But I would, I mean, I'm probably going to use it right away just for just checking the
status or doing polls or updates or something like that on, on lots of repos.
So this looks neat.
Yeah, it looks neat.
I like the idea a lot.
So basically I'm the same as you.
I've got a directory, maybe a couple of levels, but all of my GitHub repos go in there, right?
I grouped them by like personal stuff or work stuff.
But other than that, they're just all next to each other.
And this would just let you say, go do a Git poll on all of them.
That's great.
Yeah.
Or like, for instance, at work, I've got often like three or four different related repos
that if I switch to another project that I'm working on, I need to go through and make sure
I'm not sure what branch I'm using or if everything's up to date.
So being able to just go through all, like even two or three, being able to go and update
them all at once or just even check the status of all, it'll save time.
And then for the show, at least somebody that interviewed for a testing code, at least,
Adam Johnson wrote an article called maintaining multiple Python projects with my repos.
And we'll link to his article in the show notes.
Yeah.
Perfect.
I like this idea enough that I wrote something like that already.
You did.
Well, what I wrote is something that will, it'll go and actually synchronize my GitHub account
with a folder structure on my computer.
So I'll go and just say like repo sync or whatever I called it.
And it'll use the GitHub API to go and figure out all the repos that I've cloned or created
and the different organizations like talk Python organization versus my personal one.
And then it'll create folders based on the organization or where I forked it from and then
clone it.
And if it's already there, it'll update it within.
It'll like basically pull all this down.
Oh, that's cool.
I need that.
It was a lot of work.
This seems like it's pre-built and pretty close.
So it looks pretty nice.
The one thing it doesn't do is it doesn't look like it doesn't go to GitHub and say, oh,
what other repos have you created that you maybe don't have here?
Maybe you want that.
Maybe you don't.
If you've like forked Windows source code and it's like 50 gigs, you don't want this tool
that I'm talking about.
But if you have reasonable size things like I forked Linux.
Okay, great.
That's going to take a while.
But normally it would be I think it would be pretty neat.
Yeah.
Another thing that's neat around managing these types of things is Docker.
And did you know that Python has an official Docker image?
I did not.
I didn't either.
Well, I recently heard that, but it's fairly new news to me that there is an official Docker
Python image.
So theoretically, if you want to work with some kind of Linux Docker machine that uses Python,
you can go and Docker run or to create the Python one.
Right.
So it's not super surprising.
It's just called Python.
Right.
But it's yeah, it's just called Python.
That's it.
I believe so.
Pretty straightforward working with it.
But I'm going to talk about like basically looking through that Docker, that official Docker
image.
So Itamar Turner Trouring, who was on Talk Python not long ago, talking about Phil.
And we also talked about Phil and Python bytes, the data science focused memory tool.
He wrote an article called a deep dive into the official Docker image for Python.
So basically it's like, well, if there's an official Docker image for Python, what is it?
How do you set it up?
Because understanding how it's set up is basically how do you take a machine that has no Python
whatsoever and configure it in a Python way?
So this is using Debian.
That's just what it's based on.
And it's using the Buster version because apparently Debian names all their releases after characters
from Toy Story.
I didn't know that.
But yep.
Buster.
Buster is the current one.
So it's going to create a Docker image.
You create the Docker file.
You say this Docker image is based on some other foundational one.
So Debian Buster.
And then it sets up slash user slash local slash bin for the environmental path.
Because that is the first thing in the path.
Because that's where it's going to put Python.
It sets the locale explicitly to the EMV language is to UTF-8.
There's some debate about whether this is actually necessary because current Python also defaults
UTF-8.
But, you know, here it is.
And then it also sets an environment variable Python underscore version to whatever the Python
version is.
Right now it's 385.
But whatever it is, that's kind of cool.
So you can ask, hey, what version is in this system without actually touching Python?
That's cool.
And then it has to do a few things like register the CA certificates.
Like, I've had people sending me messages that are taking courses.
And they're trying to run the code from, you know, something that talks to requests,
whether it's SSL certificate endpoint, HTTPS endpoint.
And they'll say, this thing says the certificate is invalid.
I'm like, the certificate's not invalid.
What's going on here?
Right?
And almost always, something about the way that Python got set up on their machine didn't
run the create certificate command.
So there's like this step where Python will go download all the major certificate authorities
and like trust them in the system.
So that happens next.
And then it actually will set up things like GCC and whatnot.
So it can compile it.
It's interesting.
Downloads the source code, compiles it.
But then what's interesting is it uninstalls the compiler tools.
It's like, okay, we're going to download Python and we're going to compile it.
But you didn't explicitly ask for GCC.
We just needed it.
So those are gone.
Right?
Cleans up the PYC files and all those kinds of things.
And then it gives an alias to say that Python 3 is the same as Python.
Like the command, you could do it without the 3.
Another thing that we've gone on about that's annoying is like, I created a virtual environment.
Oh, it has the wrong version of pip.
Is my pip out of date?
Your pip's probably out of date.
Everyone's pip is out of date.
Unless you're like a rare, like two-week window where Python has been released at the same time
like the modern pip has been released.
So guess what?
They upgrade pip to the new version, which is cool.
And then finally it sets the entry point of the Docker container,
which is the default command to do if you just say Docker run this image,
like Docker run Python 3.8-slim-buster.
If you just say that by itself, what program is going to run?
Because the way it works is it basically starts Linux and then runs one program.
And that program exits, the Docker container goes away.
And so it sets that to be the Python 3 command.
So basically, if you Docker run the Python Docker image, you're going to get just the REPL.
Interesting.
Yeah, you can always run it with different endpoints like Bash and then go in and like do stuff to it
or run it with MicroWSGI or Nginx or whatever.
But if you don't, you're just going to get Python 3 REPL.
Anyway, that's the way the official Python Docker image configures itself from a bare Debian buster
over to Python 3.
Neat.
Yeah, neat.
I thought it might be worth just thinking about like, what are all the steps?
And you know, how does that happen on your computer?
No, that's good.
Because yeah, I have been curious about that.
I was going to throw Python on a Docker image.
What does that get me?
Yeah, exactly.
And that's what it is.
Oh, you could also apt install Python 3 dash dev.
Yeah, that might be cheating.
All right.
What's this final one?
Oh, so it was recommended by, we covered some craziness that Anthony did an episode or two
ago.
And somebody commented that maybe we need to only in a pandemic section.
Yeah, that sounds fun.
So I selected Nanner Most.
No, sorry.
Nanner Nest.
It's an optimal peanut butter and banana sandwich placement.
So this is kind of an awesome article by Ethan Rosenthal.
Talks about during the pandemic, he's been sort of having trouble doing anything.
And so he really liked peanut butter and banana sandwiches when he was just still even.
He got picked this habit up from his grandfather, I think.
Anyway, this is using Python and computer vision and deep learning and machine learning and a
whole bunch of cool libraries to come up with the best packing algorithm for a particular
banana and the particular bread that you have.
So you take a picture that includes both the bread and the bananas or the banana you have.
And it will come up with the optimal slicing and placement of the banana for your banana
sandwich.
Wow, this is like a banana maximization optimization problem.
So if you want, you got to see the pictures to get this.
So like, if you're going to cut your banana into slices, and obviously the radius of the
banana slice varies at where you cut it in the banana, right?
Is it near the top?
Is it in the middle?
It's going to result in different size slices.
On where do you place your bread, the banana circles on your bread to have maximum surface
area of bananas relative to the what's left of the bread, right?
Something like that?
Yes.
And he's trying to maximize, make it so that you have almost all of the bites of the sandwich
have an equal ratio of banana, peanut butter, and bread.
Oh yeah, okay.
It's all about the flavor.
I didn't understand like the real motivation, but yeah, you want to have an equal layer, right?
So you don't want that spot where you just get bread.
You actually learn quite a bit about all these different processes and there's quite a bit
of math here talking about coming up with arcs for, you have to estimate the banana shape as
part of an ellipse and using the radius of that to determine banana slices and estimates for,
because you're looking at a banana sideways, you have to estimate what the shape of the banana
circle will be and it's not really a circle, it's more of an ellipse also.
Yeah, there's a lot going on here.
Some advanced stuff to deliver your bananas perfectly.
I love it.
Actually, this is really interesting.
This is cool.
And it's, I mean, it's a silly application, but it's also a neat example.
Yeah, actually, and this would be, I think, a cool thing for, to talk about difficult problems
and packing for like a teaching, like in a school setting.
I think this would be a great example to talk about some of these different complex problems.
Yeah, totally.
Well, that's it for our main items.
For the extras, I just wanted to say I'll put the links for the Excel to Python webcast and
the memory measurement course down there and we'll put the Patreon link as well.
Let's see if you have anything else you want to share.
No, that's good.
Yeah, cool.
How about sharing a joke?
A joke would be great.
So I'm going to describe the situation and you can be the interviewer slash boss who has the
caption, okay?
Okay.
So the first, there's two scenarios.
The title is Job Requirements.
This comes to us from Eduardo Orochena.
Thanks for that.
And the first scenario is the job interview where you're getting hired.
And then there's the reality, which is later, which is the actual on the job day to day.
So on the job interview, I come in, I'm an applicant here and Brian, the boss says.
Invert a binary tree on this whiteboard.
Or some other random data structure, like quick sort this, but using some other weird thing,
right?
Yeah.
Something that is kind of really computer science-y, way out there, probably not going
to do, but kind of maybe makes sense, right?
All right.
Now I'm at the job and I've got like my computer.
I have a huge purple buy button on my website that I'm working on.
And the boss says, make the button bigger.
Yep.
That's the job.
Yeah.
Very nice.
Good, good.
All right.
Well, I love the jokes and all the tech we're covering.
Thanks, Brian.
Yeah.
Thank you.
Yeah.
Bye.
Thank you for listening to Python Bytes.
Follow the show on Twitter via at Python Bytes.
That's Python Bytes as in B-Y-T-E-S.
And get the full show notes at pythonbytes.fm.
If you have a news item you want featured, just visit pythonbytes.fm and send it our way.
We're always on the lookout for sharing something cool.
On behalf of myself and Brian Okken, this is Michael Kennedy.
Thank you for listening and sharing this podcast with your friends and colleagues.