سؤال

لدي اثنين من التكرارات، أ list و itertools.count كائن (أيمولد قيمة لا نهائية).أرغب في دمج هذين الاثنين في مكرر ناتج يقوم بتبديل قيم الإنتاجية بين الاثنين:

>>> import itertools
>>> c = itertools.count(1)
>>> items = ['foo', 'bar']
>>> merged = imerge(items, c)  # the mythical "imerge"
>>> merged.next()
'foo'
>>> merged.next()
1
>>> merged.next()
'bar'
>>> merged.next()
2
>>> merged.next()
Traceback (most recent call last):
    ...
StopIteration

ما هي الطريقة الأبسط والأكثر إيجازًا للقيام بذلك؟

هل كانت مفيدة؟

المحلول

ومولد سوف يحل مشكلتك بشكل جيد.

def imerge(a, b):
    for i, j in itertools.izip(a,b):
        yield i
        yield j

نصائح أخرى

ويمكنك أن تفعل ما هو exaclty تقريبا ما اقترحPramod أولا.

def izipmerge(a, b):
  for i, j in itertools.izip(a,b):
    yield i
    yield j

وميزة هذه الطريقة هي أنك لن ينفد من الذاكرة إذا كان كل من أ و ب هي غير نهائية.

وأنا أتفق أيضا على أنه لا حاجة itertools.

ولكن لماذا التوقف عند 2؟

  def tmerge(*iterators):
    for values in zip(*iterators):
      for value in values:
        yield value

ويعالج أي عدد من التكرارات من 0 على أعلى.

وUPDATE: وزارة الصحة! وأشار المعلق إلى أن هذا لن ينجح إلا إذا كل المكررات هي نفس الطول.

ورمز الصحيح هو:

def tmerge(*iterators):
  empty = {}
  for values in itertools.izip_longest(*iterators, fillvalue=empty):
    for value in values:
      if value is not empty:
        yield value

وونعم، أنا فقط حاولت ذلك مع قوائم غير المتكافئ طول، وقائمة تحتوي على {}.

وكنت تفعل شيئا من هذا القبيل. وسيكون هذا معظم الوقت والفضاء كفاءة، وبما انك لن يكون الحمل من فتح سوستة الأشياء معا. هذا وستعمل أيضا إذا كان كل a وb هي بلا حدود.

def imerge(a, b):
    i1 = iter(a)
    i2 = iter(b)
    while True:
        try:
            yield i1.next()
            yield i2.next()
        except StopIteration:
            return

يمكنك استخدام zip إلى جانب itertools.chain.هذا سوف عمل فقط إذا كانت القائمة الأولى محدود:

merge=itertools.chain(*[iter(i) for i in zip(['foo', 'bar'], itertools.count(1))])

ولست متأكدا ما هو طلبك، ولكن قد تجد اعدد () تعمل أكثر فائدة.

>>> items = ['foo', 'bar', 'baz']
>>> for i, item in enumerate(items):
...  print item
...  print i
... 
foo
0
bar
1
baz
2

وأنا أفضل هذه الطريقة الأخرى التي هي أكثر من ذلك بكثير موجزة:

iter = reduce(lambda x,y: itertools.chain(x,y), iters)

واحدة من السمات أقل من المعروف جيدا من بيثون هو أنه يمكن أن يكون أكثر لبنود في التعبير المولد. مفيدة جدا لتسطيح قوائم متداخلة، مثل تلك التي تحصل من الرمز البريدي () / izip ().

def imerge(*iterators):
    return (value for row in itertools.izip(*iterators) for value in row)

وهنا هو حل أنيق:

def alternate(*iterators):
    while len(iterators) > 0:
        try:
            yield next(iterators[0])
            # Move this iterator to the back of the queue
            iterators = iterators[1:] + iterators[:1]
        except StopIteration:
            # Remove this iterator from the queue completely
            iterators = iterators[1:]

وعن طريق قائمة انتظار الفعلي لتحسين الأداء (كما اقترح ديفيد):

from collections import deque

def alternate(*iterators):
    queue = deque(iterators)
    while len(queue) > 0:
        iterator = queue.popleft()
        try:
            yield next(iterator)
            queue.append(iterator)
        except StopIteration:
            pass

وكان يعمل حتى عند بعض المكررات هي محدودة والبعض الآخر لا حصر له:

from itertools import count

for n in alternate(count(), iter(range(3)), count(100)):
    input(n)

وأختام:

0
0
100
1
1
101
2
2
102
3
103
4
104
5
105
6
106

وكما يتوقف بشكل صحيح إذا / عندما تستنفد كل التكرارات.

إذا كنت تريد التعامل مع iterables غير مكرر، مثل القوائم، يمكنك استخدام

def alternate(*iterables):
    queue = deque(map(iter, iterables))
    ...

استخدم izip وسلسلة معا:

>>> list(itertools.chain.from_iterable(itertools.izip(items, c))) # 2.6 only
['foo', 1, 'bar', 2]

>>> list(itertools.chain(*itertools.izip(items, c)))
['foo', 1, 'bar', 2]

وماذا itertools المطلوبة؟

def imerge(a,b):
    for i,j in zip(a,b):
        yield i
        yield j

في هذه الحالة واحدة على الأقل من A أو B يجب أن يكون طول محدود، والسبب البريدي إرجاع قائمة، وليس مكرر. إذا كنت بحاجة إلى مكرر كإخراج ثم يمكنك الذهاب من أجل حل كلاوديو.

وعن طريق itertools.izip ()، بدلا من الرمز () كما هو الحال في بعض الإجابات الأخرى، وتحسين الأداء:

وأما "pydoc itertools.izip" المعارض: "يعمل مثل وظيفة الرمز البريدي () ولكن يستهلك ذاكرة أقل من خلال العودة مكرر بدلا من قائمة"

وItertools.izip كما تعمل بشكل صحيح حتى لو كان واحد من التكرارات هو لانهائي.

وهناك طريقة مختصرة لاستخدام تعبير مولد مع itertools.cycle (). فإنه يتجنب خلق سلسلة طويلة () من المجموعات.

generator = (it.next() for it in itertools.cycle([i1, i2]))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top