Як розірвати лінію ланцюгових методів у Python?


138

У мене є рядок із наступного коду (не звинувачуйте в назві конвенцій, вони не мої):

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 символів у цій ситуації. Чи є кращий спосіб розбити його (бажано, без нахилів)?

Відповіді:


256

Ви можете використовувати додаткові дужки:

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()
    )

Мені також це найбільше подобається. Не додає більше коду, і це без зворотних нахилів.
Джуліуш Гонера

22
Не впевнений, що виправдовує тут додатковий відступ; Я думаю, що це рішення читається так само добре, як висячі лінії, відрізані лише один раз, а останній батько зовсім не такий.
Карл Мейєр

4
На мою думку, подвійне відступ є корисним тут, оскільки воно візуально відрізняється від звичайного відрізного блоку. Якщо оточений іншим кодом, це робить більш очевидним, що це обернутий єдиний рядок.
sth

1
Найкраща відповідь, з точки зору використання паронів. Як згадується в коментарі Шанімала в іншій відповіді, використання мається на увазі продовження рядка через дужки насправді PEP 8 віддає перевагу проти символу продовження ``
kevlarr

Я вважаю за краще зворотні коси. Парентез - це не натяк на всю ситуацію. Наприклад, він не працює з оператором призначення. Уявіть, що ви хочете зламати лінії в цьому ланцюжку:foo.set_default('bar', {}).set_default('spam', {}).set_default('eggs', {})['lol'] = 'yeah'
loutre

56

Це випадок, коли символ продовження рядка вважається за краще відкривати дужки. Потреба в цьому стилі стає більш очевидною, оскільки імена методів стають довшими, і коли методи починають брати аргументи:

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 на косому напрямку і вирівнюючи ланцюгові фільтри в цьому конкретному випадку. Ця ситуація виникає і в Джанго і є найбільш читаною таким чином, - але в будь-якій іншій ситуації я відчуваю, що круглі фрази є вищими (не страждаєте від проблеми "чи є пробіл після мого зворотного кута?"). З цього приводу, для досягнення такого ж ефекту можна використовувати дужки в дужках, але це переводить вас у режим читання в Ліспі посеред читання Python, який я вважаю нездужаючим.
zxq9

11
Я не бачу, як з цим рішенням краще впоратися, «оскільки імена методів стають довшими, і коли методи починають брати аргументи», ніж «перегортання у зовнішні паролі» або «розрив рядків після кожного відкритого батька та перед кожним закритим батьком» рішення. Насправді, в поводженні з цим гірше, оскільки (принаймні, як показано тут) для кожного висячого рядка потрібно набагато глибший відступ.
Карл Мейєр

1
Занадто багато відступу для викликів фільтрів. Тут вистачило б однієї вкладки або 4 пробілів. Також вирівнювання `` ... Скільки секунд ви затримали цю клавішу пробілу? Як правило, я проти всіх способів, які вимагають від вас забити цей космічний ключ, як ні завтра.
Зельфір Кальтшталь

2
fwiw, PEP8 читає "Кращим способом загортання довгих рядків є використання мається на увазі продовження лінії Python всередині дужок, дужок і дужок. Довгі рядки можуть бути розбиті на кілька рядків, загортаючи вирази в круглі дужки. Їх слід використовувати, надаючи перевагу використанню зворотної косої риски. для продовження рядка. " - Python.org Продовжується обговорення, коли
нахили

Чудова посилання на PEP8! Тут набридлива проблема вирівнювання всіх .filterвикликів полягає в тому, що якщо ви перейдете subkeywordна sub_keyword, тепер вам доведеться виправити відступ кожного окремого рядка лише тому, що ви змінили ім'я змінної. Не добре, коли стиль насправді заважає
ремонту

15

Мій особистий вибір був би:

subkeyword = Session.query (
    Subkeyword.subkeyword_id,
    Subkeyword.subkeyword_word,
) .filter_by (
    subkeyword_company_id = self.e_company_id,
    subkeyword_word = підрозділ_word_word,
    subkeyword_active = Правда,
) .one ()

1
Я згоден, якщо кілька параметрів передаються, але це виглядає некрасиво, коли загальні 0 або 1 параметри. Наприклад: gist.github.com/andybak/b23b6ad9a68c7e1b794d
Енді Бейкер

1
Так, цей стиль має вироджені випадки (як і будь-який стиль). Я б не зламався на всіх відкритих паренах. Ніщо з цього не залишає мене щасливим, але ось деякі випадки: gist.github.com/pkoch/8098c76614765750f769
pkoch

12

Просто збережіть проміжний результат / об'єкт і застосуйте на ньому наступний метод, наприклад

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

4

Відповідно до посилання на мову Python,
ви можете використовувати зворотну косу рису.
Або просто зламати його. Якщо дужка не є парною, python не сприйме це як лінію. І за таких обставин відступ наступних рядків не має значення.


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 умову.
Джуліуш Гонера

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.