Визначте функції із занадто великою кількістю аргументів, щоб відповідати стандарту PEP8


76

Я визначив функцію з довгим списком аргументів. Загальна кількість символів у визначенні перевищує 80 і не відповідає PEP8.

def my_function(argument_one, argument_two, argument_three, argument_four, argument_five):

Яким може бути найкращий підхід, щоб уникнути горизонтальної прокрутки?


1
І який найкращий спосіб написати оператор виклику функції? :)
Набін

Відповіді:


110

Приклад наведено в PEP 8:

class Rectangle(Blob):

    def __init__(self, width, height,
                 color='black', emphasis=None, highlight=0):

Тож це офіційна відповідь. Особисто я ненавиджу такий підхід, при якому рядки продовження мають пробіли, що не відповідають жодному реальному рівню відступу. Мій підхід буде таким:

class Rectangle(Blob):

    def __init__(
        self, width, height,
        color='black', emphasis=None, highlight=0
    ):

. . . або просто нехай рядок перевищує 80 символів.


9
Я насправді був у розгубленості, вибираючи між цими двома. Для першого, суцільні рядки мають довільні пробіли, що я не вважаю за краще. Другий виглядає заплутаним, будучи функціоналом. Я вважаю за краще офіційну відповідь PEP8.
Судіп Кафле

3
Я вважаю метод у вашому другому прикладі брудним. Декларація в першій кидається в очі без проблем, тоді як у другій мені потрібно придивитися уважніше, щоб її знайти. Особливо, коли існує 20 методів з по 2-3 рядки в кожному класі.
Błażej Michalik

5
Йдеться не про те, щоб бути більш-менш брудними, це про метод на другому прикладі насправді більш практичний. Це дозволяє редактору робити належне згортання коду тіла методу, одночасно дозволяючи побачити параметри методу. Офіційний, як припускає автор, не відповідає жодному рівню відступу.
BPL

1
Потенційно трохи не в темі, але хтось знає, чи є спосіб змусити flake8 вийти з ладу, коли він знаходить перше рішення? Мені це дуже не подобається, хоча це стандартний PEP8 і віддаю перевагу другому рішенню набагато більше.
Аріель

1
Якщо ви можете, додайте до списку правило "E128" pep8 у своєму підводці, якщо це не працює краще, напишіть власні власні правила @Ariel
Salyangoz

20

Для коду Python, який використовує анотації типів , я пропоную таке:

def some_func(
    foo: str,
    bar: str = 'default_string',
    qux: Optional[str] = None,
    qui: Optional[int] = None,
) -> List[str]:
    """
    This is an example function.
    """
    print(foo)
    ...

Якщо ви використовуєте yapf, ви можете використовувати ці параметри в .style.yapf:

[style]
dedent_closing_brackets = true
split_arguments_when_comma_terminated = true

Я б зробив це незалежно від того, чи використовуєте ви анотації. Це і більш ремонтопридатне, і більш розбірливе.
DylanYoung

ПРИМІТКА: Мені не вдалося змусити YAPF відповідати цьому стилю. Я дійшов висновку, що принциповою проблемою способу проектування YAPF є упаковка контейнерів. Упаковка сміття в значній мірі протилежна тому, що ви хочете зробити для форматування коду.
DylanYoung

14

Особисто я також придумував те саме рішення, що і другий стиль @BrenBarn. Мені подобається його спосіб правильно відображати відступ параметрів функції І її реалізацію, хоча "нещасне обличчя" є дещо незвичним для деяких інших людей.

На сьогодні PEP8 конкретно наводить приклад для такого випадку, тому, можливо, мейнстрім буде адаптувати цей стиль:

    # Add 4 spaces (an extra level of indentation)
    # to distinguish arguments from the rest.
    def long_function_name(
            var_one, var_two, var_three,
            var_four):
        print(var_one)

Звичайно, ми можемо піти на крок далі, відокремивши кожен параметр у свій рядок, так що будь-яке подальше додавання / видалення параметра дасть чистий результат git diff:

    # Add 4 spaces (an extra level of indentation)
    # to distinguish arguments from the rest.
    def long_function_name(  # NOTE: There should be NO parameter here
            var_one,
            var_two,
            var_three,
            var_four,  # NOTE: You can still have a comma at the last line
            ):  # NOTE: Here it aligns with other parameters, but you could align it with the "def"
        print(var_one)


10

1. Що я б порекомендував

Незважаючи на те, що це робить функцію більш багатослівною, для більш ніж одного аргументу , я вважаю, що це найкраще - наведений нижче приклад - Python-:

def my_function(
    argument_one, 
    argument_two, 
    argument_three,
    argument_four, 
    argument_five,
):
    ...

2. Чому?

  1. Наявність кожного аргументу в одному рядку робить дуже простим використання git diffs, оскільки зміна однієї змінної лише відобразить цю зміну. Якщо у вас є більше одного аргументу в кожному рядку, це буде більш візуально дратувати пізніше.
    • Зауважте, що кінцева кома після останнього аргументу спрощує додавання або видалення аргументу пізніше, а також відповідає Конвенції PEP 8 про кінцеву кому , а git diffпізніше дає кращу .
  2. Однією з причин, чому я дійсно не люблю парадигму "вирівняти аргументи з відкриваючими дужками", є те, що вона не дає легко підтримуваного коду .
    • Кевлін Хенні пояснює, що це погана практика в його ІТТ 2016 - Лекція про сім неефективних звичок кодування багатьох програмістів (близько 17:08) .
    • Основна причина, що це погана практика, полягає в тому, що якщо ви зміните назву функції (або якщо вона занадто довга), вам доведеться перередагувати інтервал у всіх рядках аргументів, що не відповідає все масштабоване, хоча може бути ( іноді ) ( суб’єктивно ) симпатичнішим.
    • Інша причина, тісно пов’язана з наведеною вище, стосується метапрограмування . Якщо кодова база стане занадто великою, зрештою вам доведеться запрограмувати зміни у самому файлі коду, що може стати пеклом, якщо інтервали для аргументів різні для кожної функції.
  3. Після відкриття дужок більшість редакторів enterвідкриють новий рядок табуляцією та переносять закриваючі дужки в наступний рядок без вкладки. Отже, форматування коду таким чином є дуже швидким і начебто стандартизованим. Наприклад, це форматування дуже поширене в JavaScriptта Dart.
  4. Нарешті, якщо ви вважаєте, що кожен аргумент занадто громіздкий, оскільки у вашої функції може бути багато аргументів, я б сказав, що ви компрометуєте легке форматування коду через рідкісні винятки.
    • Не приймайте законодавство за винятками .
    • Якщо у вашій функції багато аргументів, ви, мабуть, робите щось не так. Розбийте його на більше (під) функцій та (під) класів.

3. У будь-якому разі ...

Хороша конвенція краще, ніж погана, але набагато важливіше дотримуватися такої, ніж непотрібна до них вимогливість .

Після того, як ви вирішили використовувати стандарт, поділіться своїм рішенням зі своїми колегами та скористайтеся автоматичним форматуванням - наприклад, Prettier - популярний вибір для JavaScriptin VS Code; а Dartмова стандартизувала один у всьому світі: dartfmt- послідовно застосовувати його, зменшуючи потребу в ручному редагуванні.


4

Я вважаю себе таким чином досить цікавим:

def my_function(
        argument_one, argument_two, argument_three,
        argument_four, argument_five
):
    ...

це дозволяє складання коду досить легко розкрити підписи функції, наприклад, розглянемо наведений нижче фрагмент:

def my_function(
        argument_one, argument_two, argument_three,
        argument_four, argument_five
):
    s1 = 1
    s2 = 2
    if s1 + s2:
        s3 = 3


def my_other_function(argument_one, argument_two, argument_three):
    s1 = 1
    s2 = 2
    if s1 + s2:
        s3 = 3

Цей спосіб дозволяє кодувати весь файл і переглядати всі функції / підписи одночасно, тобто:

введіть тут опис зображення


3

Мені особисто подобається виставляти параметри по одному на рядок, починаючи з відкритих дужок і дотримуючись цього відступу. flake8здається, це теж задоволено.

def guess_device_type(device_name: str,
                      username: str=app.config['KEY_TACACS_USER'],
                      password: str=app.config['KEY_TACACS_PASS'],
                      command: str='show version') -> str:
    """Get a device_type string for netmiko"""

У цього стилю є кілька технічних проблем. 1) для синтаксичного аналізу модуля, повного функцій, оку доводиться постійно переходити туди, куди він сканує горизонтально (якщо всі назви функцій не мають однакову довжину;)). 2) Додавання аргументу після commandстворить помилкові різницькі рядки. 3) Введення функціонального блоку є назвою (це не інтуїтивно, оскільки в python відступ зазвичай починає блок).
DylanYoung

1
@DylanYoung, хороші бали! У наші дні я використовую blackдля автоматичного форматування коду.
Бен,

Я б також використовував чорний у будь-якому новому коді. +1 Наші проблеми в основному стосуються міграції старого коду, ха-ха. Колись ....
DylanYoung
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.