« Return to show page
Transcript for Episode #180:
What's new in Python 3.7 and beyond
00:00 Michael Kennedy: The Python Core Developers recently released Python 3.7 and are now busy planning what's coming in 3.8. That makes right now a great time to dig into what's included in Python 3.7, and see what's on deck for this next great release of CPython. On this episode, we have Anthony Shaw back on the podcast to tell us all about it. This is Talk Python To Me, Episode 180, recorded October 1st, 2018. Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem, and the personalities. This is your host, Michael Kennedy. Follow me on Twitter where I'm @mkennedy. Keep up with the show and listen to past episodes at talkpython.fm and follow the show on Twitter via @TalkPython. Hey everyone, Michael here, just a couple of things to share before we get to our chat with Anthony. First, you might notice I sound slightly different this week. I've been experimenting with a new audio setup. Your feedback on whether this is actually an improvement is welcome, just send me a mention on Twitter via @mkennedy or @TalkPython. Second, I recently released a new course and this is one I've wanted to create for a long time. It's called Asynchronous Techniques and Examples in Python. This is the definitive course on asynchronous programming in Python. It starts out right away digging into the asyncio and the magical async and await keywords. We also discuss the time-tested techniques such as threading and multiprocessing, but, we don't stop there, once you are using threads, you need thread safety, covered. Want to write some async web apps with Flask using async and await, covered. We even discuss some ways to combine these techniques for when you have some mixed CPU-based work and some I/O-based work. We round out the course with leveraging Cython to make our threaded code go truly fast. If you're interested in this course, just visit talkpython.fm/async and if you act quickly enough, you can still grab the early bird discount. Now let's talk Python present and future with Anthony Shaw. Anthony, welcome back to Talk Python.
02:05 Anthony Shaw: Hi Michael, it's great to be back.
02:07 Michael Kennedy: Yeah, it's super to have you here. It's great to have you on as a regular. You're doing so many cool things.
02:11 Anthony Shaw: Thanks.
02:12 Michael Kennedy: Yeah, so just to let people know, you were on way back in episode 155 for practical steps moving to Python 3, and also in 168 for 10 security holes in Python. Those were both fun episodes. But we're kind of not just upgrading to Python 3. We're cutting edge at this point, right?
02:28 Anthony Shaw: Yeah, we're going all the way up to 11.
02:31 Michael Kennedy: This one goes to 11, awesome. Well, I'm definitely looking forward to doing that but, before we get into all the details of Python 3.7 and 3.8 and the speculation around there, let's just talk really quickly about what you do day-to-day so people know where you're coming from.
02:45 Anthony Shaw: Great, yeah, sure. I work at Dimension Data, it's a big IT company, and I actually work in HR, which might surprise a few people as I came from an R&D background and a couple of years ago I made a big career change, so I focus on learning now, similar to you, I guess, and skills development within the organization.
03:05 Michael Kennedy: Yeah, and just so people know, that's not like a team of 10, right? There's more than 10 people I think that take some of these programs you put together and stuff?
03:12 Anthony Shaw: Yes, so, we're about 31,000 employees, and I've been running a whole bunch of learning programs and stuff like that, trying to teach people Python, for example. About 4000 people are learning Python that work for us across the world, which has been fantastic, and running a whole bunch of other programs as well, basically trying to make sure that the skills of the people who work at the company are up to date with all the latest technologies.
03:35 Michael Kennedy: Wow, that's really cool, and you've done some good write-ups. Maybe I'll even link to them in the show notes. So that's your day-to-day, but then, when maybe you're on an airplane or you're like traveling, you decide to break out some massive open source contribution, something like that?
03:48 Anthony Shaw: Yes, so I...
03:48 Michael Kennedy: What else do you do?
03:50 Anthony Shaw: I have to travel quite a lot for my job, which means I spend a lot of time on the planes really quite bored. And there's only really so many Marvel movies you can watch. So, really, I kind of fill some time working on open source projects, involved in quite a few at the moment, so, all of them are Python. They were a range of different languages a few years ago but now I'm a hundred percent Python contributions only, so that's been excellent, and on the Apache Software Foundation, just got named as a PSF Fellow a couple of months ago, which was awesome.
04:23 Michael Kennedy: Yeah, congratulations on that, that's really awesome. Only five people per quarter get that honor and so that's a big deal, that's cool.
04:29 Anthony Shaw: Yeah, I don't really know what I did to get that but, it was really great to be among the amazing list of names of people who are on the list, so.
04:37 Michael Kennedy: Yeah, that's awesome. Cool, well, our topic for today is, Python current and beyond, right, at least at the time of this recording, people will go back far enough in time, maybe they'll hit 3.7 as something in the past but we're going to start by talking about what is cool in Python 3.7, people should pay attention to, they should learn about. Then we're going to talk about what's coming in the next version of Python, 3.8. So, let's start with Python 3.7, like, is it a big change? What do you think?
05:04 Anthony Shaw: I guess you kind of have to bring in the whole history of, well not the whole history of Python 3, but you have to kind of take a bigger lens to this because Python 3 has been out for 11 years now? Is it 10 years, 11 years?
05:15 Michael Kennedy: Since 2008, I believe. They just had, it was just 3000 days, like a year ago or something like that, so.
05:24 Anthony Shaw: So yeah, it's been about...
05:25 Michael Kennedy: 3,365
05:26 Anthony Shaw: Been about 10 years, but no offense to the developers who worked on it, but the first few releases weren't really production ready, so 3.0 to 3.3, were really where they introduced a lot of the major changes in Python 3 and there were a lot of stability issues and there were memory leaks and stuff like that that needed to be fixed.
05:46 Michael Kennedy: Also performance, right? Like a lot of people said, well, I need to do this thing and this thing is like 50% slower on Python 3 so, I'm not doing it.
05:53 Anthony Shaw: Definitely. There's some major changes in Python 3 which made things significantly slower in some of the earlier releases. But really the focus was, let's not just spend 10 years and then, ta-da, we've come out with something that's production-ready. They wanted to get something out there and get the feedback earlier on so, even though Python 3 has been out for a decade, I guess the stable releases have only been out for the last four years, five years now, so 3.4 was pretty stable, but 3.5 definitely. That's where you saw it get picked up by some of the major Linux distributions, for example. It starts to get packaged with operating systems and stuff like that.
06:30 Michael Kennedy: Yeah, that's a great point, because I feel like 3.5 is really where, it was clearly the right choice. Like, 3.4, there was a debate at that time. It was like, well, maybe, maybe not. At 3.5, it was like, what are you talking about? Come on, let's go.
06:41 Anthony Shaw: Yeah, exactly, and then 3.6 comes out, so 3.5 included the async/await, a lot more functions with asynchronous programming, and also 3.6 came out with more async/await features like the generators, for example, async with statements, so context managers, basically, and the f-Strings came out in 3.6, which is yet another way of formatting strings in Python, which is pretty cool.
07:09 Michael Kennedy: Because, I don't know what, 5 wasn't enough for however many there were?
07:12 Anthony Shaw: And if you want to get to use it, actually it's definitely my favorite way to format strings now.
07:15 Michael Kennedy: I'm with you. You know, the problem I run into with f-strings is, I'm like halfway through a string and then I decide, oh, yeah, now I want to put this variable here, and I got to go back to the beginning and put the f, and then go back and do it. And, you know, it's, I don't know, that's something I just got to train myself to put the f at the front more often.
07:34 Anthony Shaw: Yeah, so 3.7 came out and, yeah, I guess really, they're at that opportunity now where they can start adding some good new features for people, it's not just bug fixes and performance improvements, so, yeah, it's been a pretty significant change.
07:47 Michael Kennedy: Yeah, that's right, because Python 3 is really either as close to, as you know, same as Python 2, or faster, right? There's a lot of performance improvements where the argument used to be, well I can't switch to Python 3 cause it's too slow. Now it's, well, if I don't, I'm going to be behind, right?
08:03 Anthony Shaw: Yeah, so 3.7 was the first release of 3 that basically, across all the benchmarks, was quicker than Python 2.7, with one exception, which is the startup time, but that's still been an ongoing issue.
08:17 Michael Kennedy: Yeah, and it really depends on what kind of apps you're building whether that matters. If you're building like a background thing that runs in a queue or a web app, like, who cares about startup time? If you're building a little command line app, that might matter, right?
08:29 Anthony Shaw: Yeah, but it's still milliseconds, it's not.
08:33 Michael Kennedy: It's not, it's not .NET or something, right? Not WPF. I just want to, like while we're on this little micro-subject of Python 2 to 3, I just want to put it out there one more time, let's all call Python 2, legacy Python, and I think that's going to start to make a little dent with the managers. You know, we're still using the legacy Python. Could we stop doing that?
08:51 Anthony Shaw: Yeah, it came out in June, which is winter for us, so I'm on the other side of the world. So, yeah, it came out in June this year, so 2018. The first sort of beta versions came out earlier in the year, sort of February time, and there was a couple of alphas out very early on in the new year this 2018, so yeah, it's been out for, what are we at, six months now, nearly, so that's, yeah, it's kind of had the chance to pick up speed. Obviously it's not been included with any of the major distributions yet, so you have to go out of your way to download and install or upgrade to Python 3.7, so I'd say it's not as widely used as 3.6 or 3.5 yet, but it's definitely a major step forward.
09:31 Michael Kennedy: Yeah, it definitely is, and you know, if I log into my Ubuntu machines, it's Python 3.6.6, I think, which is already really awesome, right, that's way better than 2, like, that ships with the operating system, so that's really cool. But 3.7 has a bunch of great features. Now, the reason that I thought, hey, I should have Anthony on the show, is cause you wrote a pretty sweet course on, specifically, what's new in Python 3.7 and it's really quite consumable, it's like an hour, right? Do you want to just tell us quickly about your course, then we'll get into the details of the stuff?
10:02 Anthony Shaw: Yeah, sure, so the course is on Pluralsight and it's called What's New In Python 3.7, and, basically, I go through all of the new features and functions and show you how to use them, show you any gotchas and stuff like that. It's only about an hour, so it's not going to take much of your time to watch. But yeah, it's been fun to put it together because, with each alpha and beta release, I was running all the upgrades, or pretty much just running on a copy of the master branch on CPython development for a few months.
10:34 Michael Kennedy: Now that it's on GitHub, right? Yeah, it's finally on GitHub.
10:37 Anthony Shaw: Yeah, yeah, so, it's good to kind of keep on the bleeding edge, and also to read all the PEPs and get all the background and details and stuff, so, I put the course together to condense all of that information down into a way that's a bit easier for people to just watch, get a gist of, and get going with all the cool new stuff.
10:54 Michael Kennedy: Yeah, sweet, so, we'll link to that in the show notes as well. I guess it's worth pointing out, like, this came out in June, and Python right now is on an 18-month shipping schedule. So, not next June, but next Christmas, December, something like that, it should be Python 3.8, right, so that's the current schedule, anyway.
11:15 Anthony Shaw: Yeah, so about, in around August, maybe September, there was actually a Python 3.8 release scheduled already, I think in about September 2019, we're going to start to see some beta releases of 3.8. You can actually test some of the new features already if you go and download from the source.
11:33 Michael Kennedy: If you live at the tip of the master branch.
11:36 Anthony Shaw: Yeah, exactly, and there was a Python meetup in London last month, when I gave a quick lightning talk showing some new 3.8 features, and it basically segfaulted halfway through the demo, so.
11:50 Michael Kennedy: Don't do this in production just yet.
11:52 Anthony Shaw: Don't run it in production.
11:52 Michael Kennedy: Put it off a little bit.
11:54 Anthony Shaw: It's not even alpha ready yet, it's very, very, very early.
11:57 Michael Kennedy: Yeah, that's, that is pretty cool though. There's some really great stuff in there, but let's start with 3.7 and we'll talk about 3.8 in a little bit. So, I guess, probably the big star of the show for 3.7 is dataclasses, right? That's the thing that is totally new and pretty obvious, that most everyone could use, right?
12:14 Anthony Shaw: Yeah, so Python has always come batteries included, which is in the form of the standard library, and in the standard library, there's a whole lot of things you can do. You can talk to HTTP endpoints. You can work with a file system. You can even manipulate audio files like all from the standard library. There's all this great functionality built in and, really, there's been a bit of a gap in terms of working with data structures, like APIs, for example, or working with document databases. The gap's been filled by a couple of third-party packages. Django for example, has its kind of built-in ORM, which helps you, basically, create a class called a Django model and you can have attributes on the class and then when you save that to a database, it automatically creates all the fields and stuff for you, so, Django's not the only one to do that, there's lots of other ways of doing it. There's also packages like attrs, A T T R S, which is great, and which does a very similar thing, but the concept is that you have a class in Python, you put some fields on it and, basically, you can use that to represent data that's in some other system, so that could be an API you're talking to, or a database or it could be a memory store, for example, so it's a whole bunch of boilerplate code you have to create if you do this from hand, you have to create all the dunder methods that you want to use, so dunder init, for example, to dunder eq, dunder repo, dunder STR, etc.
13:48 Michael Kennedy: Right, so, it seems like you're kind of done when you've done the dunder init and you put your fields in there, but if you're going to, say, try to hash those things, or compare them or sort them, or you want them read only, all of those things require like a pretty good understanding of the Python data model and the Magic Methods and all that kind of stuff, and dataclasses are just, you know, frozen equals true type thing.
14:11 Anthony Shaw: Yeah, so there's some great features in dataclasses. Really what you have to do is, you create a new class and, that implements a dataclass and, within that, then you basically have attributes on the class, so these are class-level attributes. You have to give them type annotations, otherwise, they get ignored. If you're not aware what type annotations are, it's a colon after the variable name and then the type you want to use, but there's another change in type annotations in 3.7 as well, which we'll come back to maybe later. And, basically, you put the fields on there and then you're done, and then you can basically call that class and you can call the initializer or the constructor on the class and pass it all the fields that you've assigned, and then there's a whole bunch of really cool features you can use. For example, you can set default values. You can have default factory values. You can have, it implements dunder eq for you, dunder repo, dunder str, so, basically, it just gets rid of a lot of boilerplate of having a class which, basically, just stores data and represents some of that kind of data structure.
15:17 Michael Kennedy: Yeah, it's really sweet, and that dunder, that initializer, it writes that for you, right?
15:22 Anthony Shaw: Yeah, it writes everything for you so, all you need to do is declare the class and assign the fields, and then you're done.
15:28 Michael Kennedy: That's cool, can you inherit them? Can you create like a hierarchy of dataclasses?
15:31 Anthony Shaw: You can.
15:34 Michael Kennedy: I'm not saying you should. I just want to know if you can.
15:36 Anthony Shaw: You definitely can and there's a load of reasons why you'd want to do that. Now, if you want to inherit from a base class, it's important to note that, within dataclasses, you can have fields with default values and fields with, basically, require a value so, if you mix those things together, so if you have a base class that has both non-default and default fields, and then you have a inherited class, you can't mix them in the inherited class. This is probably one of the bigger limitations of the dataclasses package, is to do with the way that it builds the dunder init method, but it's, yeah, that's pretty, I guess a bit of an edge case, but, it's something to watch out for.
16:18 Michael Kennedy: Yeah, that's cool, and so, if I want to have some kind of check in my initializer, like this number has to be between zero and 10, but it's written that for me, like, where's that happen?
16:29 Anthony Shaw: Yeah, so you can do it in a couple of places. One would be a post init method, so after the auto-generated dunder init has been called, you can have your own method which does extra stuff at the end, so that could be a value check, for example. It doesn't do type checking, so even though it uses type annotations, I guess their Pythonic way of doing it is that, they're just a friendly reminder of the type and they're not guarded in any way.
16:56 Michael Kennedy: They're meant for linting and like build time checks, not runtime checks, right?
17:01 Anthony Shaw: Yeah, exactly. Another great feature is that you can use basically, you can create immutable types, so, by default, they're mutable, which means you can change the values. You can also create immutable types by setting frozen equals true when you basically instantiate them. The frozen basically means that they're immutable, you can't change their values, and also they'll implement a dunder hash method as well, so they're hashable, so you can use them in things like sets, also you can use them as dictionary keys, which is pretty cool.
17:34 Michael Kennedy: Yeah, that's really cool because you shouldn't be able to put a thing into the dictionary and then change the value of it and then make it no longer have the same key. That would definitely drive you insane, which is what it effectively means if the hash value changes, right?
17:48 Anthony Shaw: Yeah, so it's a really cool way of creating more complex dictionary keys I guess, and also, moving away from just representing everything in dictionaries all the time, I think when you're working with APIs and stuff like that and you're passing data around it's convenient to just stick with dictionaries cause you don't have to worry about what field you have and what types they are, etc. but it's definitely nicer and it's also a lot more testable to introduce a bit more concreteness, I guess, to the types.
18:19 Michael Kennedy: Yeah, it's cool and dataclasses kind of makes that less effort to do so correctly.
18:23 Anthony Shaw: Yeah, really easy.
18:27 Michael Kennedy: This portion of Talk Python To Me is brought to you by Tidelift. Open source software is everywhere. How can we, as a community, ensure that the open source software we rely upon continues to get even more awesome and more dependable? Tidelift believes the solution is hiding in plain sight. Pay the maintainers. They're dedicated to creating an effective way to do just that, and today, they're excited to share that they've reached over one million dollars in committed payments from maintainers available via the Tidelift platform. Yes, that includes Python maintainers. Income for a project grows based on usage, not the number of hours spent on the project, so you can build significant income around your open source projects. Tidelift is offering a guaranteed minimum $10,000 payout over the next 24 months to select projects in the Python ecosystem. If you maintain a Python project, visit talkpython.fm/tidelift to find out if your project is included. That's talkpython.fm/tidelift. The next one doesn't actually sound very exciting to me, but I'm sure it's super important, is that the locales and UTF-8 got a little bit of a change, right? What's the story around that? How much do we need to worry about it?
19:38 Anthony Shaw: I think we won't spend too much time on this cause it's really confusing.
19:42 Michael Kennedy: Any form of encoding, I think, is confusing. It's super confusing.
19:45 Anthony Shaw: Yeah, so, like, a locale means it's a, I guess the settings on your operating system which describe what country you're from, for example, and the way you like things to be represented, so, you know, in...
19:56 Michael Kennedy: Like digit grouping, or the order of month/date/year when you print out a date, things like that?
20:03 Anthony Shaw: Yeah, so like, in mainland Europe, most countries would represent the number 1000 with 1.000, whereas in the UK and the US, they would be 1,000, and so that's one change, I guess, and that would be in the locale. Other ones would be the encoding type, which is where UTF-8 comes in, the keyboard mapping, and there's a whole bunch of other things. Basically, there's a default, which is the sort of ANSI C locale, which was default like 20 years ago, maybe, but you know, since computing has moved on a bit, and we've had to support more countries than just the US.
20:41 Michael Kennedy: So, I would like to think that this is like the world going, let's accept everybody and let's support all of those languages, but how much of this has to do with emojis?
20:51 Anthony Shaw: It definitely helps with emojis. And hopefully that's not the main...
20:56 Michael Kennedy: My thumbs-up is a square, come on!
20:59 Anthony Shaw: Hopefully that's not the main driver. But yeah, there's quite a few reasons. Now, I guess, Python 2 doesn't really, or say, legacy Python, this problem is a bit more invisible because they use byte strings by default, whereas in Python 3, we use Unicode strings by default, so I guess that the thing is, if you're reading from a shell pipe, for example, and so you want to pipe the output of one command into your Python script, then Python will look at the locale, so look at the operating system to see what settings you've got locally. Now, the problem is that, not everyone configures their operating system correctly. There's also other scenarios, like people creating Docker containers, and not bothering to set the locale.
21:47 Michael Kennedy: Exactly, like all the cloud stuff, right? Yeah, all the cloud stuff, you just fire it up and you're like, oh, look, it works, but I pass in my data file, which was in one locale, and I tried to read it in another and, there it went.
21:59 Anthony Shaw: Exactly, so if you just have it set to the, I guess the most basic one, which would be the ANSI C, then that would not be with UTF-8, so Unicode would not work by default, so basically, in Python 3.7 they've made more assumptions about people miss-configuring the operating system to make it more friendly for Unicode.
22:21 Michael Kennedy: Nice, so the next one that you have on your list, I think is really interesting, and it's interesting because, I'm like, oh, this is super. This is not, like, why would I ever use it? I would never use this, what is this about? And then, at the end I'm like, oh no, this is really useful, and I feel like I really learned something, and I always love it when that happens, so the next one is breakpoints, right? We used to like import PDB and we'd do the set trace thing, or, we would like fire up PyCharm or Visual Studio Code or something and just click in the side, so why does Python need a breakpoint? Is it just like an alias for PDB set trace?
22:55 Anthony Shaw: Yes and no. Python 3.7 introduces a new built-in function called breakpoint, so if you just write, breakpoint open close parentheses, anywhere in your code, when that line executes, it will jump into a debugger. By default, that will be the one that comes in the standard library called PDB, so anyone who's done debugging in Python today, I guess, would have learnt that to jump into a debugger, you basically type import pdb:
23:28 Michael Kennedy: Wait, wait, wait, what did you just say?
23:31 Anthony Shaw: Semicolon.
23:33 Michael Kennedy: Oh my gosh, all right. Yeah, so you can put it on one line and comment it out real easily, right?
23:36 Anthony Shaw: Yeah, so, you basically, to enter a breakpoint in Python requires two separate statements, and having two lines of code to do a breakpoint kind of is confusing because then you've basically just changed your code in the way that you didn't really need to. It's also, I guess, not super-intuitive for people coming from other languages, people who are just used to working in an IDE and clicking on the red circle on the left-hand side, you know, how do you do that in Python? How do you step into a breakpoint and work in a debugging console within Python? So, what they've introduced is this new built-in function. By default, it will just call import pdb, pdb.set_trace, so it behaves the same way that you would expect.
23:36 Michael Kennedy: But you don't have to write a semicolon, that's better.
23:36 Anthony Shaw: Yeah, exactly, and it doesn't cause the linter to throw up, throw up its arms in anger, that you even dared to include a semicolon in your code.
23:36 Michael Kennedy: And so if that's all it was, I don't think actually it has that much value. I'm not against having it in the language, but it's not that huge of a thing, but then, what if I don't want to use PDB, like, I don't like it, there's other, even in the terminal, there's other nice, sort of semi-graphical debuggers that are pretty cool, right?
23:36 Anthony Shaw: Yeah, so what it actually does is, it will eventually call a breakpoint hook, which is, I guess, a global variable within the sys module, and, but like I said, by default, that's set to pdb.set_trace, but you can change it to something else so, there are loads of other debuggers of Python which are a lot more friendly to use. Also, there's browser-based ones like Web-PDB, for example, so Web-PDB, when you hit that instead of, as your breakpoint, it lets you pause the code and it'll actually start up a little web server and you can open up your browser and you can navigate to it, and it's just a lot easier to see and explore through the local scope and the global scope in a browser with a whole tree and everything, than it is to get it in a little text console.
23:36 Michael Kennedy: Yeah, that's awesome, and basically, the breakpoint lets you customize what debugger gets called when you type breakpoint().
23:36 Anthony Shaw: Yeah, so you can do it either in code, so in Python, you can change the value of sys.breakpoint_hook or, alternatively, you can set it using an environment variable, which is also pretty cool. So, if, for example, if you wanted to have breakpoints in a certain part of your code because you wanted to debug something that was running across multiple machines, and one of the debuggers would be a local one and one of them might be a remote one, then you could have an environment variable set to basically change which debugger you want to use whenever breakpoint gets called.
23:36 Michael Kennedy: Yeah, that's pretty awesome, so even remote debugging, I love it.
23:36 Anthony Shaw: Yeah, it's great, I think it's a great feature.
23:36 Michael Kennedy: Yeah, the next one is pretty cool. So, time seems pretty accurate in Python already. So, even like, if I subtract two datetimes and I get a timedelta, there's a lot of accuracy to like, DT dot total_seconds or whatever it is, but in Python 3.7, there's more seconds, or more parts of seconds.
23:36 Anthony Shaw: Yeah, this one really confused me when I was doing the research. I think anyone who's worked with time accuracy, this would totally make sense, but if you have put it in the, I don't really care, I just want the rough, I want the time, and it sounds pretty accurate camp, which is where I was definitely from, then, when the announcement came that they were introducing these nanosecond resolution time functions. It's like, okay, great, why do I need that much accuracy in my application? So, basically, what they've changed in 3.7 is they've introduced some new functions into the time standard library module, and they have the append _ns, for nanoseconds and basically, you've got a lot more accurate representation of time.
23:36 Michael Kennedy: Right, so it's not like time now returns better numbers. You have to call time_ns, right?
23:36 Anthony Shaw: Yeah, they're different.
23:36 Michael Kennedy: It's a separate function.
23:36 Anthony Shaw: They're different functions and, for whatever reason, the old functions that would give you the time epoch. So the time epoch was the first of January, 1970. Apparently nothing happened before then, but that's basically the beginning of time. In Python it'll give you a floating point number back, and anyone who's worked with floating points before, will know there can be all sorts of inaccuracies, so the number of seconds since the first of January 1970 is sort of, is the major number, and then the actual floating point itself is the parts after that, so it's, you know, the milliseconds, for example. Now, the problems is that, you get all sorts of rounding issues, and also they're not particularly accurate, so it's, it is not necessarily in milliseconds. It's almost like the closest number that it could find depending on the operating system and the CPU that you're using. So there can be these gaps and issues with accuracy in Python. Anyone who's used Python before to try and measure time very accurately will probably know about these limitations. So, in 3.7, they've introduced a new nanosecond function, which is awesome, and it will give you that in basically a long integer, which is the Python integer.
23:36 Michael Kennedy: Nice, well I know that in, that there's the time_ns, so what else, are there other _ns functions I can use?
23:36 Anthony Shaw: It's this gettime_ns, there's settime_ns, which you can only use in certain situations, and there's a couple of others as well, but those were the major ones.
23:36 Michael Kennedy: Yeah, nice, for this next one, what do you think, before we get into the details of what's new, let's just talk about type annotations. You hinted at this in the dataclasses bit because it's very interesting it's even required, although not checked at runtime. How do you feel about type annotations?
23:36 Anthony Shaw: So I haven't really used them very much up until 3.7 for exactly the reason that we'll talk about this next feature, actually. So, in 3.6 they're kind of in a clunk to use. If you want to represent anything remotely complicated other than just a string or an int, you might have extra code you have to add to your Python modules. I think it's just cumbersome, and I haven't got a huge amount of value out of it so far. So I think,
23:36 Michael Kennedy: Right, like you might, if you want to say a list, you'd have to say import type List from typing like capital L List, not lowercase l list, things like that, right?
23:36 Anthony Shaw: Yeah, cause you know, a lot of my code that I would write would return a tuple, for example and the first entry would be a integer, and the second one would be a dictionary, and in the dictionary then, there'd be a certain structure, and you know, by the time you've had to describe all of that, it's just, such a pain, and it adds so much code to your, and the type annotations themselves, the way to represent complex types, is not particularly intuitive, and if you're not familiar with type annotations either, then it's quite a bit of a blocker.
23:36 Michael Kennedy: Yeah, you know, my take on them is, I really like them, but I don't think they belong everywhere, but I really love type annotations on the boundaries, so if I have like a data access layer, and I've got some functions you call, like, those functions you call, I love to put like annotations on, well, you get a list of these things back, or this one could be an optional user cause they might not exist in the database, and then that kind of flows enough through the editors for me that, like, everything else kind of picks it up, but I don't turn it into like C# and put types on everything that exists, right, even though you could.
23:36 Anthony Shaw: Yeah, one of the other big limitations is that, if your, the type is actually a class that you've declared somewhere, then you have to import that in order to add it as an annotation.
23:36 Michael Kennedy: Yes, yeah, and that's partly what drove the changes for 3.7, right? Like, so the place that made me the most crazy was, it cannot be solved in the most straightforward way, is that I have, like, say, some kind of method on a class, like a class method on a class, that returns an instance of that class. I cannot say in the type system, it returns that class, because it's not yet defined. I mean, you could use like the string, sort of cheating style, but you can't, you'd have to go realize, there's this other way to, like, talk about it when it's not imported, and it's not as well as enforced, and so on.
23:36 Anthony Shaw: Yeah, so the example I use in the course is sort of fictional, but you can have a theater class, for example, and in the theater, it has a number of seats, and you have a seat class which is in a separate file, so in a separate sub-module, and then, you've got an attribute on the seat which says which theater it belongs to, or you've got an attribute in the theater which says how many seats there are and you can reference the seats. Now, in order to do that as a type annotation, you need to import either one from each other, which creates a circular import, which Python's pretty good at handling. It's not as bad as it was in C, but what it's kind of led to is, kind of similar to what you see in Java and C#, which is the first 30 lines of every file is just import statements, which, it is a bit of a limitation, really, cause you're not using them for anything other than to add a type hint to the method in terms of what it returns or what it expects, as parameters, you're not doing anything with it, and the imports, obviously, you know, add quite a bit of time as well, it slows the application down.
23:36 Michael Kennedy: It's not a compile time thing, it's a runtime thing, so when you import all those things to just declare, here is the return type, that actually slows down the startup of that program because now it's doing more stuff just to get to that one function.
23:36 Anthony Shaw: Yeah, yeah, exactly.
23:36 Michael Kennedy: All right, so what's 3.7 add for us? Like, how does it change this or make it better?
23:36 Anthony Shaw: So, what I've introduced is, delayed evaluation type annotations, which is a very fancy way of saying that basically what happens is, when your code gets parsed, so in Python, to create the, I guess the executable code, the Python files, the text files, all the code that you've given it needs to be parsed first, and then it needs to be compiled, so at the parser stage, if you've imported from dunder future annotations, and that, it will enable these delayed evaluations. Then what it does is, it goes and looks at all your type hints, and if you'd imagine, let's say you've got a method, and you want to set the return type, so you'd do a colon, and then the type hint of the type that you want to return, basically, what a parser will do is equivalent to just putting double quotes either side of that name, so it's basically, it's a string literal, as a type hint, instead of the actual reference to the type.
23:36 Michael Kennedy: Right, which already works in the previous version, like, you can do that, those sort of put it in the quotes, and that actually works for, say, like the class self-reference thing I talked about, but then you don't get like, say, refactoring help and things like that, right?
23:36 Anthony Shaw: Yeah, so, it causes issues with IDEs and other things like that. So basically what this does is, it supports a lot of stuff you'd get in IDE, so the IDE would know, well it should know, how to import those and reference them, but, it doesn't slow down your application at runtime and it's a lot easier to use, cause you don't have to worry about all the imports and things like that.
23:36 Michael Kennedy: Yeah, very interesting, the IDEs and the linters, they know how to still check for those things even though Python at execution time doesn't necessarily do the imports, right?
23:36 Anthony Shaw: Yeah, it basically pushes the problem down the path a bit, though, because if you actually want to use the type annotations to find the concrete types, then you need to call a special new function called get_type_hints, and you need to pass it the local and global scope, and when you call that function, the thing, the type hints that you, if they'd reference there, a class, for example, then you will have need to imported that class by that point, otherwise that function won't work, so, you still have to import stuff if you want to use the type hints, but it basically assumes that most people just want to use them as general annotations. They don't want to use them to actually reference concrete types.
23:36 Michael Kennedy: Right, yeah. Very interesting. So, while we're on this type annotations thing, there's some very interesting stuff happening around type annotations in the later versions of Python and they all seem to be about trying to make Python faster, actually, I mean, obviously it was introduced to make it more understandable, and help the tooling and stuff, but, so, Cython has recently started adopting, if you have regular Python 3 type annotations, it'll use those instead of its funky way of declaring variables, so it can actually take just annotated code and compile it to C better, which is pretty cool.
23:36 Anthony Shaw: That's awesome. I didn't know that.
23:36 Michael Kennedy: Yeah, no, I just learned it as well, and, do you know about mypy, which is like the type verifier, it's like a linter, but like deeply for typing?
23:36 Anthony Shaw: Yeah, it's like an introspection tool, isn't it?
23:36 Michael Kennedy: Yeah, so that's pretty interesting, and there're some tools to like generate the type annotations, based on like runtime stuff, and various interesting things, but what also is interesting is Dropbox is releasing this thing called mypyc, which is a compiler that takes annotated, statically typed Python code and compiles them to C extensions.
23:36 Anthony Shaw: Wow, okay, that's really cool.
23:36 Michael Kennedy: So these are both kind of interesting things that people are playing with, so anyway, those are worth maybe looking at.
23:36 Anthony Shaw: Yeah, and the important thing with this feature is that in the PEP, it says that it will be the default behavior in Python 4.
23:36 Michael Kennedy: That's pretty awesome, there's going to be a Python 4 pretty soon.
23:36 Anthony Shaw: Well, we don't know how long, but.
23:36 Michael Kennedy: Yeah, well. A couple of 18 month iterations, there's actually talk, Lukasz Langa is overseeing the release for Python 3.7, 3.8, I believe, definitely for 3.8, maybe 3.8 and 3.9. I don't want to give you..
23:36 Anthony Shaw: 3.8 and 3.9.
23:36 Michael Kennedy: The wrong numbers, yeah, thank you, and he's trying to get the release cycle down to yearly, but that's kind of on hold for a little bit. Okay, so, maybe it'll be quicker than it otherwise would have been by six month segments. All right, so, we opened this section, the whole podcast a little bit, by talking about performance in Python 2 and 3, so 3.7 brings some interesting new performance benefits, right?
23:36 Anthony Shaw: Yes, so, I guess, calling methods, or calling functions in Python, definitely has an overhead, or an associated cost.
23:36 Michael Kennedy: I got to say, when I first learned Python, I was blown away at how slow a function call was, compared to if I inlined it effectively.
23:36 Anthony Shaw: Right, yeah, and I think...
23:36 Michael Kennedy: It's a big deal.
23:36 Anthony Shaw: And this leads to people writing code in certain funky ways, copying and pasting things a bit more, once they learn about this quirk of Python. Especially if you're using some of the frameworks, because the number of, I guess turtles deep, or the number of layers that it has to go through, functions calling functions, it can really kind of slow down the application.
23:36 Michael Kennedy: Yeah, yeah, I know in SQLAlchemy that Mike Bayer very carefully worked on the architecture to minimize the function call depth, actually, for performance reasons. Like, there's a lot, so in 3.7, calling functions gets somewhat faster, not quite like inline assembly fast, but faster, right?
23:36 Anthony Shaw: Yeah, so, basically, they've introduced in some new op codes and there's a different implementation for calling, and it's quite specific, actually. It's not just functions and methods in general, it has to be in a bound method, which means, if you've got a class and you've got a method on the class, you've instantiated it, when you call it, if that class doesn't have any keyword arguments, then it will be faster than before, so like I said, it's quite specific, but that probably covers quite a lot of cases.
23:36 Michael Kennedy: But that's a really common case, right? There's a lot of times when you have some kind of object and it has a function and you want to call it, like, if objects don't have functions, why would you create them, right? Just, just don't put them in a data structure or something, right? So, it's obviously nice that those are there, and not, I wouldn't say the majority of functions have keyword arguments, so it's a good improvement.
23:36 Anthony Shaw: Yeah, no, I think it's a good step forward, so it is 10 to 15 percent faster, and in benchmarks, there's basically a speed boost because of that knock-on effect.
23:36 Michael Kennedy: Yeah, that's really cool. So, maybe that might even affect, like, what you've pip installed, right, even if you have no classes, you might be interacting with like some turtle at layer three, that used classes, right?
23:36 Anthony Shaw: Yeah, so even just by moving to Python 3.7, you can actually see a speed improvement on a lot of applications, of up to 10 to 15 percent.
23:36 Michael Kennedy: Yeah, that's really sweet. So, after going through all this, doing all this research, if somebody's on 3.6, is it worth upgrading to 3.7, you think?
23:36 Anthony Shaw: Definitely yep, worth upgrading to 3.7.1.
23:36 Michael Kennedy: Anything other than zero?
23:36 Anthony Shaw: Yeah, I would never upgrade to a dot zero release in a production environment, but definitely, I think base classes are really cool. I've been using the attrs module for a few projects and really liked it, but, obviously, adding yet another dependency to your application is not always desirable, so it's nice to have that in the standard library. The speed improvements are always welcome. I think anything that can make Python faster is great. The type annotations change, I think, actually makes it a lot more appealing to me, cause you don't have to worry so much about all the import statements and things like that if you're not actually going to use them in that way, so, yeah, there're some big changes. I think the one that I've actually used the most is breakpoint, just since I've switched to 3.7, just got so used to just writing breakpoint instead of the import pdb, and also worked with the PyTest team to get breakpoint supported in PyTest, which was pretty fun, so I go to kind of dig in...
23:36 Michael Kennedy: Oh, that's awesome!
23:36 Anthony Shaw: Yeah, they got to dig in some of the details, by the way, somebody, Anthony Sottile, the other Anthony who works on PyTest, there's quite a few of us, actually, but he raised a pull request basically saying that he'd proved that my pull request basically did nothing, so I was a bit disheartened.
23:36 Michael Kennedy: Oh dear.
23:36 Anthony Shaw: But it has a lot of test coverage.
23:36 Michael Kennedy: That's good. I hear you with the dot zero release in production. Right now, what I'm doing is I'm still using 3.6.6, whatever the latest on Ubuntu is, right, keep that up to data as it ships with it, but I'm doing 3.7 in dev, and just making sure that I don't use any of the features yet that are going to cause trouble when I ship it, so I've definitely taken my site down by accidentally using an f-String in a utility function that was never called in the web app but was actually parsed by 3.5, and it wouldn't start the web app, so, yeah, it can sneak in there, but, you know, maybe, maybe you can start testing in dev or qa or some sort of staging environment, and then someday, switch pretty soon.
23:36 Anthony Shaw: Yeah, and then someday move to the 3.8.
23:36 Michael Kennedy: Yeah, you could move to 3.8, although this segfault thing you're talking about, maybe not yet, maybe wait a little bit, but yeah, let's do a quick preview of what's coming in 3.8.
23:36 Anthony Shaw: Yeah, sure.
23:36 Michael Kennedy: So there's a couple, like, quick, easy ones, so, for example, asyncio on Windows gets a different event loop, a proactor event loop. Actually, I have no idea what that is. I now want to go research it and see what that is, but that's pretty cool, that asyncio has these configurable, extendable event loops like uvloop and so on. Language, there's a few, like, simple language things like a continue statement used to be illegal in a final clause, but now it's not, but then there's a few other major ones. You want to maybe touch on the big ones, and we can hit a few small ones after that?
23:36 Anthony Shaw: Yeah, there's been, well, there's I guess for every major release of Python, there's all the proposals for new features, in the form of these documents called PEPs, so I've been reading I guess some of the proposed PEPs in detail and trying to understand what they do. There's a couple that have been accepted already. The big controversial one, I guess, was PEP 572, which is something called assignment expressions. The easiest way to understand it is that if you just do, in a REPL, x equals one, enter, and then x enter, it will return one, and assignment expressions basically introduce a new syntax which is, x colon equals one, which will return one, so those two, it basically squashes those two statements together into one.
23:36 Michael Kennedy: Right, and a lot of time you might be thinking, well, I would never use that, like, what is the value of this, but, if you're doing, well, expressions, right, if I'm doing like a list comprehension, and I want to do both a test and a creation of a variable or things like that, you know, like, there's a few interesting use cases where it does simplify the language a bit.
23:36 Michael Kennedy: All right, maybe like a Lambda expression where you've got to squeeze it into one line, possibly? I don't know.
23:36 Anthony Shaw: Yeah, yeah, especially, because yeah, you can't write return statements inside where something should be a statement itself.
23:36 Michael Kennedy: Yeah, even with semicolons, right, you shouldn't sneak them in there.
23:36 Anthony Shaw: Even with semicolons, you can't do that, no.
23:36 Michael Kennedy: Right, so, this was controversial, partly because, let's say close to half of the community, really thought, like, this just does not need to be added to the language, we don't do this, and there was like a, maybe a minority, I'm not sure, but another part of the community that really did want it, and so there was actually quite a bit of a disagreement about whether it should have been accepted, and the fallout of that is sort of the straw that caused Guido van Rossum to step down as BDFL.
23:36 Anthony Shaw: Yeah, I think that some of the comments got a bit nasty, to be honest, and some of the pushback, but, I don't know. I've seen places where it could be used, and I think, yeah, that could be useful, but I don't think I'd use it every day, like this is a once every now and again you'd use it kind of thing.
23:36 Michael Kennedy: Yeah. That's kind of how I feel about it, like, it's not a huge deal, but I'm not like looking forward to doing amazing stuff with it yet.
23:36 Anthony Shaw: Yeah, like an async context manager, like, you know, that's kind of cool, but it's not something you'd use in everyday code.
23:36 Michael Kennedy: Yeah, for sure, so the one that's coming that I'm really positive about is the None-aware or null-aware operators, tell us about that.
23:36 Anthony Shaw: Yeah, so this one has been proposed, but not accepted. I think it's in draft state at the moment. So it's basically, again, a new syntax, so anyone who works with Python a lot will know the sort of famous error that x is not an attribute of None type, or something along those lines.
23:36 Michael Kennedy: It's like, I think it's one of the most common exceptions is that, attribute error, something is not an attribute of None type.
23:36 Anthony Shaw: Yeah, so it's basically like it's the equivalent of a null pointer, or a null reference exception, in Java or C# or C++. It's the Python equivalent of that, is a None I guess is our special type to represent nothing, instead of null. It doesn't technically have a value. Similar to null, it's actually a special type in itself, which means that in this PEP, in the proposal, what they're proposing is basically a special set of syntax, based around the question mark, to allow you to basically follow branches depending on whether something is null or not. So, for example, if you had even a dataclass, actually, if you had a dataclass with some fields and one of the values of the fields was null, you could reference it, and then if that field itself, you wanted to call a method on it like, I don't know, let's assume it was a string you wanted to run the upper on it, if it was None, then you'd get upper is not a property of None type, but it's basically instead, you could do question mark dot as the method, accessor.
23:36 Michael Kennedy: Right, right, instead of object dot, like, init dot field dot upper, you'd say object dot field, question mark, dot, dot question mark, upper.
23:36 Anthony Shaw: Yes. It also introduces something called a ternary operator which is a double question mark, but that already exists in Python, but it's pretty long-form, you have to say, something equals something if the thing is that, else not this.
23:36 Michael Kennedy: Yeah, and it is a little bit longer, the sort of double question mark null coalescing, None coalescing operator thing, does not excite me very much, but this fluent style of these chaining function calls that at any step may be None, is super exciting to me because you might have to check first is the original object None, okay, then, if that's not the case, now is its name, you know, maybe it contains like a rich object, you know, is that like an address, like is the address None? Okay, the address is not None, so, is the street not None? Okay, now we can call to upper on it, that's like one line, right? Object question mark dot address question mark dot street question mark dot upper, and boom, you get either None, or you get an uppercase street name, I love it.
23:36 Anthony Shaw: Yeah, so if anyone works with like highly nested types, I guess, then, and you have to run all these, if that's not None, do this, if that's not None, do this, then yeah, this is super helpful. Could be, if it gets accepted.
23:36 Michael Kennedy: Yeah, it could be, it's theoretic, it's one possible branch of the future, this will be very helpful. Yeah, it just, one thing that I dislike a lot in my code is like sort of seesaw nested jaggety code, and this can like turn that into just like a one-liner, which I think is great. You also have called out runtime audit hooks. What might that be useful for?
23:36 Anthony Shaw: This is probably a security feature mainly, but this again is the proposal, it's not been accepted yet, but the idea is that if any built-in functions, or things in the sys module, or some of the low-level functions within Python standard library, whenever they got called by either your code or somebody else's code, you've got the option to set like an audit hook, so you might write a special application like a profiler, for example, if you're writing a profiler, and you want to see every time somebody tries to open a network socket or calls a URL or something, or if you wanted to develop security application that checked anyone who is opening file handles in the operating system, or...
23:36 Michael Kennedy: Right, you could build a really cool package that you can just import into your app at the beginning, and it says, these operations are allowed, these are not, like, no network access, no file access, and with this, maybe you could just stop that globally in the runtime, right?
23:36 Anthony Shaw: Yes, they're kind of similar to what you can do with SELinux, definitely everyone's familiar with SELinux. You can kind of lock down the kernel effectively for certain users and certain method calls can't be run, so this would be similar to, you could basically have a Python interpreter, and when certain things are done inside Python, then your method gets called as part of an audit.
23:36 Michael Kennedy: You could watch for the eval function.
23:36 Anthony Shaw: Yeah, exactly. It is a little bit light on the details. The implementation's not finished either, but I know that Steve Dower's working on this PEP, and he has said if anyone wants to help contribute to the implementation or do some testing, then the door is open.
23:36 Michael Kennedy: Nice. Get in touch with Steve Dower, perfect. So maybe I'll just throw out a few real quick ones also that are like more minor, so for virtual environments, it now has an ability to activate this through PowerShell on Windows, which, if you care, if you use PowerShell on Windows, that's pretty awesome.
23:36 Anthony Shaw: Yeah, that's awesome. I don't know why you wouldn't use PowerShell if you're using Windows anymore. If you want to use MSDOS, that's fine, but it's not the greatest shell.
23:36 Michael Kennedy: Yeah, I like cmder, C M D E R, I think it's cmder.net, on Windows. So, optimizations around file copies, that's been improved on the Unix-based systems, right?
23:36 Anthony Shaw: Yep, and I think that makes a pretty big difference, actually.
23:36 Michael Kennedy: Yeah, there's some built-in, like, sys platform-specific fast copy, like I wonder if it uses the new Apple file system sort of replication stuff, which would be a lot faster and be very cool. You also removed pyvenv, the script, so, the expected way that you create a virtual environment is Python3 -m venv, right?
23:36 Anthony Shaw: Yes, I don't really use that, but...
23:36 Michael Kennedy: Well, that's what they tell you in 3.8. Other minor changes, and I guess one thing that's interesting is, Python 3.8 is temporarily, like, it's pretty far out, so this might not be a problem forever, but right now, with Guido stepping down and the governance decision, like the choice of how Python governs its decision-making is not decided, no new features are going into 3.8 until they can decide how to decide on those new features. That's a bit of a weird Catch-22 thing there.
23:36 Anthony Shaw: I bump into one of the core developers last month and they said that it's likely that there'll be fewer features in 3.8 than there have been in previous releases, unless, well, until they can work this thing out, and then even then, having I guess faster, sort of mainly bug fix and performance improvement releases is a lot more likely to be the way forward.
23:36 Michael Kennedy: Yeah, I suspect it's going to take a little while before this gets dialed in and it becomes a smooth process again, but I'm sure they're figure it out.
23:36 Anthony Shaw: Yeah, there's, well, there's a few major things that kind of need to happen in future releases. I guess one thing is the startup time. It's still slower than legacy Python. I think there's ways for them to optimize that in terms of what it loads at import, startup, and how it does all the importing and caching and stuff. Hopefully that'll be quicker in newer releases, and then there's a C API, which has over time got a little bit messy, so I think there's a few proposals of Python 4 for that to have a bit of a revamp.
23:36 Michael Kennedy: Yeah, that'll be interesting, that's a little far out, but definitely it'll be interesting. Maybe it'll be rewritten in Rust.
23:36 Anthony Shaw: Yeah, maybe.
23:36 Michael Kennedy: I don't know about that. Anyway, that's a ways out, but definitely something to look forward to. Well, I think that's probably a good place to leave it. We're probably low on time, I've kept you long enough, but before you go, I know I've asked you this at least back in Episode 155, but that was a while ago. I'll ask you again, if you're going to write some Python code, maybe your answer has changed, what editor do you use?
23:36 Anthony Shaw: Still Visual Studio Code, still like it.
23:36 Michael Kennedy: Right on, with the Python plugin. Yeah, it's definitely a nice thing, and it's like, I don't know how many times, millions, like five, 10 million, I don't know how many millions there are, of downloads of that Python extension for Visual Studio Code, but it's pretty remarkable.
23:36 Anthony Shaw: Yeah, it's great, although, I'm using Windows at the moment, with Windows Subsystem for Linux, which is causing me problems because Linux creates different virtual environments to Windows, so the IDE's using a different virtual environment to my actual application, which is causing all sorts of pain.
23:36 Michael Kennedy: There's all sorts of process isolation and like system isolation between the Windows Subsystem for Linux, and Windows itself, right?
23:36 Anthony Shaw: Yep, so that's causing.
23:36 Michael Kennedy: You're going to be an expert pretty soon, that'll be awesome. You'll be able to help people who are also doing this. Nice, all right, so, notable PyPI package?
23:36 Anthony Shaw: Yeah, I'd recommend people check out Black, if they haven't already. They're the code formatter, it's basically, if you're ever checking code and then the build fails because you've put an extra space at the end of a line or something, and you've had that frustration, then you can use this basically as a tool to go through your code and format it for you, so that it passes most code linters and style checkers and stuff like that, and it's basically no options, no configuration, you just run the command, give it a directory and it'll just go through and change your code for you. I have to do that a lot.
23:36 Michael Kennedy: That's sweet.
23:36 Anthony Shaw: I think the code it produces is really nice, really easy to read, and it's just really easy to get set up with.
23:36 Michael Kennedy: Yeah, most linters, they just complain to you, you should get rid of that space at the end or whatever. This one just goes, yeah, I'll fix that.
23:36 Anthony Shaw: Yeah, it just fixes it up for you and you check it in.
23:36 Michael Kennedy: Ah, right on. All right, final call to action, people are excited about 3.7, what do they do?
23:36 Anthony Shaw: They need to download it first, install it, test it out, run their applications on it, see how they're improved, and then check out some of the new features.
23:36 Michael Kennedy: Yeah, maybe check out your course?
23:36 Anthony Shaw: Yeah, and absolutely check out my course. Yeah, like I said, it'll take less than an hour of your time, it's 57 minutes, and you can find out all the details on Python 3.7.
23:36 Michael Kennedy: Yeah, awesome, I definitely agree, people should check it out, there's some great features and Anthony, thanks for being on the show to share them with everyone.
23:36 Anthony Shaw: Thanks, Michael, it's been great to be back.
23:36 Michael Kennedy: Yep, bye. This has been another episode of Talk Python To Me. Our guest on this episode was Anthony Shaw, and it was brought to you by Tidelift. If you run an open source project, Tidelift wants to help you get paid for keeping it going strong. Just visit talkpython.fm/tidelift, search for your package, and get started today. Want to level up your Python? If you're just getting started, try my Python Jumpstart By Building 10 Apps, or our brand new 100 Days Of Code In Python, and if you're interested in more than one course, be sure to check out the Everything Bundle. It's like a subscription that never expires. Be sure to subscribe to the show. Open your favorite podcatcher and search for Python. We should be right at the top. You can also find the iTunes feed at /iTunes, Google Play feed at /play, and direct RSS feed at /rss on talkpython.fm. This is your host, Michael Kennedy. Thanks so much for listening, I really appreciate it. Now, get out there and write some Python code.