Python вставляет несколько элементов за одну итерацию в понимании списка условно - PullRequest
0 голосов
/ 04 октября 2018

Предположим, мы должны вставить два элемента в понимание списка за одну итерацию, но нам нужно выбрать, какие два вставить.Как нам составить такой список понимания.

Например: - Предположим, у нас есть список hostnames = ['aa', 'bb', 'dd', 'pp', 'bb', 'zz', 'hh'].

Теперь мы хотим изменить этот список таким образом, чтобы имена хостов, начинающиеся с p, имели дополнительный элемент, называемый _prd (например,'pp_prd') и аналогичным образом дополнительный элемент для имен хостов, начинающийся с h со значением "_uat" (например, "hh_uat").

Таким образом, желаемый вывод для приведенного выше списка будет mod_hostnames = ['aa', 'bb', 'dd', 'pp_prd', 'pp', 'bb', 'zz', 'hh_uat','hh'].

Теперь я подумал написать так:

>>> fh=lambda x: x+'_uat'
>>> fp=lambda x:x+'_prd'
>>> fo=lambda x:x
>>> lis
['aa', 'bb', 'dd', 'pp', 'bb', 'zz', 'hh']
>>> hostnames = lis
>>> mod_hostnames = [f(host) for f in (fo,fp) if host[0]=='p' else f(host) for f in (fo,fh) if host[0]=='h' else host for host in hostnames]
  File "<stdin>", line 1
    mod_hostnames = [f(host) for f in (fo,fp) if host[0]=='p' else f(host) for f in (fo,fh) if host[0]=='h' else host for host in hostnames]
                                                                 ^
SyntaxError: invalid syntax

Мы получаем синтаксическую ошибку,Я также знаю, что в списочном понимании второй цикл выполняется быстрее (как в циклах for), и в нашем коде host in hostnames работает быстрее, чем другой требуемый способ.Итак, я попробовал это: -

>>> mod_hostnames = [f(host) for host in hostnames for f in (fo,fp) if host[0]=='p' else for f in (fo,fh) if host[0]=='h' else for f in (fo)]  
File "<stdin>", line 1
    mod_hostnames = [f(host) for host in hostnames for f in (fo,fp) if host[0]=='p' else for f in (fo,fh) if host[0]=='h' else for f in (fo)]
                                                                                       ^
SyntaxError: invalid syntax

Есть предложения?Или это невозможно в списках.Я знаю, что это вообще не читается, и есть гораздо лучшие способы записать это.(например, используя dict 'switch', чтобы записать это в одном операторе внутри цикла for, или старый добрый if в цикле и т. д.).

Редактирование пост-решения: Получены прекрасные ответы.Спасибо!Может кто-нибудь объяснить, почему код, который я разместил, также неверен?Я чувствую это потому, что операторы, вычисленные, когда условие истинно или ложно, просто преобразуются в пустой цикл for: for f in (fo,fp) if host[0]=='p' else for f in (fo,fh) если host [0] == 'p', программа переходит в пустой цикл for for f in (fo,fp).Это правильно?Думаю, обратное проектирование моего неверного понимания в цикл for прояснит это.

Ответы [ 4 ]

0 голосов
/ 04 октября 2018

На самом деле, лучше начать с традиционного цикла for, чтобы вы могли увидеть, что сложно:

mod_hostnames = []
for name in hostnames:
    if name.startswith('p'):
        r = [name + '_prd', name]
    elif name.startswith('h'):
        r = [name + '_uat', name]
    else:
        r = [name]
    mod_hostnames.extend(r)

assert mod_hostnames == ['aa', 'bb', 'dd', 'pp_prd', 'pp', 'bb', 'zz', 'hh_uat', 'hh']

Чтобы создать список понимания, вам нужно преобразовать if ... elif ... else оператор в однострочном выражении с использованием ... if ... else ...

Вы можете обработать следующим образом:

mod_hostnames = []
for name in hostnames:
    r = [name + '_prd', name] if name.startswith('p') else \
        ([name + '_uat', name] if name.startswith('h') else [name])
    mod_hostnames.extend(r)

Ну, это становится немного сложнее.

Чтобы преобразовать этот цикл в список понимания, вам нужно будет перебрать значение r.Вы получите список понимания внутри списка понимания.

mod_hostnames = [item
                 for name in hostnames
                 for item in ([name + '_prd', name] if name.startswith('p') else
                              ([name + '_uat', name] if name.startswith('h') else [name]))]

Хм!Я думаю, что это слишком сложно, чтобы вести список понимания.Кто захочет это отладить?

Для удобства обслуживания я предлагаю решение с небольшой функцией:

def modify(name):
    if name.startswith('p'):
        return [name + '_prd', name]
    elif name.startswith('h'):
        return [name + '_uat', name]
    else:
        return [name]


mod_hostnames = [item
                 for name in hostnames
                 for item in modify(name)]

Вот оно!

0 голосов
/ 04 октября 2018

Сначала вам нужно написать функцию преобразования, которая всегда возвращает кортежи , используя при необходимости одноэлементные кортежи:

def transform(name):
    if name.startswith("p"):
        return (name + "_prd", name)
    if name.startswith("h"):
        return (name + "_uat", name)
    return (name,) # Singleton tuple.

Затем вы можете сделать это:

import itertools
mod_hostnames = list(itertools.chain.from_iterable(
    transform(name) for name in hostnames))
print(mod_hostnames)

itertools.chain.from_iterable - это, по сути, плоская операция, которая объединяет все промежуточные кортежи, а внешний list превращает выходные данные этого генератора в фактический список.

Однако в таких случаях более пешеходный подходс традиционным циклом for имеет больше смысла.

0 голосов
/ 04 октября 2018

Ваша идея в порядке, но ваш синтаксис был неверным.Вот способ изменить ваш существующий код:

mod_hostnames = [
    f(host) for host in hostnames 
    for f in (
        (fo,fp) if host.startswith('p') else 
        (fo,fh) if host.startswith('h') else 
        (fo,)
    )
]
print(mod_hostnames)
#['aa', 'bb', 'dd', 'pp', 'pp_prd', 'bb', 'zz', 'hh', 'hh_uat']

Оберните if / else, чтобы изменить итерацию для f в скобках, а также вам нужна конечная запятая в (fo,)чтобы сделать его кортежем.

Вы также можете использовать str.startswith вместо индексации первого символа в строке.

В любом случае традиционный цикл здесь вполне подходит и может быть предпочтительнымдля удобочитаемости / простоты понимания:

mod_hostnames = []
for host in hostnames:
    mod_hostnames.append(fo(host))
    if host.startswith('p'):
        mod_hostnames.append(fp(host))
    elif host.startswith('h'):
        mod_hostnames.append(fh(host))
0 голосов
/ 04 октября 2018

Использование списка с if, else if, else и set

mod = [i +'_prd' if i.startswith('p') else i + '_uat' if i.startswith('h') else i for i in hostnames]
mod = sorted(list(set(mod) | set(hostnames)))
# ['aa', 'bb', 'dd', 'hh', 'hh_uat', 'pp', 'pp_prd', 'zz']
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...