Як я можу використовувати перевантаження методу в Python?


169

Я намагаюся реалізувати метод перевантаження в Python:

class A:
    def stackoverflow(self):    
        print 'first method'
    def stackoverflow(self, i):
        print 'second method', i

ob=A()
ob.stackoverflow(2)

але вихід є second method 2; аналогічно:

class A:
    def stackoverflow(self):    
        print 'first method'
    def stackoverflow(self, i):
        print 'second method', i

ob=A()
ob.stackoverflow()

дає

Traceback (most recent call last):
  File "my.py", line 9, in <module>
    ob.stackoverflow()
TypeError: stackoverflow() takes exactly 2 arguments (1 given)

Як зробити цю роботу?


39
У Python розглядають методи як особливий набір " атрибутів ", і для об'єкта може бути лише один " атрибут " (і, отже, один метод) заданого імені. Останній метод перезаписує будь-які попередні методи. У Java методи не є першокласними громадянами (вони не є "атрибутами об'єктів"), а скоріше викликаються "надсиланням повідомлень", які статично вирішуються на основі найближчого типу (саме там відбувається перевантаження ).



6
Чому жодна з відповідей на це питання ще не прийнята? Просто натисніть на застарілу галочку зліва від вашої улюбленої відповіді ...
glglgl

Відповіді:


150

Це метод перевантаження, а не переопределення методу . А в Python ви робите це все в одній функції:

class A:

    def stackoverflow(self, i='some_default_value'):    
        print 'only method'

ob=A()
ob.stackoverflow(2)
ob.stackoverflow()

У Python у вас не може бути двох методів з тим самим іменем - і цього не потрібно.

Дивіться розділ Значення аргументів за замовчуванням підручника Python. Див. "Найменше здивування" та змінений аргумент за замовчуванням для поширеної помилки, яку слід уникати.

Редагувати : Див. PEP 443 для отримання інформації про нові загальні функції єдиної диспетчеризації в Python 3.4.


119
і вам не потрібно - IMHO колись було б дуже зручно мати метод перевантаження, як, наприклад, в C ++. Гаразд, це не "потрібно" в тому сенсі, що це неможливо зробити за допомогою інших конструкцій - але це зробить деякі речі легшими та простішими.
Андреас Флорат

7
@AndreasFlorath Я не згоден. Навчіться любити качку вводити і писати кожен метод, щоб це робилося лише одне, і немає необхідності в перевантаженні методом.
agf

4
+1 за те, що я змусив прочитати про "поширену помилку, яку слід уникати", перш ніж мене спіймали
mdup

43
Я хотів би трохи не погодитися;) ... перевантаження часто робить код чистішим, тому що ви не упакуєте метод із занадто великою кількістю операторів if-else, щоб обробляти різні випадки. У певному розумінні вся гама функціональних мов використовує подібну ідею, тобто відповідність аргументів-моделей. Що означає, що у вас є менші більш чисті методи .., а не гігантські нечитабельні.
sten

2
@ user1019129 Ось і є мета єдиних диспетчерських загальних функцій, доданих у Python 3.4 та пов'язаних у моїй відповіді.
agf

62

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

from pythonlangutil.overload import Overload, signature

class A:
    @Overload
    @signature()
    def stackoverflow(self):    
        print 'first method'

    @stackoverflow.overload
    @signature("int")
    def stackoverflow(self, i):
        print 'second method', i

6
Я думаю, що це єдина коректна відповідь на питання. Я б подвоїв заяву, якби міг.
Майкл

3
це добре, але він не працює на необроблених функціях, а лише в межах класу.
Legit Stack

1
@LegitStack Цю функціональність можна також додати. Це не неможливо.
Есан Кешаварзіян

3
@LegitStack Я оновив код на GitHub, тепер він працює і з функціями.
Ехсан Кешаварзіян

1
@PaulPrice Правильно. Я оновив свою відповідь і видалив офіційний розділ підтримки. Ви все ще можете використовувати мій код для відправлення перевантажень. Зараз він працює як з методами, так і з функціями. Візьміть код від GitHub. Я ще не оновлював PyPi.
Ehsan Keshavarzian

48

У Python ти не робиш так. Коли люди роблять це на таких мовах, як Java, вони, як правило, хочуть значення за замовчуванням (якщо цього немає, вони, як правило, хочуть метод з іншим ім'ям). Отже, в Python ви можете мати значення за замовчуванням .

class A(object):  # Remember the ``object`` bit when working in Python 2.x

    def stackoverflow(self, i=None):
        if i is None:
            print 'first form'
        else:
            print 'second form'

Як бачите, ви можете використовувати це, щоб викликати окрему поведінку, а не мати лише значення за замовчуванням.

>>> ob = A()
>>> ob.stackoverflow()
first form
>>> ob.stackoverflow(2)
second form

2
Переважно Noneкорисно, коли потрібно змінити значення за замовчуванням. Окрема поведінка повинна бути в окремих функціях.
agf

@agf: Noneтакож може бути корисним як справжнє значення за замовчуванням.
Кріс Морган

Так, але я мав на увазі використання його як вартового значення, саме таким чином ви використовуєте його у своїй відповіді, і, як я думаю, мій коментар дає зрозуміти.
agf

ти кажеш "взагалі"? ти маєш на увазі, що це не завжди так?
joel

24

Не можна, ніколи не потрібно і не дуже хочеться.

У Python все є об’єктом. Класи - це речі, тому вони є предметами. Так само і методи.

Існує об'єкт, Aякий називається класом. Він має атрибут під назвою stackoverflow. Він може мати лише один такий атрибут.

Коли ви пишете def stackoverflow(...): ..., що відбувається, це те, що ви створюєте об'єкт, який є методом, і призначаєте його stackoverflowатрибуту A. Якщо ви пишете два визначення, друге замінює перше, так само, як завжди поводиться завдання.

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

Замість того, щоб намагатися визначити окрему функцію для кожного типу речей, які вам могли б бути надані (що мало сенсу, оскільки ви все одно не вказуєте типи для параметрів функції), перестаньте турбуватися про те, що є, і починайте думати про те, що вони можуть робити .

Ви не тільки не можете написати окремий, щоб обробити кортеж порівняно зі списком, але і не хочете чи не потрібно .

Все, що ви робите, - скористатися тим, що вони обидва, наприклад, ітерабельні (тобто ви можете писати for element in container:). (Те, що вони не мають прямого спадкового зв’язку, не має значення.)


6
ТББ, я був би обережніший з "ніколи не потрібно". Це те, на що можна позначати кожну особливість будь-якого реального світу і вказувати на повну мову програмування, а тому не є коректним аргументом. Кому потрібні генератори? Кому потрібні заняття? Мови програмування - це лише синтаксичний цукор до чогось більш конкретного.
Себастьян Мах

6
Повністю не згоден. Можливо, вам "ніколи не потрібно було" або "ніколи не хотіли", але є достатньо додатків, де ви відчайдушно хочете. Спробуйте, наприклад, написати програму, яка витончено обробляє і Python, і numpy масиви, не засмічуючи програму, екземпляром ...
Elmar Zander,

1
Виходячи з відповіді масі, я б сказав, що "ти не можеш" зараз неправильно та застаріло. Виходячи з існування @overloadдекоратора, я б сказав, що "не дуже хочеться" є сперечальним, у кращому випадку. З PEP-3124, "... в даний час є загальним анти-шаблоном для коду Python для перевірки типів отриманих аргументів ..." очевидний спосіб "зробити це шляхом перевірки типу, але це крихко і закрито для розширення ... "Так здається, що так хотіли достатньо людей, що це стало частиною Python.
Майк S

@MikeS, стандарт @overloadпризначений лише для набору тексту.
Нарфанар

@Narfanar Я не знаю, як ваша відповідь стосується мого коментаря. Чи можете ви пояснити?
Майк S

16

Хоча @agf мав рацію з відповіддю в минулому, зараз PEP-3124 ми отримали синтаксичний цукор. Докладні відомості про @overloadдекоратор див. У документації щодо введення тексту, але зауважте, що це справді лише синтаксичний цукор та ІМХО. Це все, про що люди сперечаються з тих пір. Особисто я згоден , що наявність декількох функцій з різними підписами робить його більш зручним для читання , то маючи одну функції з аргументами 20+ всіх заданого значення за замовчуванням ( Noneбільшу частину часу) , а потім необхідності возитися з допомогою нескінченних if, elif, elseланцюгів , щоб з'ясувати , які абонент насправді хоче, щоб наша функція стосувалася наданого набору аргументів. Це було давно назріло після Python Zen

Красиве краще, ніж потворне.

і, можливо, також

Простий - краще, ніж складний.

Прямо з офіційної документації Python, зв'язаної вище:

from typing import overload
@overload
def process(response: None) -> None:
    ...
@overload
def process(response: int) -> Tuple[int, str]:
    ...
@overload
def process(response: bytes) -> str:
    ...
def process(response):
    <actual implementation>

саме те, що я шукав, акуратніше, ніж визначати власного декоратора перевантаження
pcko1

2
btw: для тих, хто цікавиться, чому це не працює, я б запропонував поглянути на обговорення в stackoverflow.com/questions/52034771/… - функції @overloadредакції не повинні мати реальної реалізації. Це не очевидно з прикладу в документації Python.
masi

15

Я пишу свою відповідь на Python 3.2.1.

def overload(*functions):
    return lambda *args, **kwargs: functions[len(args)](*args, **kwargs)

Як це працює:

  1. overloadбере будь-яку кількість дзвінків і зберігає їх у кортежі functions, після чого повертає лямбда.
  2. Лямбда приймає будь-яку кількість аргументів, після чого повертає результат функції виклику, що зберігається в виклику, functions[number_of_unnamed_args_passed]з аргументами, переданими лямбда.

Використання:

class A:
    stackoverflow=overload(                    \
        None, \ 
        #there is always a self argument, so this should never get called
        lambda self: print('First method'),      \
        lambda self, i: print('Second method', i) \
    )

14

Я думаю, що слово, яке ви шукаєте, - це «перевантаження». Немає методу перевантаження в python. Однак ви можете використовувати аргументи за замовчуванням, як показано нижче.

def stackoverflow(self, i=None):
    if i != None:     
        print 'second method', i
    else:
        print 'first method'

Коли ви передасте йому аргумент, він дотримуватиметься логіки першої умови та виконує перше твердження про друк. Коли ви не передасте йому ніяких аргументів, він перейде в elseумову і виконає друге твердження про друк.


13

Я пишу свою відповідь на Python 2.7:

У Python перевантаження методу неможливе; якщо ви дійсно хочете отримати доступ до тієї самої функції з різними функціями, я пропоную вам перейти до переосмислення методу.

class Base(): # Base class
    '''def add(self,a,b):
        s=a+b
        print s'''

    def add(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c

        sum =a+b+c
        print sum

class Derived(Base): # Derived class
    def add(self,a,b): # overriding method
        sum=a+b
        print sum



add_fun_1=Base() #instance creation for Base class
add_fun_2=Derived()#instance creation for Derived class

add_fun_1.add(4,2,5) # function with 3 arguments
add_fun_2.add(4,2)   # function with 2 arguments

9

У Python перевантаження не є прикладним поняттям. Однак якщо ви намагаєтесь створити випадок, коли, наприклад, ви хочете, щоб один ініціалізатор був виконаний, якщо передали аргумент типу, fooа інший ініціалізатор для аргументу типу bar, оскільки все в Python обробляється як об'єкт, ви можете перевірити назва класу типу переданого об'єкта і записувати умовне поводження на основі цього.

class A:
   def __init__(self, arg)
      # Get the Argument's class type as a String
      argClass = arg.__class__.__name__

      if argClass == 'foo':
         print 'Arg is of type "foo"'
         ...
      elif argClass == 'bar':
         print 'Arg is of type "bar"'
         ...
      else
         print 'Arg is of a different type'
         ...

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


7

У Python ви б це зробили за допомогою аргументу за замовчуванням.

class A:

    def stackoverflow(self, i=None):    
        if i == None:
            print 'first method'
        else:
            print 'second method',i

5

Щойно натрапив на цей https://github.com/bintoro/overloading.py для всіх, хто може зацікавити.

З readme пов'язаного сховища:

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

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

Особливості

Перевірка функції після реєстрації та детальні правила вирішення гарантують унікальний, чітко визначений результат під час виконання. Реалізує кешування роздільної здатності функцій для отримання великих показників. Підтримує необов’язкові параметри (значення за замовчуванням) у підписах функцій. Оцінює позиційні та ключові аргументи при вирішенні найкращої відповідності. Підтримує резервні функції та виконання спільного коду. Підтримує поліморфізм аргументів. Підтримує класи та успадкування, включаючи класові методи та статичні методи.


3

Python не підтримує такий метод перевантаження, як Java або C ++. Ми можемо перевантажувати методи, але можемо використовувати лише останній визначений метод.

# First sum method.
# Takes two argument and print their sum
def sum(a, b):
    s = a + b
    print(s)

# Second sum method
# Takes three argument and print their sum
def sum(a, b, c):
    s = a + b + c
    print(s)

# Uncommenting the below line shows an error    
# sum(4, 5)

# This line will call the second sum method
sum(4, 5, 5)

Нам потрібно надати необов'язкові аргументи або * аргументи, щоб надати різну кількість аргументів під час виклику.

Люб’язно від https://www.geeksforgeeks.org/python-method-overloading/


Це не перевантажує. Це називається перезаписом. Пізніший підтримується Python. Перший можна реалізувати за допомогою декораторів.
Paebbels

2

Python 3.x включає стандартну бібліотеку набору тексту, яка дозволяє перевантажувати метод із використанням декоратора @overload. На жаль, це зробить код більш читабельним, оскільки для методів @overload потрібно буде дотримуватися недекорований метод, який обробляє різні аргументи. Більше можна знайти тут, але для прикладу:

from typing import overload
from typing import Any, Optional
class A(object):
    @overload
    def stackoverflow(self) -> None:    
        print('first method')
    @overload
    def stackoverflow(self, i: Any) -> None:
        print('second method', i)
    def stackoverflow(self, i: Optional[Any] = None) -> None:
        if not i:
            print('first method')
        else:
            print('second method', i)

ob=A()
ob.stackoverflow(2)

1
В кінці вашої відповіді "З" змушує мене думати, що ви ще не закінчили писати свою відповідь. Будь ласка , змініть свій відповідь , щоб завершити його.
Artjom B.

0

У файлі MathMethod.py

from multipledispatch import dispatch
@dispatch(int,int)
def Add(a,b):
   return a+b 
@dispatch(int,int,int)  
def Add(a,b,c):
   return a+b+c 
@dispatch(int,int,int,int)    
def Add(a,b,c,d):
   return a+b+c+d

У файлі Main.py

import MathMethod as MM 
print(MM.Add(200,1000,1000,200))

Ми можемо перевантажити метод, використовуючи кратну диспетчеризацію


Це вимагає використання пакету multipledispatch ( pypi.org/project/multipledispatch ), який не є частиною ядра python.
Антоній

0

Python додав декоратор @overload з PEP-3124, щоб забезпечити синтаксичний цукор для перевантаження через перевірку типу - замість того, щоб просто працювати з перезаписом.

Приклад коду щодо перевантаження через @overload від PEP-3124

from overloading import overload
from collections import Iterable

def flatten(ob):
    """Flatten an object to its component iterables"""
    yield ob

@overload
def flatten(ob: Iterable):
    for o in ob:
        for ob in flatten(o):
            yield ob

@overload
def flatten(ob: basestring):
    yield ob

перетворюється @ перевантажувачем в:

def flatten(ob):
    if isinstance(ob, basestring) or not isinstance(ob, Iterable):
        yield ob
    else:
        for o in ob:
            for ob in flatten(o):
                yield ob
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.