Як я можу позначити невикористані аргументи функції?


78

"Деконструюючи" кортеж, я можу використовувати _для позначення елементів кортежу, які мене не цікавлять, наприклад

>>> a,_,_ = (1,2,3)
>>> a
1

Як я можу висловити те ж саме за допомогою аргументів функції за допомогою Python 2.x? Я намагався використовувати підкреслення:

>>> def f(a,_,_): return a
...
  File "<stdin>", line 1
SyntaxError: duplicate argument '_' in function definition

Я також намагався просто повністю пропустити аргумент:

>>> def f(a,,): return a
  File "<stdin>", line 1
    def f(a,,): return a
        ^
SyntaxError: invalid syntax

Чи є інший спосіб досягти того самого?


4
Чому б просто не мати значення за замовчуванням для своїх аргументів? Чому у вас є невикористані аргументи у функції?
jamylak

5
@jamylak: Я використовую фреймворк, який очікує від мене передачі викликів у різних місцях. Однак у багатьох випадках мені насправді не потрібні всі аргументи, передані фреймворком.
Фріріх Раабе,

5
@FrerichRaabe, якщо фреймворк вимагає конкретних аргументів, тоді здається найчистішим лише використання тих самих імен, які фреймворк визначає для кожного аргументу, незалежно від того, використовує їх функція. Це також означає, що вам не доведеться міняти підпис вашої функції, коли пізніше ви виявите, що вам все-таки потрібні були інші аргументи.
Дункан,

2
@jamylak Для колеги (та таких інструментів, як pylint) поки незрозуміло, чи навмисно невикористовувані змінні . Таким чином, ви в кінцевому підсумку додасте якийсь коментар - я хотів би уникнути цього, якщо це можливо, висловивши це в самому коді.
Frerich Raabe

1
@jamylak Я не сперечався на користь відповіді, яку пропонував використовувати del(мені це теж не подобається), я виступав проти надання аргументам правдоподібних назв, але потім просто не використовував їх, оскільки інструменти, як і колеги піднімають над цим свої (електронні) брови. Звичайно, я завжди можу задокументувати свої наміри як текст англійською мовою, але я б віддав перевагу чомусь, що виражає це на Python.
Frerich Raabe

Відповіді:


30

Ось що я роблю з невикористаними аргументами:

def f(a, *unused):
    return a

2
Хм, приємно! Навіть працює з лямбдами. Однак що робити, якщо я хочу проігнорувати перший і третій аргументи, але не другий?
Фріріх Раабе,

1
@FrerichRaabe: тоді вам все одно доведеться виписати перший. Вибач за це.
Fred Foo

Розумію. Шкода, у моєму випадку це насправді перший (або перші два) аргументи, які я хочу проігнорувати. Тим не менше, ваш підхід варто знати, дякую за поділ! +1 від мене.
Фріріх Раабе,

2
@JoelCornett: Я не впевнений, що розумію. Можливо, це через відсутність досвіду Python - я працюю з фреймворком, який вимагає від мене виклику з чотирма аргументами. Зрештою фреймворк називає мою функцію як f(a,b,c,d). Здається, явно не називає аргументів - чи kwargsдопомогло б у цьому випадку використання на моїй стороні? Можливо, рамки неоптимальні, але що я можу сказати - я повинен використовувати те, що мені дають. : -}
Фріріх Раабе,

1
@JoelCornett: Цим я зараз і займаюся. Просто деякі аргументи мають досить довгі імена, тому я подумав скоротити їх до чогось - скажімо, _ - щоб я міг бачити, що передається змінна - мені просто не потрібно (зараз).
Frerich Raabe,

105

Кумедний спосіб, про який я тільки що думав, - це видалення змінної:

def f(foo, unused1, unused2, unused3):
    del unused1, unused2, unused3
    return foo

Це має численні переваги:

  • Невикористовувана змінна все ще може використовуватися при виклику функції як позиційний аргумент, так і як аргумент ключового слова.
  • Якщо ви почнете використовувати його пізніше, ви не зможете, оскільки його видалено, тому ризик помилок менше.
  • Це стандартний синтаксис python.
  • PyCharm робить правильно! (Станом на 2020 рік PyCharm більше не робить належних дій: (відстеження цього на https://youtrack.jetbrains.com/issue/PY-39889 )
  • PyLint не скаржиться, і використання del- це рішення, рекомендоване в посібнику PyLint .

2
Мені дуже подобається підказка, і я її прийму. Я все одно додам невеликий коментар, наприклад, # Ignored parameters.оскільки намір насправді не полягає у "видаленні" змінних.
Teebrin

6
Чудово, це ЄДИНА відповідь, яка стосується ВИВЕДЕННЯ невикористаних параметрів, на відміну від кінцевого. Інші мають бути проти або проти виключені;)
Томаш Гандор,

1
Чи визначає це оптимізатор?
Пепеду

1
@Pepedou На даний момент, мабуть, не в типовому випадку. delСам бере на себе «визволення» аргумент (так він отримує право на збір сміття , якщо нічого іншого не посилатися на нього). Єдина інша накладна витрата - це передача самого аргументу, який повинен бути незначним, але не може бути оптимізований без аналізу цілої програми 1) пошук і зміна кожного абонента, який не передає ці аргументи, 2) забезпечення того, що ці оптимізовані абоненти не будуть викликати функції що ж до сих пір використовує ці аргументи в оптимізованому чином, і 3) довести , що ніякий інший абонент функції не існує. (продовження)
mtraceur

1
@Pepedou (продовження) Але Python настільки динамічний, що третя вимога в основному неможлива в загальному випадку. А «досить просуне гіпотетичний оптимізатор» може створити код , який робить деякі припущення, має ті оптимізації, і має для перевірки цих припущень, і має певний резервний код , коли ті НЕ в змозі . Це потрапляє в "оптимізацію точки доступу" та "своєчасну компіляцію" - інші реалізації Python, такі як PyPy, вже роблять це. TL; DR: Я б не очікував, що невикористані аргументи будуть оптимізовані, але я також думаю, що накладні витрати абсолютно незначні.
mtraceur

54

Підкреслення використовується для речей, які нас не турбують, а аргументи * у * позначають список аргументів. Тому ми можемо використовувати * _ для позначення переліку речей, які нас не турбують:

def foo(bar, *_):
    return bar

Він навіть проходить перевірки PyCharm.



5
Я використовую _nameяк _rubbishдля параметрів викиду з трьох причин: а) Pycharm більше не відображає попередження. б) Я пам’ятаю, що викидаю. в) Не існує конфлікту з одним підкресленням.
Thinkeye

_name використовується для позначення напівприватних змінних / атрибутів класу. Використовувати його тут не має сенсу, оскільки метою є ігнорування введених даних. Однак це має настільки мало сенсу, що навряд чи вийде плутанина із звичайним використанням _name.
Paul Brown

1
це за замовчуванням використовується пілінт, ignored-argument-names=_.*шаблони збігів аргументів дозволяється не використовувати
Anentropic

Дві конвенції не дуже стикаються: для зовнішніх імен, тобто імен класів, функцій та методів, підкреслення під знаком означає "не чіпати, якщо ви не знаєте, що робите"; для локальних змінних - підкреслення під знаком означає "невикористаний". Однак для аргументів вони справді стикаються. Аргументи стають локальними змінними, АЛЕ в Python вони є частиною зовнішнього інтерфейсу функції, якщо абонент передає їх по імені. У такому випадку ви не можете їх перейменовувати. Гірше того, _argконвенція також використовується в значенні "необов'язковий приватний аргумент, не проходьте, якщо ви не знаєте, що робите".
Бені Чернявський-

36

Ви можете використовувати '_' як префікс, щоб pylint ігнорував ці параметри:

def f(a, _b, _c):

1

Якщо у вас є як аргументи, так і ключове слово аргумент, вам слід скористатися

def f(a, *args, **kwargs):
    return a

Я відчуваю, що для цього в pylint має бути налаштування конфігурації, загальноприйнято приймати загальні *args, **kwargsфункції для тих функцій, де ти можеш ними не користуватися чи піклуватися про них
Anentropic

1
Взяти всі аргументи, що можливі у Всесвіті, і кинути їх на підлогу - це жахлива пропозиція.
боксував

0

Я думаю, що прийнята відповідь погана, але вона все одно може спрацювати, якщо ви використовуєте те, що я повинен називати "способом Perl", для роботи з аргументами (я не знаю Perl насправді, але я кинув спроби вивчити його, побачивши subсинтаксис, з ручним розпаковуванням аргументів):

Ваша функція має 3 аргументи - це те, що вона викликає (набираючи качку, пам’ятаєте?). Отже, ви отримуєте:

def funfun(a, b, c):
    return b * 2

2 невикористані параметри. Але зараз укажіть вдосконалений підхід лерсманів:

def funfun(*args):
    return args[1] * 2

І там ідуть попередження ...

Однак мені все одно подобається більше в боксі:

def funfun(a, b, c):
    del a, c
    return b * 2

Він зберігає якість самодокументування імен параметрів. Вони є функцією, а не помилкою.

Але сама мова вас там не примушує - ви також можете піти навпаки, і просто дозволити всій вашій функції мати підпис (*args, **kwargs), і кожен раз робити синтаксичний аналіз аргументів. Уявіть, який рівень контролю вам дає. І більше немає винятків, коли вас викликають застарілим способом після зміни вашого "підпису" (кількість аргументів і значення). Це те, що варто розглянути;)


1
Я думаю, що той факт, що ви написали # unusedкоментар, натякає на те, що насправді це не так самодокументоване чи ідіоматичне використання del.
Frerich Raabe

Добре, я видалив коментар. Зараз це справді самодокументування. (І серйозно: ви стверджуєте, що "ігнорування" не є самодокументуванням. Мені байдуже, якщо ігнорування самодокументоване, але чи API самодокументовується).
Томаш Гандор

1
Це не робить API самодокументуванням. Крім того, те, що зроблено, зроблено і не можна скасувати: той факт, що ви вважали за необхідне написати коментар, свідчить про те, що насправді незрозуміло, що delробить. Або ви вважаєте, що незрозумілий код стає менш неясним, якщо ви видаляєте з нього коментарі?
Джонатан Каст,

Ви не розумієте, що в цьому випадку означає "самодокументування API": це означає той факт, що аргументи: 1) присутні, їх кількість відома; 2) мають значущі імена. API означає "інтерфейс", підпис функції. Що нижче def(..)рядка - коментувати чи ні - не має значення.
Томаш Гандор,

0

Щоб уникнути повідомлень про перевірку "невикористовуваної змінної" для невикористаних * аргументів та / або ** kwargs, я замінюю argsі kwargsна _та __:

def f(a, b, *_, **__):
    ...

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

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

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