Je cherche un moyen de "page à travers" un itérateur Python. C'est-à-dire que je voudrais envelopper un itérateur donné ITER B> et Page_SIZE B> avec un autre itérateur qui permettrait de renvoyer les éléments de ITER comme une série de "pages". Chaque page serait elle-même un itérateur avec page_size b> itérations.
J'ai regardé par iTertools et la chose la plus proche que j'ai vue est ithertools.lice . À certains égards, ce que j'aimerais, c'est le contraire de iTertools.chain A> - au lieu de enchaîner une série d'itérateurs ensemble dans un itérateur, je voudrais briser un itérateur dans une série d'itérateurs plus petits. Je m'attendais à trouver une fonction de pagination dans itertools mais je ne pouvais pas localiser un. P> J'ai proposé la classe de pager et la démonstration suivantes. P> class pager(object):
"""
takes the iterable iter and page_size to create an iterator that "pages through" iter. That is, pager returns a series of page iterators,
each returning up to page_size items from iter.
"""
def __init__(self,iter, page_size):
self.iter = iter
self.page_size = page_size
def __iter__(self):
return self
def next(self):
# if self.iter has not been exhausted, return the next slice
# I'm using a technique from
# https://stackoverflow.com/questions/1264319/need-to-add-an-element-at-the-start-of-an-iterator-in-python
# to check for iterator completion by cloning self.iter into 3 copies:
# 1) self.iter gets advanced to the next page
# 2) peek is used to check on whether self.iter is done
# 3) iter_for_return is to create an independent page of the iterator to be used by caller of pager
self.iter, peek, iter_for_return = itertools.tee(self.iter, 3)
try:
next_v = next(peek)
except StopIteration: # catch the exception and then raise it
raise StopIteration
else:
# consume the page from the iterator so that the next page is up in the next iteration
# is there a better way to do this?
#
for i in itertools.islice(self.iter,self.page_size): pass
return itertools.islice(iter_for_return,self.page_size)
iterator_size = 10
page_size = 3
my_pager = pager(xrange(iterator_size),page_size)
# skip a page, then print out rest, and then show the first page
page1 = my_pager.next()
for page in my_pager:
for i in page:
print i
print "----"
print "skipped first page: " , list(page1)
6 Réponses :
regarder grouper () code>, à partir du
iTERTOOLS code> recettes
. from itertools import zip_longest
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
Merci d'avoir souligné les recettes. Je peux voir utiliser le mérou car c'est efficace et adapter la recette pour se comporter exactement comme mon pager. Je suis toujours curieux de savoir si le téléavertisseur qu'il sent a beaucoup de mérite - ou je devrais l'abandonner pour une approche en forme de mérou.
Basé sur le pointeur de la recette Itertools pour le mérou (), j'ai proposé l'adaptation suivante du mérou () à imiter le pager. Je voulais filtrer tous les résultats et vouloir retourner un itérateur plutôt qu'un tuple (bien que je soupçonne qu'il pourrait y avoir peu d'avantages pour faire cette conversion)
# based on http://docs.python.org/library/itertools.html#recipes def grouper2(n, iterable, fillvalue=None): args = [iter(iterable)] * n for item in izip_longest(fillvalue=fillvalue, *args): yield iter(filter(None,item))
Je le ferais comme ceci: de cette façon, Aucun code> peut être une valeur légitime que l'itérateur crache. seul em> l'objet unique
FLIDVALUE CODE> filtré, et il ne peut pas éventuellement être un élément du iérêteur. p> p>
Merci, Matt. Vous avez fait de me faire comprendre que je n'étais pas à la fois de ne pas laisser aucune valeur légitime de l'itérateur et que je ne comptais pas la FillValue.
Pourquoi n'utilisez-vous pas cela?
def grouper( page_size, iterable ): page= [] for item in iterable: page.append( item ) if len(page) == page_size: yield page page= [] yield page
Merci d'avoir répondu à ma question et de donner un bon moyen de réfléchir à la façon de faire de la boucle à travers l'itérateur. Je pense qu'il y a une petite erreur - avez-vous voulu ajouter l'élément à la page - comme dans: def grouper (page_size, itérêtant): page = [] pour l'élément en iTable: si len (page) == page_size : page page page = [] Sinon: page.append (item) page de rendement
@Raymondyee: En fait, il y a une meilleure façon. Votre version abrite un grand. Essayez de voir que cela saute un article.
@ S.Lott - Oui, bien sûr, je mets ma page.append (article) au mauvais endroit. Merci pour la correction. J'apprends toujours quand itTools peut aider et quand il n'y a pas besoin de le besoin. Des lignes directrices à offrir?
@RAYMONDYEE: Aucun conseil. Je n'utilise pas iterools tout cela souvent. Les fonctions de générateur sont très simples.
def group_by(iterable, size): """Group an iterable into lists that don't exceed the size given. >>> group_by([1,2,3,4,5], 2) [[1, 2], [3, 4], [5]] """ sublist = [] for index, item in enumerate(iterable): if index > 0 and index % size == 0: yield sublist sublist = [] sublist.append(item) if sublist: yield sublist
plus_itétools.chunked fera exactement ce que Vous recherchez:
>>> import more_itertools >>> list(chunked([1, 2, 3, 4, 5, 6], 3)) [[1, 2, 3], [4, 5, 6]]
Connexes: Stackoverflow.com/questions/312443/... Stackoverflow.com/questions/434287/... Stackoverflow.com/questions/1335392/iterectionfover-over-slic es a> Stackoverflow.com/questions/760753/...