 Ychydig sydd y cyfrifod y bywydau i'r IOT i'r ffordd o'r devices. Mae'r cyfrifodd o'r tyfnol. Chw'n mynd i'n ddaf i'n mynd i'n gwneud y cyrchwyn a'i gyrchwyn arbennwys cyrchwyn ar y cyfrifodd o'r llangwys cyrchwyn ac y gallwn i'n gyfrifodd o'r gyfrifodd o'r llangwys ac rast ac yn gyfrifodd i'r gyfrifodd a'r gyfrifodd i'r gyfrifodd a'r gyfrifodd. Mae'r unrhywbeth oedd y gallwn ni'n gwneud o'r pwysig o'r pwysig yn python yma, ac mae'n gweithio'n meddwl i'n ddweud o'r fawr o'r fawr o'r ddechrau, a'n gallu'n fawr o'r ddweud o'r ddweud o'r pwysig o'r ddweud o'r pwysig. Felly, yma'r wneud ymddangos? Mae'n gweithio yrym o y fanrif yn wych o jedныll fadeifydd, a dyma'n gweithio o dechrau fawr o fel fawr o'r ddweud o'r beleidiaod a ddweud o yn tuogol. Gweithred thyrwyd, mae'n gallu gwneud that am gael ei fnarsfal a os fel gwneud allan ag yr hyn jarsffu gwneud objectives, i fy Grenwer Oeddet York i rywunriaethrauenid forth. Mae'r iechyd yn ein rahol i fynt mewn gwahanol i g saoedd ym gyfafol ichon weakol o dda. sain sydd y gallwch yn unrhyw hwn yn ymdweud hynny sy'n cynharu gwneud nifer. Mae'n dweud assertio'r llyfr i'r gyfryd. Mae'n dda'n rhaid i'r gweithredu eu stryd o'r ddin yn dweud. Ac rwy'n meddiwch am ffwrddain yn ychydig ni'n mynd y gofio sydd maen nhw'n bryd ac yn dweud. Ond, wrth gwrs, wrth gwrs, ychydig wedi wnaeth ymddangos ran hynny'n gweithrio. Of course you might, but they allow you an extra way to express your intents behind what your programme does and if that type check is okay, then it gives you some sort of guarantee that you've written what you intended to. Types follow the syntactic structure of terms, which means that if this is lambda x or m is a function that so in Python you might read ffx return m, whatever m is, maybe that's a global variable say, and so then if x has type A and m has type B, then because types follow this syntactic structure, we can say automatically that the function that takes x and returns m is of type A to B because x is type A and m is type B, so that automatically tells us about the type of the function. Now having looked at that, you're probably thinking, this is not why I like Python, I don't want to do this in my Python, I don't want to learn about lambda calculus, I don't want to use any types, that's not what Python's for, right? That's something I hear people say, I've certainly heard colleagues say that that's not why they like to use Python, but Zenith Python says things like explicit is better than implicit. Yesterday I saw someone struggling with a door, one of these doors has these handles sticking out on the door and sort of pulling it and trying to open the door and then eventually it turned out it was a push door, it needed to be pushed. The handle on it was misleading him into thinking it was a door that needed to be pulled, so I think the thing is that it's better than implicit because often we get implicit wrong. These cues like the handle on the door is misleading, it's an implicit sign that the door needs to be pulled, but it just wasn't true. In industrial design, I think this is called an affordance and in human computer interaction as well. The analogy there is that it's like a type hint in Python, the annotation in Python here called a type hint as well. It's hinting that the door is of type pull door, but that wasn't true in that case, so it's better to be explicit. It also says that readability counts, types make our code more readable because I'll show some examples in a second where a variable name is maybe not expressive enough to say what it's actually doing or what it contains or what you pass to a function. In the face of ambiguity, refuse the temptation to guess. We don't want to be guessing what that variable is. We want something that tells us more directly exactly what it is. The other thing people say is if I'm writing Python, if it smells like a duck, I don't really care what type it is, but you do. That is what you're saying. You're saying if it talks like a duck, walks like a duck, smells like a duck, whatever, you're describing the type. You want the type of thing that smells like a duck. Duck typing is an abstraction for a bunch of possible values, just like I said a type is. Something that has attribute dot read, I'm saying I'm expecting this to be a value that has a dot read that fits into the definition of what I said a type is. Typing Python is not at all incompatible with that wish, so it's the real code I promised. These are all real examples I found in the code base I'm working on day to day. This function save event takes an argument called event. Maybe the doctor tells me something more about that. It's the event to save. There's nothing domain specific about an event for me. An event doesn't mean anything more to me than it does to you looking at that. Deploy devices. That probably returns some kind of collection of devices that are deployed. That seems reasonable to me. No, it returns the integer count of devices that are deployed. I don't know about you, but I never would have guessed that looking at that function. Get firmware manifest. I think that took some arguments that are not important. Returns the manifest contents or none, if none. That's a bit better. That's more reasonable. It's telling me something a bit more about what it returns. I still don't really understand what manifest contents are from that. That could be bytes or addicts of some more stuff that's inside the manifest, but it turns out to be a string. That's not terrible, but it could certainly be improved. Finally, this is one that actually does have a type annotation and it's telling me that it's a dict. If I get it, I have a dict of some stuff. What's inside that? What can I actually do with that? On the assumption that I've maybe convinced you, at least slightly, that this is something you might want to do, how do we actually do that in Python? The basic syntax is we have an identifier, a variable name that we usually have, and then we have equals of value, but we have this type annotation before it, colon, the type, and then equals of value. Or we might have a more complex type that takes, in square brackets, some other type that customizes it more further. I'm using vague words here because I'll get to explain that a bit more later. Or in functions, we have the argument type annotated in the same way and then the return type after a dash angle bracket arrow to say what type it's going to return. There it is in a function with a default value. We might have passwords. We're saying it's a string, Hunter 2. We can see clearly that that's a string, so we don't need to do that. If we're using a tool like Mypy to analyze this, it can figure that out. It's not that stupid, but this is just an example to show you. Then increment is a function that takes a number. It's an integer, and we increment by another number, which defaults to one. Then what we return is, of course, still an integer. Sets, tuples, ooh. Sets, tuples, and lists. These are an example of the more customizable type I mentioned. We say that it's a set, and then in the square brackets we say what it's a set of, what is the type of each element of that set. Similarly with tuple, we list, we have two, three, four, N, different things inside it, and what are the types of each of those things. Of course, these might be different things. It might be a tuple of string and end. The other form of a tuple is to say, this is the type. It could have any number of those types in it, so we don't know if this is a two tuple, a three tuple, a four tuple, but elements of it, however many it has, will be of this type, and similarly lists. For example, pythons is a set due to constraints on the page. It's a mutable object that may have more added to it, and certainly will if you're familiar with the pythons, as I'm sure you all are. So we see that that's a set of strings. A torque, like this one, types pylandinium string string. My initials are OJF. Someone's initials might be, they might only have two initials, they might have five initials, so we say that each element of initials is a string, but we don't know how many of those we're going to have. In my case, it's three, it might not be. List of talks that I have given is a list of those talks from previously, and they were of type tuple string string, so you can say we can nest these customisable types within each other, dictionaries. So now, again, like soren tuples, have two types, but rather than referring to the same sort of thing that, as you might guess, referring to the key type and the value type. So we've identified a dic type type because of value. So the age of people is a dictionary that maps from a certain age to a list of people that have that age, so we'd write dic int list string. It's quite old, it goes from string to ball, so Fred, if he's 123, he's quite old, so we put him in our is quite old dic. And people ages is a function that then reverses that age people dic. So it's turning, we can see that it takes in this age people dic intistra, and it defaults to age people, and then what it's returning is the opposite of that. So before we even look at the implementation of that, it's giving us a much more detailed hint than just the name alone does. We can sort of guess more just from looking at the type hints what the semantics of this function actually are. If we see something that's people ages, it takes a single argument that's dic intistra and returns the dic styr to in, well, we can make a reasonably good assumption about what that function might do. So we don't always care about the precise container type. We've seen dic strings, tuples now. We might just want something that we can iterate through and say whether something contains nuts. We don't care what the thing is, we just want to look for nuts inside it. So we can use an iterable type when we want to do this. And similarly sequence when we want some notion of order on the thing, say we're writing a function that returns the middle element from something. So the general sort of advice or best practice here is to be as flexible as possible in your argument so that your caller can give you... they're not as constrained in what they can supply you. So if we're returning the initials, the letters that someone's surname begins with for a bunch of people, rather than mandating that the person supplies us with a list of people's initials, we can say we don't really care about that, actually, we just want something that we can iterate through and then they can give us a tuple if that's more convenient to them. But when we return something, we should be specific that what we're returning to you is a set of things, rather than just saying it's something that's iterable again, because then when they get that value back, they can be more specific about what they can actually do with it, whereas if we just said we're returning an iterable, if they're also utilising tool like MyPy themselves, then they'll be more limited in what they can do because they'll only be able to do the things that you could do with any iterable. So not all functions return values, right? We might be returning none, like in the print keys example from the Dick page. So we can annotate that just with none, just say we're returning none here. Or we can use optional to say that we might return none or we might return something else, like when I showed the example of the manifest contents that might return the string or it might return none if the contents was none. So init, for example, always returns none. Or we might say we can print keys in some sort of colour and that's an optional string. So what if I think it's not that it might be none, but it might be an integer or it might be a string? Well, we can use something called union for that, which says exactly that. It's either this thing or another thing or something else. Again, I've shown two here, but it could be three different things that it might be. I'm going to have to start to speed up slightly. Args and quags, what we type here is not the full thing that we're getting, but just the elements inside. So if each of our args is an int, then we say our star args are type int and then what we actually get when we use it when we're checking with my pi is a tuple of that thing n-length inside. Similarly, with quags, it's always a dick from string, which of course it is the string being the name of the argument to whatever type we specify. It just reduces the verbosity really. So we might define a function cat that concatenates a bunch of strings that you give it. So we don't have to annotate that it takes a tuple of str. We just say str because it actually doesn't take a tuple of str anywhere, right? It takes those expanded. Similarly, set feature flags might take any number of quags, but each of them has to be ball. We want to type something that's a callable. Then we have two things again, but the first one is actually a list in its own rights, a list of the argument types, and then the second thing being the type that it returns. So we might have, for example, something that's a kind of not great re-implementation of map that just only works the integers. So we take something that's a callable from int to int and we return, well, in this case, an iterable of ints. That's not actually what map we're talking about. Generators and iterators. Generators have three yield types, end type, return type, or in the case of an iterator, it's a more specialized generator. We have only the yield type, just the thing that we're yielding, which is quite often only what we're using if we have something like this where we're saying, i for i in range of 100, or only just range of 100, actually. Then that's an iterator of int, or if we're doing some sort of request and getting a stream response on that, then each of our chunks, our chunks come back in an iterator. Classes, if we have something that we've defined ourselves we want to use, parsing into our functions, as, of course, we do. Otherwise, why are we using it? Then it's only natural that we want to use that as a type hint as well. That is just as simple as being the class name. We just annotate. If we've defined our class foo, then we use that as the type hint. Type. All these things, by the way, that I'm mentioning, in typing in the standard library, so this is from typing import type, is then says the type of that. So that's saying not the... We're annotating that it's not an instance of foo, but it's foo itself. It's the class... Yeah. Hopefully that makes more sense than I'm speaking. So similarly, if we've defined our own meta classes, then I think this is where it makes more sense because we might say we want something that's the type of bar, and so then we know that we're getting bars, i.e. instances of bar meta, rather than instances of bar, which we would get if we just used bar. Now, this, I think, is worth understanding because we've seen a few times is people thinking type is just... Well, that's the thing I want, right? I want the type bar, so I write type. No, actually, you know, if you're saying you want to be getting bars, i.e. instances of bars into your function, then you just use bar. Type bar is not instance to class itself. So if we've seen that we can use classes as types, then we might start to think, actually, should we be using dicks, all these massive dicks that we're passing on everywhere? Maybe we should... Do we need to define classes for those as well? You know, until now we've made do with dicks, but actually there are some nice helpers for these. Name tuple and typed dicks in typing and typed dicks from mypy extensions, which allow us to write these classes and just say what things are in them, from tuple analogously to lowercase name tuple from collections and typed dicks, just allowing us to say what the keys are that come inside this dict and what the types are of those constituent elements. So when we start to nest these together more deeply, it can get a bit hard to read. We saw people's initials, which is an iterable of a sequence of strings. If we start to do something that has people's initials inside, it's going to get really long, really quickly. So we might want to alias initials to be sequence of string, and then we can just say that people's initials is an iterable of initials. That's much nicer. We can also use new type, which allows us to say this is not just an alias. This is something we actually want to be constructing to allow us to distinguish more safely between things. So we work with a lot of different ideas and different things. When people have something similar, very similar, but are actually different, and you don't want to confuse them. If you have a function, say, that's taking an account ID as well as a device ID, I want to be damn sure I get those the right way to round otherwise I'm going to have bugs that are pretty hard to trace down why this thing didn't exist, and it's actually because I confused them up. Whereas if we use new types to that, then we're making sure that we constructed a device ID or an account ID, and so we get the right one in the right place. I'm going to really quickly go to this because I'm going to run out of time otherwise. So type variables differ from unions in allowing us to say that the thing we get is not just that it can be any of these things, but the one that we get as the argument to the function is the one that we're going to refer to somewhere else. So we could use it going back to our map function, for example, say we want to make that more generic, like the real map, then the callable thing, it's callable from our type variable to type variable, the thing we return is going to then be an iterator of that same type, whereas if we use unions in those two places, then we're saying that the callable we take is from any of those things to any of those things, and the return type is also to any of those things, so we might be returning an iterator of strings even though your function was mapping from ints to ints, actually. So there's also some pretty neat methods like supports ints, supports bytes, for example, that allow us to say that dunder init is something that's available, or hashable, for example, says that from collections.hashable, I should say, says that dunder hash is something that's available. And where duck typing really comes into it, and you start to see how this is pythonic again, is because we can say that the type of something is this protocol, this is in 3.7, I should say, that something that looks like this is what we expect, what we want to use. So note that concrete class is not deriving template, it's not an instance of template, it's just something that has certain attributes or signatures or there's something about it, it's in some way template-like. Template is the definition of things that smell like ducks and concrete is something that smells sufficiently much like a duck for our purposes. And just a few things to finish up that are helpful when you're getting started. Forward references, if you're defining less than on a class, then the other thing you want is something of the same type as the class you're defining on, but the class name isn't fully defined yet, so we just put that in quotes to be able to refer to it ahead of time. Cast, when you need to, is instance checks can not be fully understood sometimes, so even though you know it's correct, it might by might not fully understand that. Any, you use it sparingly because it kind of defeats the point. Type checking is true while we're type checking and then false at runtime, so this can be useful for some imports. If we have something that's quite expensive to import and we're doing it because we want to type annotate a class from somewhere else, then that can be helpful to hide that behind a guard there that says only to run if we're type checking. And there's an awful lot more that I haven't covered. I trust you can read the typing docs. If this excites you, there are a bunch of peps. A book I really recommend for the theory, if you're interested in the theory or if you're interested in a language that takes typing more to heart and things with it, check out Rust. I'm trolling slightly. Thank you very much.