E
This is a typical case where it is cool to use the functionality of Python "generators" - generators are characterized by responding to the "iterator protocol" - and as such, objects that can be used in a command for Python.One of the ways to create generators is to write functions that have the keyword yield in the body. The expression used in yield is passed as value for an interaction of for. In this case, you can create a generator that receives the large list as a parameter, and generates as slice results of 100 elements:def slice(biglist, slice_size=100):
for i in range(0, len(biglist), slice_size):
yield biglist[i: i + slice_size]
and to use this:for list_slice in slice(biglist):
# código para usar a sublista com 100 elementos
...
What are the generators:The idea is: a normal function is always called, receives parameters, creates its local variables, executes its code (which may or may not call other functions), and returns a value - when it finds the command return (Functions without one return in Python return None implicitly).At the time of one return, all local variables created by the function are destroyed. Including the parameters you received. If it is called again, the execution returns to the first line of the function, and it has to redo all the calculations it has already done again. In terms of programming, it is said that a function has not "state" - that is, every time it is called with the same parameters, it will redo everything, and return the same results (of course, if it itself does not hold other calls that return variable factors such as file readings, acquisition of internet data, or data typed by the user (with input)).When we talk about object-oriented programming, one of the major changes that happen is that objects can have attributes - that is, between a call from one method and another of the same object, their state can change. So, in this case, for example - we want to have something that "remember the big, complete list", and that "remember" of which was the last "fatia" of the list that was used, and that can generate the next slice, and signal when the big list ends.In orientation to the normal object, without the improvements of the "generators" that Python incorporates, this could be written as follows:Sentinela = None
class Fatia:
def init(self, listagrande, tam_fatia=100):
self.listagrande = listagrande
self.tam_fatia = tam_fatia
self.indice = 0
def proximo(self):
if self.indice >= len(self.listagrande):
return Sentinela
resultado = lista_grande[self.indice: self.indice + self.tam_fatia]
self.indice += self.tam_fatia
return resultado
And this class could be used to slice the list, similar to what I did in the first example, but more "manual":fatiador = Fatia(listagrande)
while True:
pedaco = fatiador.proximo()
if pedaco is Sentinela:
# sai do while
break
# Codigo para usar a essa sublista
....
Now the for in Python, being a command that already knows how to go through sequences or iterators, knows how to "use" objects similar to that defined in that class. Every time we do one for numero in sequencia: in Python, he takes the object in the expression after the keyword in (in this case, the variable "sequence"), and calls the method iter of this object. (If the object does not have a method iter the command for has a plan B" using the length of the object and numerical indices, but does not come to the case now).The value returned by this method iter, he himself has to be an object that has the method next. If you have, then the command for will call this method next once for each iteration - and the value returned by the method is used in the variable of for. When next no more objects to return, it raises a type exception StopIteration. Unlike the exceptions we are most used to seeing, such as ValueError and TypeError, the StopIteration does not indicate an error - and yes, it is a sign just used by for to know that that iterator has no more results. The command for then ends, and the execution continues on the first line after the for.So, for example, we can adapt the above class to work with for, only one method iter and rename the method proximo:class Fatia:
def init(self, listagrande, tam_fatia=100):
self.listagrande = listagrande
self.tam_fatia = tam_fatia
self.indice = 0
def __iter__(self):e
# Como essa propria classe contem
# um metodo __next__ apropriado,
# basta retornar o proprio objeto
return self
def __next__(self):
if self.indice >= len(self.listagrande):
raise StopIteration()
resultado = lista_grande[self.indice: self.indice + self.tam_fatia]
self.indice += self.tam_fatia
return resultado
There, with this simple change - before a sentinel variable was returned, whose value was compared in one if that had to be written manually, and now it causes the StopIteration exception. Objects of this class can be used as in the first example of the answer:for list_slice in Fatia(biglist):
# código para usar a sublista com 100 elementos
...
So the language maintainers, noting that this was a standard of common use, created the key word yield. Whenever it appears in the body of a function, the function ceases to be a common function, and becomes what we call "manager": internally, it functions as the above class, and Python - when we call a function of this, the language already performs the equivalent of the iter, but does not execute a line within the function, and yes, returns an object that has the method next. When we call it next (or better, the command for do this call internally). there yes, the function body code is executed, to the point where it finds the first yield. At that point, the value given to yield is returned as the result of the call to next. When next is called the next time, the functionc continues to be executed on the line following the yield; all local variables are "remembered" - yield works as a "pause" in function. This makes the internal code much more efficient than a "real" function call as the one that happens in the examples with classes, in which local variables are created, etc...So, revisiting our generator to better understand:def slice(biglist, slice_size=100):
for i in range(0, len(biglist), slice_size):
yield biglist[i: i + slice_size]
when Python finds for fatia in slice(minhalista): magic happens - Python first creates a "generator" object with past parameters, and then calls the next inside - this runs the line with the for inside the function slice - the variable "i" is initialized, etc... in the next line, the slice of the list of "i" up to "i + 100" is returned, and is used "out of function", in the body of for external. Local function variables function as if they were attributes of a class, preserving the "inner state" of the generator. (with the notable difference that, unlike object attributes in Python, these internal variables are not public, and can even be read, but cannot be written by code outside the generating function).You can use the generator "manually" without using the for as follows:fatiador = slice(biglist)
while True:
try:
fatia = next(fatiador)
except StopIteration:
break
# código para lidar com a fatia aqui
...