Как разорвать цепочку методов в Python?

145

У меня есть строка следующего кода (не вините за соглашения об именах, они не мои):

subkeyword = Session.query(
    Subkeyword.subkeyword_id, Subkeyword.subkeyword_word
).filter_by(
    subkeyword_company_id=self.e_company_id
).filter_by(
    subkeyword_word=subkeyword_word
).filter_by(
    subkeyword_active=True
).one()

Мне не нравится, как это выглядит (не слишком читабельно), но у меня нет лучшей идеи ограничить строки 79 символами в этой ситуации. Есть ли способ лучше его сломать (желательно без обратной косой черты)?

Юлиуш Гонера
источник

Ответы:

265

Вы можете использовать дополнительные скобки:

subkeyword = (
        Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
        .filter_by(subkeyword_company_id=self.e_company_id)
        .filter_by(subkeyword_word=subkeyword_word)
        .filter_by(subkeyword_active=True)
        .one()
    )
что-то
источник
Мне это тоже нравится больше всего. Не добавляет кода и без обратной косой черты.
Юлиуш Гонера
23
Не уверен, что оправдывает дополнительный отступ здесь; Я думаю, что это решение читается так же хорошо, когда висячие строки имеют только один отступ, а конечный парен - нет.
Карл Мейер
4
На мой взгляд, здесь полезен двойной отступ, потому что он визуально отличается от обычного блока с отступом. В окружении другого кода становится более очевидным, что это одинарная обернутая строка.
чт,
1
Лучший ответ с точки зрения использования паренсов. Как упоминалось в комментарии Shanimal в другом ответе, использование подразумеваемого продолжения строки через круглые скобки на самом деле является предпочтительным для PEP 8 по сравнению с символом продолжения ''
kevlarr
Я предпочитаю обратную косую черту. Скобки - это не намек на всю ситуацию. Например, он не работает с оператором присваивания. Представьте, что вы хотите разорвать строки в этой цепочке:foo.set_default('bar', {}).set_default('spam', {}).set_default('eggs', {})['lol'] = 'yeah'
loutre 09
57

Это тот случай, когда символ продолжения строки предпочтительнее открывать круглые скобки. Потребность в этом стиле становится более очевидной по мере того, как имена методов становятся длиннее и когда методы начинают принимать аргументы:

subkeyword = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id)          \
                    .filter_by(subkeyword_word=subkeyword_word)                  \
                    .filter_by(subkeyword_active=True)                           \
                    .one()

PEP 8 должен интерпретироваться с учетом здравого смысла и внимания как к практичному, так и к прекрасному. К счастью, нарушайте любые рекомендации PEP 8, которые приводят к некрасивому или трудному для чтения коду.

При этом, если вы часто обнаруживаете разногласия с PEP 8, это может быть признаком того, что есть проблемы с читабельностью, которые выходят за рамки вашего выбора пробелов :-)

Раймонд Хеттингер
источник
2
+1 на обратную косую черту и выравнивание связанных фильтров в этом конкретном случае. Эта ситуация возникает и в Django, и она наиболее удобна для чтения, но в любой другой ситуации я чувствую, что фразы в скобках лучше (не страдайте от проблемы «есть ли пробелы после моей обратной косой черты?»). Тем не менее, заключение фразы в скобки может быть использовано для достижения того же эффекта, но это переводит вас в режим чтения Лиспа в середине чтения Python, что меня раздражает.
zxq9
12
Я не понимаю, как это решение лучше справляется с "по мере того, как имена методов становятся длиннее и когда методы начинают принимать аргументы", чем "перенос во внешние скобки" или "перенос строки после каждого открытого парена и перед каждым закрывающим пареном" решения. На самом деле с этим справляется хуже, поскольку (по крайней мере, как показано здесь) требуется гораздо более глубокий отступ для каждой висящей линии.
Карл Мейер
1
Слишком большой отступ для вызовов фильтров. Здесь было бы достаточно одной табуляции или 4 пробелов. Также выравнивание `` ... Сколько секунд вы удерживали эту клавишу пробела? Обычно я против всех способов, которые требуют, чтобы вы забивали эту клавишу пробела, как будто завтра не наступит.
Zelphir Kaltstahl
2
fwiw, PEP8 гласит: «Предпочтительный способ обертывания длинных строк - использование подразумеваемого продолжения строки Python внутри скобок, скобок и фигурных скобок. Длинные строки можно разбить на несколько строк, заключив выражения в круглые скобки. Их следует использовать вместо использования обратной косой черты. для продолжения строки ". - Python.org Далее обсуждается, когда могут быть уместны
обратные
Отличная ссылка на PEP8! Раздражающая проблема здесь с выравниванием всех .filterвызовов заключается в том, что если вы измените значение subkeywordна sub_keyword, теперь вам придется исправить отступ каждой отдельной строки только потому, что вы изменили имя переменной.
Нехорошо,
18

Мой личный выбор:

subkeyword = Session.query (
    Subkeyword.subkeyword_id,
    Subkeyword.subkeyword_word,
).фильтровать по(
    subkeyword_company_id = self.e_company_id,
    subkeyword_word = subkeyword_word,
    subkeyword_active = Верно,
).один()
пкоч
источник
1
Я согласен, если передаются несколько параметров, но это выглядит некрасиво, когда 0 или 1 параметры являются общими. Например: gist.github.com/andybak/b23b6ad9a68c7e1b794d
Энди Бейкер
1
Да, у этого стиля есть вырожденные случаи (как и у любого стиля). Я бы не стал ломать все открытые пары. Ничто из этого не оставляет меня счастливым, но вот некоторые случаи: gist.github.com/pkoch/8098c76614765750f769
pkoch
13

Просто сохраните промежуточный результат / объект и вызовите для него следующий метод, например

q = Session.query(Subkeyword.subkeyword_id, Subkeyword.subkeyword_word)
q = q.filter_by(subkeyword_company_id=self.e_company_id)
q = q.filter_by(subkeyword_word=subkeyword_word)
q = q.filter_by(subkeyword_active=True)
subkeyword = q.one()
Иво ван дер Вейк
источник
10
Это хорошо работает для чего-то вроде запроса, но в целом я не уверен. Например, при цепочке в Beautiful Soup like team_members = soup.find(class_='section team').find_all('ul').find_all('li')возвращаемое значение от каждого .find(...)вызова еще не соответствует значению team_members.
Тейлор Эдмистон,
1
@TaylorEdmiston Конечно, частичные результаты могут иметь разные имена. Что-то вроде section = soup.find(class_='section team')и team_members = section.find_all('ul').find_all('li').
Jeyekomon 08
4

Согласно справочнику по языку Python,
вы можете использовать обратную косую черту.
Или просто сломайте. Если скобка не спарена, python не будет рассматривать это как строку. И в таком случае отступ следующих строк не имеет значения.

Haozhun
источник
4

Это немного другое решение, чем другие, но мое любимое, поскольку оно иногда приводит к изящному метапрограммированию.

base = [Subkeyword.subkeyword_id, Subkeyword_word]
search = {
    'subkeyword_company_id':self.e_company_id,
    'subkeyword_word':subkeyword_word,
    'subkeyword_active':True,
    }
subkeyword = Session.query(*base).filter_by(**search).one()

Это хороший прием для построения поисковых запросов. Просмотрите список условных выражений для извлечения из вашей сложной формы запроса (или основанные на строках выводы о том, что ищет пользователь), а затем просто вставьте словарь в фильтр.

Арни Санкт-Сигурэссон
источник
1

Кажется, вы используете SQLAlchemy, если это правда, sqlalchemy.orm.query.Query.filter_by()метод принимает несколько аргументов ключевого слова, поэтому вы можете написать так:

subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word) \
                    .filter_by(subkeyword_company_id=self.e_company_id,
                               subkeyword_word=subkeyword_word,
                               subkeyword_active=True) \
                    .one()

Но лучше бы:

subkeyword = Session.query(Subkeyword.subkeyword_id,
                           Subkeyword.subkeyword_word)
subkeyword = subkeyword.filter_by(subkeyword_company_id=self.e_company_id,
                                  subkeyword_word=subkeyword_word,
                                  subkeyword_active=True)
subkeuword = subkeyword.one()
Минхи
источник
+1 за подсказку SQLAlchemy filter_by (). Это хорошо для этого примера, но вместо этого я часто использую filter (), который принимает только одно условие.
Юлиуш Гонера
1

Мне нравится делать отступы аргументов на два блока и оператора на один блок, например:

for image_pathname in image_directory.iterdir():
    image = cv2.imread(str(image_pathname))
    input_image = np.resize(
            image, (height, width, 3)
        ).transpose((2,0,1)).reshape(1, 3, height, width)
    net.forward_all(data=input_image)
    segmentation_index = net.blobs[
            'argmax'
        ].data.squeeze().transpose(1,2,0).astype(np.uint8)
    segmentation = np.empty(segmentation_index.shape, dtype=np.uint8)
    cv2.LUT(segmentation_index, label_colours, segmentation)
    prediction_pathname = prediction_directory / image_pathname.name
    cv2.imwrite(str(prediction_pathname), segmentation)
акгтирант
источник