python - भाग में एक सूची में फिर से शुरू करने के लिए सबसे अधिक "पायथनिक" तरीका क्या है?




list loops (20)

NumPy के साथ यह आसान है:

ints = array([1, 2, 3, 4, 5, 6, 7, 8])
for int1, int2 in ints.reshape(-1, 2):
    print(int1, int2)

उत्पादन:

1 2
3 4
5 6
7 8

मेरे पास एक पायथन स्क्रिप्ट है जो इनपुट के रूप में पूर्णांक की एक सूची लेती है, जिसे मुझे एक समय में चार पूर्णांक के साथ काम करने की आवश्यकता होती है। दुर्भाग्यवश, मेरे पास इनपुट का नियंत्रण नहीं है, या मैं इसे चार-तत्व टुपल्स की सूची के रूप में पास कर दूंगा। वर्तमान में, मैं इस तरह से इस पर पुन: प्रयास कर रहा हूं:

for i in xrange(0, len(ints), 4):
    # dummy op for example code
    foo += ints[i] * ints[i + 1] + ints[i + 2] * ints[i + 3]

यह "सी-थिंक" जैसा दिखता है, हालांकि, मुझे संदेह है कि इस स्थिति से निपटने का एक और अधिक पागल तरीका है। सूची को फिर से शुरू करने के बाद छोड़ दिया जाता है, इसलिए इसे संरक्षित नहीं किया जाना चाहिए। शायद ऐसा कुछ बेहतर होगा?

while ints:
    foo += ints[0] * ints[1] + ints[2] * ints[3]
    ints[0:4] = []

हालांकि, अभी भी काफी "महसूस" नहीं है। : - /

संबंधित प्रश्न: आप पाइथन में समान रूप से आकार के टुकड़ों में एक सूची कैसे विभाजित करते हैं?


अन्य प्रस्तावों के समान, लेकिन बिल्कुल समान नहीं, मुझे इसे इस तरह से करना पसंद है, क्योंकि यह आसान और पढ़ने में आसान है:

it = iter([1, 2, 3, 4, 5, 6, 7, 8, 9])
for chunk in zip(it, it, it, it):
    print chunk

>>> (1, 2, 3, 4)
>>> (5, 6, 7, 8)

इस तरह आपको अंतिम आंशिक हिस्सा नहीं मिलेगा। यदि आप अंतिम खंड के रूप में (9, None, None, None) प्राप्त करना चाहते हैं, तो केवल izip_longest से itertools उपयोग करें।


आपकी दूसरी विधि में, मैं इसे करने के द्वारा 4 के अगले समूह में अग्रिम कर दूंगा:

ints = ints[4:]

हालांकि, मैंने कोई प्रदर्शन माप नहीं किया है, इसलिए मुझे नहीं पता कि कौन सा अधिक कुशल हो सकता है।

ऐसा कहकर, मैं आमतौर पर पहली विधि का चयन करता हूं। यह सुंदर नहीं है, लेकिन यह अक्सर बाहरी दुनिया के साथ इंटरफेसिंग का परिणाम होता है।


इस समस्या का आदर्श समाधान इटरेटर के साथ काम करता है (केवल अनुक्रम नहीं)। यह भी तेज होना चाहिए।

यह itertools के लिए प्रलेखन द्वारा प्रदान किया गया समाधान है:

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

मेरी मैक बुक एयर पर ipython के %timeit का उपयोग करके, मुझे 47.5 प्रति लूप मिलता है।

हालांकि, यह वास्तव में मेरे लिए काम नहीं करता है क्योंकि परिणाम भी आकार के समूह के लिए गद्देदार हैं। पैडिंग के बिना एक समाधान थोड़ा और जटिल है। सबसे बेवकूफ समाधान हो सकता है:

def grouper(size, iterable):
    i = iter(iterable)
    while True:
        out = []
        try:
            for _ in range(size):
                out.append(i.next())
        except StopIteration:
            yield out
            break

        yield out

सरल, लेकिन बहुत धीमी: प्रति 3 9 3 हमें लूप

सबसे अच्छा समाधान मैं आंतरिक लूप के लिए उपयोग islice साथ आ सकता है:

def grouper(size, iterable):
    it = iter(iterable)
    while True:
        group = tuple(itertools.islice(it, None, size))
        if not group:
            break
        yield group

उसी डेटासेट के साथ, मुझे प्रति लूप 305 मिलते हैं।

शुद्ध समाधान प्राप्त करने में असमर्थ किसी भी तेज से, मैं एक महत्वपूर्ण चेतावनी के साथ निम्नलिखित समाधान प्रदान करता हूं: यदि आपके इनपुट डेटा में filldata उदाहरण हैं, तो आपको गलत जवाब मिल सकता है।

def grouper(n, iterable, fillvalue=None):
    #"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    for i in itertools.izip_longest(fillvalue=fillvalue, *args):
        if tuple(i)[-1] == fillvalue:
            yield tuple(v for v in i if v != fillvalue)
        else:
            yield i

मुझे वास्तव में यह जवाब पसंद नहीं है, लेकिन यह काफी तेज़ है। 124 हमें प्रति लूप


एक अन्य दृष्टिकोण iter के दो तर्क के रूप का उपयोग करना होगा:

from itertools import islice

def group(it, size):
    it = iter(it)
    return iter(lambda: tuple(islice(it, size)), ())

इसे पैडिंग का उपयोग करने के लिए आसानी से अनुकूलित किया जा सकता है (यह मार्कस जार्डरोट के उत्तर के समान है):

from itertools import islice, chain, repeat

def group_pad(it, size, pad=None):
    it = chain(iter(it), repeat(pad))
    return iter(lambda: tuple(islice(it, size)), (pad,) * size)

इन्हें वैकल्पिक पैडिंग के लिए भी जोड़ा जा सकता है:

_no_pad = object()
def group(it, size, pad=_no_pad):
    if pad == _no_pad:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(pad))
        sentinel = (pad,) * size
    return iter(lambda: tuple(islice(it, size)), sentinel)

एक लाइनर, आकार 4 हिस्सों में एक सूची x पर फिर से शुरू करने के लिए adhoc समाधान -

for a, b, c, d in zip(x[0::4], x[1::4], x[2::4], x[3::4]):
    ... do something with a, b, c and d ...

किसी भी अस्थायी सूचियों के बिना, itertools.groupby को आपके लिए पुनरावृत्त करने के लिए काम करना आसान है:

groupby(iterable, (lambda x,y: (lambda z: x.next()/y))(count(),100))

नेस्टेड लैम्बडास द्वारा न डालें, बाहरी लैम्ब्डा count() जनरेटर और निरंतर 100 को आंतरिक लैम्ब्डा के दायरे में रखने के लिए केवल एक बार चलाता है।

मैं इसका उपयोग mysql पर पंक्तियों के भाग भेजने के लिए करता हूं।

for k,v in groupby(bigdata, (lambda x,y: (lambda z: x.next()/y))(count(),100))):
    cursor.executemany(sql, v)

किसी सूची में सभी रूपांतरणों से बचने के लिए import itertools और:

>>> for k, g in itertools.groupby(xrange(35), lambda x: x/10):
...     list(g)

पैदा करता है:

... 
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
2 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
3 [30, 31, 32, 33, 34]
>>> 

मैंने groupby जांच की है और यह सूची में परिवर्तित नहीं है या len उपयोग नहीं करता है इसलिए मैं (सोचता हूं) यह वास्तव में उपयोग किए जाने तक प्रत्येक मान के संकल्प में देरी करेगा। अफसोस की बात है कि उपलब्ध उत्तरों में से कोई भी (इस समय) इस बदलाव की पेशकश नहीं कर रहा था।

जाहिर है अगर आपको प्रत्येक आइटम को बदले में घोंसला के लिए घूमने की आवश्यकता है तो g:

for k,g in itertools.groupby(xrange(35), lambda x: x/10):
    for i in g:
       # do what you need to do with individual items
    # now do what you need to do with the whole group

जीमेल एपीआई में 1000 तक बैचों में बदलाव जमा करने के लिए जनरेटर का उपभोग करने की आवश्यकता में मेरी विशिष्ट रुचि थी:

    messages = a_generator_which_would_not_be_smart_as_a_list
    for idx, batch in groupby(messages, lambda x: x/1000):
        batch_request = BatchHttpRequest()
        for message in batch:
            batch_request.add(self.service.users().messages().modify(userId='me', id=message['id'], body=msg_labels))
        http = httplib2.Http()
        self.credentials.authorize(http)
        batch_request.execute(http=http)

छोटे कार्यों और चीजों का उपयोग करना वास्तव में मुझसे अपील नहीं करता है; मैं सिर्फ स्लाइस का उपयोग करना पसंद करता हूं:

data = [...]
chunk_size = 10000 # or whatever
chunks = [data[i:i+chunk_size] for i in xrange(0,len(data),chunk_size)]
for chunk in chunks:
    ...

जेनरेटर का समर्थन करने वाले आयात के बिना यहां एक चंकर है:

def chunks(seq, size):
    it = iter(seq)
    while True:
        ret = tuple(it.next() for _ in range(size))
        if len(ret) == size:
            yield ret
        else:
            raise StopIteration()

उपयोग का उदाहरण:

>>> def foo():
...     i = 0
...     while True:
...         i += 1
...         yield i
...
>>> c = chunks(foo(), 3)
>>> c.next()
(1, 2, 3)
>>> c.next()
(4, 5, 6)
>>> list(chunks('abcdefg', 2))
[('a', 'b'), ('c', 'd'), ('e', 'f')]

फिर भी एक और जवाब, जिसके फायदे हैं:

1) आसानी से समझ में आता है
2) किसी भी पुनरावर्तनीय पर काम करता है, केवल अनुक्रमों में नहीं (उपर्युक्त उत्तरों में से कुछ फाइलहैंडल पर चकित होंगे)
3) एक बार में स्मृति में खंड को लोड नहीं करता है
4) स्मृति में एक ही इटरेटर के संदर्भों की एक खंड-लंबी सूची नहीं बनाते हैं
5) सूची के अंत में भरने के मूल्यों का कोई पैडिंग नहीं

ऐसा कहा जा रहा है कि, मैंने इसका समय नहीं लगाया है, इसलिए यह कुछ अधिक चालाक तरीकों से धीमा हो सकता है, और उपयोग के मामले में कुछ फायदे अप्रासंगिक हो सकते हैं।

def chunkiter(iterable, size):
  def inneriter(first, iterator, size):
    yield first
    for _ in xrange(size - 1): 
      yield iterator.next()
  it = iter(iterable)
  while True:
    yield inneriter(it.next(), it, size)

In [2]: i = chunkiter('abcdefgh', 3)
In [3]: for ii in i:                                                
          for c in ii:
            print c,
          print ''
        ...:     
        a b c 
        d e f 
        g h 

अद्यतन करें:
तथ्य यह है कि आंतरिक और बाहरी लूप एक ही इटरेटर से मूल्य खींच रहे हैं, इस तथ्य के कारण कुछ कमियां:
1) जारी रखें बाहरी लूप में अपेक्षित काम नहीं करता है - यह एक खंड को छोड़ने के बजाय अगले आइटम पर जारी रहता है। हालांकि, यह एक समस्या की तरह प्रतीत नहीं होता है क्योंकि बाहरी लूप में परीक्षण करने के लिए कुछ भी नहीं है।
2) ब्रेक आंतरिक लूप में अपेक्षित काम नहीं करता है - नियंत्रण फिर भी अगले आइटम के साथ इटरेटर में घुमाएगा। पूरे हिस्सों को छोड़ने के लिए, या तो एक ट्यूपल में आंतरिक इटरेटर (ii उपरोक्त) को लपेटें, उदाहरण के for c in tuple(ii) , या ध्वज सेट करें और इटेटरेटर निकालें।


मुझे एक समाधान की आवश्यकता थी जो सेट और जेनरेटर के साथ भी काम करेगी। मैं बहुत कम और सुंदर कुछ भी नहीं आ सकता था, लेकिन कम से कम यह काफी पठनीय है।

def chunker(seq, size):
    res = []
    for el in seq:
        res.append(el)
        if len(res) == size:
            yield res
            res = []
    if res:
        yield res

सूची:

>>> list(chunker([i for i in range(10)], 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

सेट:

>>> list(chunker(set([i for i in range(10)]), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

जनरेटर:

>>> list(chunker((i for i in range(10)), 3))
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]

मैं एक प्रशंसक हूँ

chunkSize= 4
for i in xrange(0, len(ints), chunkSize):
    chunk = ints[i:i+chunkSize]
    # process chunk of size <= chunkSize

यदि आपको बाहरी पैकेज का उपयोग करने में कोई फर्क नहीं पड़ता है तो आप iteration_utilities.grouper से iteration_utilties उपयोग कर सकते हैं। यह सभी पुनरावृत्तियों का समर्थन करता है (केवल अनुक्रम नहीं):

from iteration_utilities import grouper
seq = list(range(20))
for group in grouper(seq, 4):
    print(group)

जो प्रिंट करता है:

(0, 1, 2, 3)
(4, 5, 6, 7)
(8, 9, 10, 11)
(12, 13, 14, 15)
(16, 17, 18, 19)

यदि लंबाई समूहबद्धता का एक बहु नहीं है, तो यह अंतिम (अपूर्ण अंतिम समूह) भरने या छंटनी (अपूर्ण अंतिम समूह को छोड़कर) का समर्थन करता है:

from iteration_utilities import grouper
seq = list(range(17))
for group in grouper(seq, 4):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16,)

for group in grouper(seq, 4, fillvalue=None):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)
# (16, None, None, None)

for group in grouper(seq, 4, truncate=True):
    print(group)
# (0, 1, 2, 3)
# (4, 5, 6, 7)
# (8, 9, 10, 11)
# (12, 13, 14, 15)

1 अस्वीकरण: मैं उस पैकेज के लेखक हूं।


यदि सूची बड़ी है, तो ऐसा करने के लिए उच्चतम प्रदर्शन करने वाला तरीका जनरेटर का उपयोग करना होगा:

def get_chunk(iterable, chunk_size):
    result = []
    for item in iterable:
        result.append(item)
        if len(result) == chunk_size:
            yield tuple(result)
            result = []
    if len(result) > 0:
        yield tuple(result)

for x in get_chunk([1,2,3,4,5,6,7,8,9,10], 3):
    print x

(1, 2, 3)
(4, 5, 6)
(7, 8, 9)
(10,)

यहां काफी split_groups (आप split_groups फ़ंक्शन के शरीर को भी split_groups कर सकते हैं)

import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))

for x, y, z, w in split_groups(range(16), 4):
    foo += x * y + z * w

here JF Sebastian द्वारा दिए गए समाधान के बारे में:

def chunker(iterable, chunksize):
    return zip(*[iter(iterable)]*chunksize)

यह चालाक है, लेकिन एक नुकसान है - हमेशा टुपल लौटते हैं। इसके बजाय स्ट्रिंग कैसे प्राप्त करें?
बेशक आप ''.join(chunker(...)) लिख सकते हैं, लेकिन अस्थायी tuple वैसे भी बनाया गया है।

आप अपने zip लिखकर अस्थायी tuple से छुटकारा पा सकते हैं, इस तरह:

class IteratorExhausted(Exception):
    pass

def translate_StopIteration(iterable, to=IteratorExhausted):
    for i in iterable:
        yield i
    raise to # StopIteration would get ignored because this is generator,
             # but custom exception can leave the generator.

def custom_zip(*iterables, reductor=tuple):
    iterators = tuple(map(translate_StopIteration, iterables))
    while True:
        try:
            yield reductor(next(i) for i in iterators)
        except IteratorExhausted: # when any of iterators get exhausted.
            break

फिर

def chunker(data, size, reductor=tuple):
    return custom_zip(*[iter(data)]*size, reductor=reductor)

उदाहरण का उपयोग:

>>> for i in chunker('12345', 2):
...     print(repr(i))
...
('1', '2')
('3', '4')
>>> for i in chunker('12345', 2, ''.join):
...     print(repr(i))
...
'12'
'34'

def chunker(iterable, n):
    """Yield iterable in chunk sizes.

    >>> chunks = chunker('ABCDEF', n=4)
    >>> chunks.next()
    ['A', 'B', 'C', 'D']
    >>> chunks.next()
    ['E', 'F']
    """
    it = iter(iterable)
    while True:
        chunk = []
        for i in range(n):
            try:
                chunk.append(it.next())
            except StopIteration:
                yield chunk
                raise StopIteration
        yield chunk

if __name__ == '__main__':
    import doctest

    doctest.testmod()

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

from itertools import izip_longest

def chunker(iterable, chunksize, filler):
    return izip_longest(*[iter(iterable)]*chunksize, fillvalue=filler)




chunks