There are usually multiple solutions to problems like these. If your solution is different than the ones I gave here then it is not necessarily wrong. For example, it would have been possible to use a try/except block in the next method as well. If you have different solutions that work then I would love to see them. Also, quick correction: At 3:05 I said "class attribute" when really I meant "instance attribute".
@@lacocodelfitness1 The simplest solution I could come up with was only 5 lines. class Sentence(): def __init__(self, sentence): self.words = sentence.split(" ") def __iter__(self): return self.words.__iter__()
@@andrewl.8626 could help me understand what return self.words.__iter__() means ? I understand that next() return the item and move to the next time. What about iter() ? What exactly does it return that can move through the whole iterable ? Thanks very very much.....
Corey, I am just amazed at how you deliver a complex concept so easily - I actually meant the word "deliver". You actually do that so good brother. Lucky to have an instructor like you on RU-vid. Thank you so much for your great stuff.
Dude you MUST keep this up! We seriously need examples on how to use all of the functions and statements etc, we've learned into solving common problems. So thank you for this🙏.
Thanks so much for effort you are the best. This is my solution for generator def sentence(sentence): words = sentence.split() index = 0 while index < len(words): yield words[index] index += 1
I just want to save I love your calm voice and easy-to-understand explanation, no 'uhh' or 'err' in your explanation that can interrupt my focus while listening to your explanation
Hi Corey, Please keep posting coding problems. These are great for practicing and solidifying one's understanding of the concepts you teach. Is there a reason you store both the original string and the list in the class object? Wouldn't the memory requirements be more? It seems to me that storing only a list would be sufficient (at least my solution works with just a list).
A video tutorial series on 'Data structures and Algorithms' would be great. I know this might take up a lot of time, but almost every interview has questions from DSA. Your tutorial series on Django helped me a lot at my current project. Thanks.
Exercises like this one help me learn quicker. Your comment about using generator more frequently also provides a valuable reference as I learn Python. I'd like to see more coding exercises please.
hey, I've written another class code that works: # Class code class Sentence: def __init__(self, sentence): self.sentence = str(sentence) self.words = self.sentence.split() def output(self): for words in self.words: print(words) mysentence = Sentence("This is a test") mysentence.output() # Generator code def Sentence(a): a = str(a) words = a.split() for word in words: print(word) Sentence("This is a test") """ Output: This is a test """ this seems way simpler than the one that you have explained in the video
Genuine Query: Wouldn't using sentence.split() defeat the whole purpose of using a generator in the first place? Per my understanding, generators are used because of two main reasons. First, they do not require additional storage. By using the split() method, I would argue that an array is being created that occupies O(n) space where n is the no. of words. Second, generators are better because they eagerly return/yield results when the actual computation is lengthy. By using split, we are already traversing the entire sentence. Please clarify how this implementation that uses generators, is helpful. Corey, if you are reading this, I can't thank you enough for your videos! They have helped me immensely and I often frequent them when I need to jog my memory on a particular topic. Thanks!
I found a solution that is much simpler and 50% faster: class Sentence: def __init__(self, sentence): self.sentence = sentence def __iter__(self): return (self.sentence.split()).__iter__() You make the best tutorials, thx for that. The first time you gave a "Realworld" example, I hit subscribe. Most other tutorials are stupid and all the same. I am a noob myself, and just started, but after your glorious previous video I came up with this solution by myself, glad I did not peak. An iterator already has an built-in __next__ function "Corey Schafer", so there is no need to overwrite the __next__ function. Also getting rid of 2 variables. But keep in mind my 50% more speed is only when you put the initialization included in the test..like: Anyway big thx for explaining iterators and iterables that in-deepth. from time import perf_counter start1 = perf_counter() for _ in range(100000): s1 = Sentence("This is a test") for word in s1: pass print(perf_counter()-start1)
Hi Corey, I thoroughly enjoyed this coding problem. I found it to be challenging but not overwhelming and the content in the previous tutorial video was just helpful enough. You're the best teacher I've ever had in any subject. Thank you much!
Hi Corey, using the split function will waste all the advantages of using a generator. I think the best way to do it, is just keeping in memory the current word and the index over the original sentence This is my solution: def gen_sentence(s): i, curr = -1, "" while i+1
I definitely enjoyed this format! Splitting theory and practice into two videos makes it very easy and convenient to revisit the desired part. Even though I like finding a solution on my own to coding challenges, I really like to compare it to what an experienced programmer would do to find out where improvements could be made. Also, on more complex problems, it really helps to see if the structure of the program is correct and if something is missing (exception, test case, etc.). All in all, I love all your videos and this one makes no exception, except on StopIteration ;).
Thank you for your video-tutorials Carey. They are extremely useful (as well as awesome). As for this one I think that implementation has a ideological shortcoming. I mean using both in your class and in your generator the split() function. For example: the main reason to create and use generator - to be better in terms of both memory use and performance in larger programs. But your code produces all the list (of words) at once, the list your are going to loop over one-by-one. If I am wrong persuade me please.
Clear, clean, and concise. Your videos are an inspiration to my own channel on Python. I would really love to collaborate on a RU-vid tutorial project with you should you be receptive to that idea. Cheers, and thanks again for the video.
Just looked at your channel and it looks like you have some great content! And I'd like to do some collaborations with some channels in the future, but I'm a bit too busy at the moment. I'll be sure to write you down as someone to contact if the opportunity opens up though. Keep up the good work and good luck with the channel.
Wow Corey, thank you very much. Coming from you, that's a really sincere and excellent comment and definitely boosted my mood! I entirely understand that you're a bit too swamped at the moment to take on new projects, but even if there's something that I could help out with on your channel, like producing some content, I would be happy to discuss that further. In any case, thank you so much for your words of encouragement. Please do keep me in mind, it would be an honor to work with you!
i did a seminar on python programming just by learning from your videos. Thanks a lot corey. I sincerely request you to make videos on GUI with python please
For everyone worried str.split using memory, here is a iterative splitter (which may be improved by the := operator, and possibly some operator.methodcaller on 'pop' to empty word into "".join and reset it to []): def isplit(sentence): sentence = collections.deque(sentence) word = [] while sentence: letter = sentence.popleft() if sentence == ' ': yield "".join(word) word = [] else: word.append(letter) yield "".join(word)
Hi, Thanks for your great tuts. I've got 2 questions: 1- In the last video you said we use this iterator/iterable stuff in order to not keeping our data in a list to prevent the memory overload, but you are still keeping the sentence in a list. what's the part I got wrong? 2- When we use the next() method, we move the state to the next position, so how can we reset that and get back to the first position?
split function uses memoery to story a list of words in an array and also take up some time. The main moto rather purpose of uisng iterator or generator is that 'retrieveing the data without using space at runtime'. But using spilt we are completely opposing the purpose. Correct me if i am wrong. I am a Learner too.
Excellent tutorial and challenge. I just found that your solution to the problem wasn't going to use split() as it would create a list of all words again in memory. Wouldn't this solution be space inefficient in terms of what you just explained in the tutorial?
if I run below code , print statement executes only once. Looks like iterator not resetting. But we are able to for loop regular python lists multiple times. How it is possible?? for word in my_sentence: print(word) for word in my_sentence: print(word)
Hi Corey, My __next__ function is little different class Sentence: def __init__(self, input_string): self.input_string = input_string self.words = input_string.split() self.count = 0 def __iter__(self): return self def __next__(self): if self.count >= len(self.words): raise StopIteration value = self.words[self.count] self.count += 1 return value
Thanks Corey, great videos! Here's another solution with a generator expression. ge_sentence = (w for w in a_sentence.split()) [print(w) for w in ge_sentence]
tbh id say explain under the hood principles behind some commonly used iterator "recipes" like zip(*[iter(string)] * n) is way more useful than just rewriting iter() function in several ways
Problem with treating Sentence as iterator (returning self from __iter__) is, state is not reset. We cannot run the for loop again over the same object of sentence. Ideally __iter__ should return a new iterator with fresh state. generator do takes care of that (we can also implement __iter__ of Sentence class as generator, if we still need class).
at 08:04, I have copied all the code but when I Run I get the error: 'Sentence' object in my_sentence is not iterable. How are you able to run yours without getting the same error? Thanks
Hi Corey, thanks for the content. All your videos are very useful and your approach to explaining the topics is very clear. I have a suggestion, would it be feasible to do some video sessions on python for deep learning?, since it is a hot topic.
Thank you Corey. Coding problems are very help to cement the concept. Also, could you please make tutorial on 'Python object memory optimisation' techniques. Thanks in advance.
Hi Corey, Why do you include a instance variable self.sentence? It works fine directly doing self.words = sentence.split(), but I'm wondering if it would come up handy if you were to give other functionalities to the class. Thanks!!
It's simply there to just have the original sentence available if it's needed for additional functionality as you said. We don't need to include it in this example.
@@coreyms thank you for your quick response, and THANK YOU specially for your great tutorials. I started my programming and Python journey less than a year ago and I am grateful for your videos, which I find the clearest of all. I encourage you to continue with this coding problem series.
I was just playing with the code you provided. I found that I could reset the self.index = 0 before raise StopIteration. This makes me resue my_sentence in multiple times loops. Otherwise, It will only work once.
Corey, awesome tutorials! I wonder if you could create a series for gui frameworks with python? I'm not talking about tkinter which is well enough documented and fairly easy to understand but just way to narrow in its scope. I have tried learning wxpython or pyqt but I just can't wrap my mind around how these really work and the documentation especially for wxpython is horrendous.
def sentence(string): i_string = iter(string.split(' ')) while True: try: yield next(i_string) except StopIteration: break my_sentence = sentence('This is a test an I passed it') for word in my_sentence: print(word)
Hi, great video! However I have a question, what is a point of using iterator/generator if you create the whole list in your memory anyway? I thought that iterators are a way to get round this and not to hold everything in a memory. If we had a sentance with billion words, the program would crash when executing self.words = self.sentence.split(), wouldn't it?
since self.index was already defined in the __init__ method, isn't it unnecessary to define it again in the __next__ method. overall, great video, much love. keep up the great content.
Hi Corey: Nice to have this practice in the subject - really helpful and interesting. Don't know if you can see this comment or not, but I have a question: for the generator, I tried to use the keyword 'return' rather than 'yield' and it returned the first element of the splitted list of words (sentence) 'letter_by_letter'; can you please explain the difference between 'return' and 'yield'?
My solution (I didn't saw the solution so far): class Sentence: def __init__(self, sentence: str): self.words = sentence.split(' ') def __iter__(self): self.index = 0 return self def __next__(self): if self.index >= len(self.words): raise StopIteration self.index += 1 return self.words[self.index-1] for word in Sentence('Testing this out'): print(word)
Works fine with a for loop but have you tried saving your sentence in a variable and call next()? Doesn't work: "AttributeError: 'Sentence' object has no attribute 'index'". => self.index = 0 should be in __init__ not __iter__.
I don't know if you will see this but if you do please please if you have the time make a video for hosting a flask application. I'm not able to understand any of the other tutorials and if you can make another video to the flask blog one showing how to deploy that on the internet free that would be great.
def words_generator(sentence): # My initial pointer is pointing the start of sentence. idx = 0 # I use the while loop to generate words, # since I as a generator don't know how many words are out there. # # I don't want to use len(sentence.split()), # since len(sentence.split()) will produce a list and spend lots of memory. # This is againt the generator idea that saves lots of memory. while True: # I first check whether I stop generating. if idx >= len(sentence): break # If I decide to generate, here is the generating step: start = idx # start is the pointer of start of word end = idx # end is the pointer of end of word while True: try: # Usual Case Handling: if sentence[end] != ' ': end += 1 # move the end point to right if current character is not ' ' else: end += 1 # move the end point to right if current character is ' ' (final move) idx = end break except IndexError: # When I tried to identify the last word, # sentence[end] raises the IndexError. # So this is how I handle the last word case: end += 1 idx = end break yield sentence[start:end] #my_sentence = words_generator('This is a test') # www.nytimes.com/2020/09/21/technology/microsoft-zenimax-video-game-deal.html my_sentence = words_generator('Microsoft landed a major blow in its decades-long battle with its video game industry rival Sony on Monday, announcing the $7.5 billion acquisition of a video game company that narrowed the gap between the two tech giants’ offerings.') for word in my_sentence: print(word)
thanks for those vidoes! question about your class solution. isnt it LBYL coding style? class Sentense: def __init__(self, txt) -> None: self.words = txt.split() self.index = 0 def __iter__(self): return self def __next__(self): try: word = self.words[self.index] except IndexError as exc: raise StopIteration from exc else: self.index += 1 return word
Is this a valid option (although a bit less readable...)? def sentence(sentence): gen = (word for word in sentence.split()) return gen Or without a function, a simple generator that we can loop over or print(next(gen)) gen = (word for word in sentence.split())