كيف يمكنني دمج اثنين من مكررات بايثون؟
سؤال
لدي اثنين من التكرارات، أ 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]))