 Hello everybody and welcome back to some more Python programming tutorials. I'm Rudolf Null and today we're continuing to look at the TextWrap module in Python. Remember this module is built in so you can use it kind of no matter where you are, whether or not you're on Windows or Linux. Now in this video we're going to take a look at the TextWrapper class and object. In the last video we took a look at these two functions, Wrap and Fill, within the module. And what these did was that they created actually an object of this TextWrapper class, called a function that was part of the object in the class and then managed to delete the object. So the TextWrap module documentation kind of gives us a warning that these functions that we've used before use an object and then remove it, the exact situation that I just explained to you. The instance is not reused. So for applications that do this many times, whether they wrap or fill text a bunch, it's more efficient to just create a single TextWrapper object and use it as you need to. So let's do this in our code. What I'll do is I'll create a TextWrapper object. That's just a variable name that I'll give it. And I'll say that's TextWrap.TextWrapper. Cool. Now what we can do is we can have the same functionality that we did beforehand. Let's say TextWrapper object rather than using the TextArea module itself. Now it's going to be calling from inside the object. We'll do the same thing with our TextWrapper fill. And we should have the same display as what we had last time. Let's check it out. Wrap takes exactly two arguments and three are given. Now that's because of the self keyword. And it does not need a width argument because it's actually part of the object's attributes. So let's remove width here. Now if I run this, you can see that that's working just fine. Now when I said that width is part of the TextWrapper object's attributes, that's because TextWrapper, the object that you create, is special in that it can contain more flags and more arguments that are kind of inherent to the object. We could of course have passed those into the earlier functions when we called just within the module TextWrap.Wrap. Remember it's set up here. Optional keyword arguments correspond to the instance attributes of TextWrapper. So we could pass it exactly to the solitary functions, but we can also give them to the object. So let's take a look at some of these attributes right now. Of course, we've got width. So we can say width is equal to 70. Or width is equal to width. I would think that would work. Yep. And now let's change that right here to 100 or 20 or any variation. Keep in mind that when we're calling it, we're calling these attributes or giving this object these properties and attributes within the constructor. And we're using keyword arguments to actually give them a value. So me saying width equals width is probably a little confusing. I'll just change the variable name to width variable. And width should equal the width variable that we have. Because width is the keyword for the TextWrapper object that represents the width. You can see right down here as instance attributes and keyword arguments to the constructor, that's how you actually set them. Width over here is the name of it. If we run this, it has the exact same functionality. Width is going to equal the width variable that we have that determines this. Sweet. Okay. Now let's take a look at something else because I think width is pretty simple. In our documentation, what have we got next? ExpandTabs. By default, this is true. And it says if true, all the tab characters and text will be expanded to spaces using the ExpandTabs method of text. Okay, and text is a string, remember. So that means that it's calling the ExpandTabs function on the string to begin with and has a similar functionality with all the tab characters in the text we're passing in. Let's take a look at how it works. If I add some tab variables, or at least tab characters, sorry, in our string, and I display it, what do we get? All these, you can see in our output, these dots, that's the blind text way of telling me that these are spaces. They're not actually tab characters. They've been converted to spaces. Now if I set this to equal false, ExpandTabs equals false. Let's see what functionality we have now. Oh, now it's only three spaces rather than expanding the tabs. It doesn't make them to become tab characters or at least look as if they were tab characters. They're only replaced with spaces. Now the next one over here, replace white space, that's true. This is another attribute that detects our object. By default, it's true. And if it is true, after tab expansion, but before wrapping, the wrap method will replace each white space character with a single space. Oh, and we can see that's exactly what it's doing with these tab characters. They're being replaced with a single space. And by default, it's true, so this is what it's doing. The white space characters are replaced as follows. Tab, new line, vertical, form feed, and character turn, just like this. It gives you this display. And there are notes here that says, if ExpandTabs is false and replace white space as true, each tab character will be replaced by a single space. And that's the same functionality in actual... That's what we're seeing occur right now. It's not the same as tab expansion. So let's try saying that replace white space is false. Even with our ExpandTabs being false. Place white space equals false. If I run this, now you can see their tabs. And notice how SublimeText shows them as this really long line, rather than a single dot like they were earlier. Because now they're being used as tabs rather than three spaces. Three spaces rather than three tabs. If ExpandTabs was true, like the default behavior, keep in mind, it's going to be converted to spaces. And I think you can see the distance actually is a little different. The way that some systems will encourage or treat the length of a tab character. So that's some interesting functionality. But I mean, you can play along with it as you would like. You can see the drop of white space is a default functionality that is true. At least this occurs by default. Now, white space at the beginning and end of every line, but wrapping after wrapping before indenting is dropped. They just completely remove it. White space at the beginning of a paragraph, however, is not dropped if non-white space follows it. Oh, okay. So these tabs up here were not being removed because, of course, there's text right up here. That works. That makes sense. If white space that's being dropped takes up an entire line, the entire line is dropped. Oh, all right. So what if I had a whole ton of tab characters? Stuff that even exceeded the 70 characters that we have here in the width. I'll just copy and paste this a bunch of times. And even for pottywares and giggles, let's say width is going to equal 40. So we narrow it down a little bit more. I'll supply that width and I'll even say that drop white space is true. Well, actually, we know that that's the default behavior, so we should be good. If I run this, hey, you can see that at the very top of our output here, all this white space, all these tab characters were completely removed from our string. And the same thing happens down here when we're actually using the fill function. The other ones, the remaining ones that actually do land on a line that has the text following remain here. If I remove a few of these tab characters, you can see how it works. Now if I run this, you can see it moves back. If I keep removing more of them, moving back, oh, a little too far. Hey, now we got to the very start of the line. Cool, look at that. I'll remove all this junk now that I've made. And we can, of course, say that, actually, before I remove it, let's say that drop white space actually equals false. And you can see, wow, now we got a boatload of white space at the start of our string for both the wrap and the fill function. That's massive. All right, now I'll remove all this stuff and kill. I'll comment at width. So we'll just leave it back to the default 70 and I'll take out all these optional arguments. Okay, let's move on. We got initial indent. Huh, by default this is nothing because it's a string. Okay, the string will be propended to the first line of the wrapped output. Counts towards the length of the first line. The empty string is not indented. Well, let's see what we can play with this. It looks like we can use an initial indent to actually be a tab character, like an indentation. If we run this, hey, we got our tab right there. And notice that it's not converted to spaces. Uh, does it say? Okay, it doesn't say that it's not indented. It's not really treated as something that's going to be converted to expand tabs or replace white spaces with spaces. And notice that it kind of gives us a like text paragraph look as if we were writing something in a real like book or a novel or anything like that. It looks like this is a paragraph because it's indented right at the start. And that's kind of cool. Look at the other functionality of subsequent indent. This happens to all the lines after or accept the first line. It counts towards the length of every line except the first. Let's take a look at that. It's almost like a MLA citation if you guys know those. Hanging citations where you've got the first line is not indented, but everything else is. And of course, it doesn't have to be a tab character. We can make it like an ellipsis or absolutely anything like, hello, and you can see it over and over and over again. Anything we put in that string will be added to every line except the first. And we'd have that same functionality with the initial indent, but we just didn't play with it like that. This next one is a little interesting. This is fixed sentence endings. By default, this is false. But if you set it to true, what the module will attempt to do is it'll try to find all the sentence endings and make sure that the sentences are always separated by exactly two spaces. Kind of like, I think, like proper English or something. I know a lot of people really enforce that to make sure you have two spaces after the end of a sentence. I'm a bad person and never really do it, but hey, it's generally desired for text in a monospace font, but the detection algorithm is not perfect. It assumes that the sentence will end in anything with a lowercase letter followed by like a period or an exclamation mark or a question mark or even like a quotation or another ending quotation and followed by a space. But what if there's a problem with that? A problem with the algorithm is unable to define the difference between doctor and abbreviation like Mrs. or Mam where you use a period to end the abbreviation but you continue on with your sentence. It's gonna think that's the sort of a new sentence and spot. And like C spot run, C spot, C spot run. It shows these examples here. Notice the sentence detection algorithm relies on the string.lowercase and if you've played with the string module, you know that's just a list of all these lowercase letters for the definition of lowercase letters because if it's in that array then it must be a lowercase letter based on Python. The convention of using two spaces after a period to separate the sentences is specific to English language, texts and words and stuff. All right. Let's just see what happens if we turn it to true. How about that? It's fixed sentence endings. True. Now if I run this, hey, check it out. In this case, it works just fine for it. The algorithm is okay because I don't have any situations where I'm using an abbreviation or a situation where it might goof up. But now after a sentence, there's always gonna be two spaces that have been one. That works kind of nice for us. I mean, if that's what we want, sure, we can use that. But as long as you know the algorithm is not perfect and it might goof up depending on your string and your text. Okay. Let's move on to the next two. Break long words and break on hyphens. By default, break long words is true and this means that words that are longer and width will be broken to ensure that no lines are longer than the width and that's kind of the functionality we've of course been seeing all this time. If it's false, long words are not gonna be broken and some of the lines may be longer than width. They'll be put in a new line by themselves in order to minimize the amount by which width is exceeded. So, I remember earlier when we were playing with length at the very front of our loop, you can see how long the line was, length of line along with the line itself. If I say break long words is... Is that... By default, it's true. If we set it to false, sometimes we'll have things that are longer than the default 70 width. Oh, sorry. My syntax goofed up there. Print the length of the line and then the line. Check it out. 67, 69, 66. Okay, I guess we're not really gonna see the functionality working with this string. Super... Cal... I don't even know how to spell this word. Fragilistic... XPL... Doshes. Let's pretend that's a word. Okay, check it out. This is a really long... I'm trying to break it. 66... Uh... I don't know. I can't tell if this is the functionality that we want to be seeing. Let's not break the line yet. Okay, yeah. See, now this word is 82. It's even longer than 70 by the default. The default width argument. And that's because this word is so long that it even... It seeds the width argument by default. So, it's placed on its own line and separated from all the others. But it's gonna be longer than 70 and this one is gonna cut until it gets the word and the next line is gonna begin completely away from that... That really long word. So... By default, this is false... By default, this is true, sorry. So, this functionality, you probably won't see a whole lot and you'll never really have a word that looks like this. So... All right. The next one, break-on hyphens. If true wrapping will occur preferably on white space and right after hyphens and compound words, as is customary in English. If false, only white space will be considered as potentially good places for line breaks. But you need to set break-long words to false if you want truly insecable words. Default behavior in previous versions was to always allow breaking hyphens to words but this time you kind of have the option to change that. Um... Um... You can have really long words like... Spider-Man. Who the hyphen? I don't know. I'll be happy to make Spider-Man again. Um... But... And now you can see this is really long string. I'm happy to be making Spider-Man again. And now it's totally okay to cut because of that hyphen there. If it were any longer, if it were like regular Spiderman, it'd say, oh, that's gonna get us to 70. So, let's say Spiderman. Okay, cool. Now it's gonna break to this other line. But if I were to say Spiderman with the hyphen there, it's okay to break at that hyphen rather than the end of the word. Because that's true by default. If we set it to false, break on hyphens, it's false. Now it's gonna enforce that this word Spiderman is on a new line even though it has a hyphen on there. Okay. I mean, that's the way that works. And that's about all of them. I know this made for a super long video but I want to just run through all of these attributes and kind of properties that the text wrapper object can have in the text wrap module. Of course, it still has these same two functions. But really, they do the same thing that the convenience function do. But if you ever have a situation where you need to wrap or fill multiple strings, you should create the text wrapper object and just do it over and over again with this text wrapper object rather than calling the convenience functions. Because what that does is it creates an object, removes it, creates another object, removes it, and it does this again and again and again. Or if we only have one object doing all the work, we're much more efficient. So, okay. Thank you for tolerating this video. I know it was super duper long but there's a good run through and crash course for all the different options and parameters we can give to this object. Thank you guys for watching. Hope you enjoyed the video. And maybe if you're willing, like the video. Sorry, like the video. Maybe comment. Maybe subscribe. It's all up to you. But I'll see you in the next tutorial. Thanks guys.