ฉันจะใช้วิธีการโอเวอร์โหลดใน 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 ให้คิดว่าเมธอดเป็นชุดพิเศษของ " attribute " และสามารถมีได้เพียง " attribute " (และหนึ่งวิธี) ของชื่อที่กำหนดสำหรับวัตถุ วิธีสุดท้ายเขียนทับวิธีก่อนหน้านี้ ใน Java วิธีการไม่ได้เป็นพลเมืองชั้นแรก (พวกเขาจะไม่ได้ "คุณลักษณะของวัตถุ") แต่ค่อนข้างจะเรียกโดย "ส่งข้อความ" ที่มีกำลังการแก้ไขแบบคงที่ขึ้นอยู่กับชนิดที่ใกล้เคียงที่สุด (ซึ่งเป็นที่ที่มากไปมาใน)



6
เหตุใดจึงไม่มีคำตอบของคำถามนี้ที่ยอมรับ เพียงคลิกที่เครื่องหมาย outlied ด้านซ้ายของคำตอบที่คุณชื่นชอบ ...
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 ++ ตกลงไม่จำเป็นในแง่ที่ว่ามันไม่สามารถทำได้โดยใช้สิ่งก่อสร้างอื่น - แต่มันจะทำให้บางสิ่งง่ายขึ้นและง่ายขึ้น
Andreas Florath

7
@AndreasFlorath ฉันไม่เห็นด้วย เรียนรู้ที่จะรักการพิมพ์เป็ดและเขียนแต่ละวิธีเพื่อทำสิ่งเดียวเท่านั้นและไม่จำเป็นต้องใช้วิธีการมากไป
agf

4
+1 ที่ทำให้ฉันอ่านเกี่ยวกับ "ข้อผิดพลาดทั่วไปที่ควรหลีกเลี่ยง" ก่อนที่ฉันจะถูกจับได้
mdup

43
ฉันต้องการที่จะไม่เห็นด้วยเล็กน้อย;) ... การโหลดบ่อยเกินไปทำให้โค้ดสะอาดขึ้นเพราะคุณไม่ได้แพ็ควิธีที่มีคำสั่ง if-else มากเกินไปเพื่อจัดการกับกรณีที่แตกต่างกัน ในแง่หนึ่งขอบเขตทั้งหมดของภาษาเชิงหน้าที่ใช้ความคิดที่คล้ายกันนั่นคือการจับคู่รูปแบบการโต้แย้ง ซึ่งหมายความว่าคุณจะมีวิธีการที่สะอาดกว่าเล็กน้อยกว่า .. แทนที่จะเป็นวิธีที่อ่านไม่ได้ขนาดยักษ์
เตน

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
ฉันคิดว่านั่นเป็นคำตอบเดียวที่ถูกต้องสำหรับคำถาม ฉันจะเพิ่มขึ้นเป็นสองเท่าถ้าทำได้
Michael

3
เป็นเรื่องที่ดี แต่มันใช้งานไม่ได้กับฟังก์ชั่นดิบเพียงแค่วิธีการภายในห้องเรียน
Legit Stack

1
@LegitStack สามารถเพิ่มฟังก์ชันการทำงานได้ มันเป็นไปไม่ได้
Ehsan Keshavarzian

3
@LegitStack ฉันอัปเดตรหัสบน GitHub ตอนนี้มันใช้ได้กับฟังก์ชั่นด้วย
Ehsan Keshavarzian

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สามารถใช้เป็นค่าเริ่มต้นของแท้ได้
Chris Morgan

ใช่ แต่ฉันหมายถึงใช้มันเป็นค่าแมวมองซึ่งเป็นวิธีที่คุณใช้ในคำตอบของคุณและฉันคิดว่าความคิดเห็นของฉันชัดเจน
agf

คุณพูดว่า "โดยทั่วไป"? คุณหมายถึงสิ่งนี้ไม่ได้เป็นอย่างนั้นเหรอ?
joel

24

คุณไม่สามารถไม่จำเป็นต้องและไม่ต้องการจริงๆ

ใน Python ทุกอย่างเป็นวัตถุ คลาสเป็นสิ่งต่าง ๆ ดังนั้นมันจึงเป็นวัตถุ ดังนั้นวิธีการคือ

มีวัตถุที่เรียกว่าAซึ่งเป็นชั้นเรียน stackoverflowมันมีแอตทริบิวต์ที่เรียกว่า มันสามารถมีคุณลักษณะดังกล่าวได้เพียงหนึ่งรายการเท่านั้น

เมื่อคุณเขียนdef stackoverflow(...): ...สิ่งที่เกิดขึ้นคือการที่คุณสร้างวัตถุซึ่งเป็นวิธีการและกำหนดให้แอตทริบิวต์ของstackoverflow Aหากคุณเขียนคำจำกัดความสองคำนิยามที่สองจะแทนที่คำสั่งแรกแบบเดียวกับที่การบ้านมอบหมายให้ทำเช่นนั้นเสมอ

นอกจากนี้คุณไม่ต้องการเขียนโค้ดที่เป็นตัวสร้างสิ่งต่าง ๆ ที่บางครั้งการใช้งานมากเกินไป นั่นไม่ใช่วิธีการทำงานของภาษา

แทนการพยายามที่จะกำหนดฟังก์ชั่นแยกต่างหากสำหรับแต่ละประเภทของสิ่งที่คุณจะได้รับ (ซึ่งทำให้ความรู้สึกเล็ก ๆ น้อย ๆ เนื่องจากคุณไม่ระบุประเภทสำหรับพารามิเตอร์ฟังก์ชั่นต่อไป), หยุดกังวลเกี่ยวกับสิ่งที่มีและเริ่มคิดเกี่ยวกับสิ่งที่พวกเขาสามารถทำได้

คุณไม่เพียง แต่ไม่สามารถเขียนหนึ่งแยกต่างหากที่จะจัดการกับ tuple รายการ แต่ยังไม่ต้องการหรือจำเป็นที่จะต้อง

สิ่งที่คุณทำคือใช้ประโยชน์จากความจริงที่ว่าทั้งคู่มีตัวอย่างเช่น iterable (เช่นคุณสามารถเขียนได้for element in container:) (ความจริงที่ว่าพวกเขาไม่ได้เกี่ยวข้องโดยตรงจากการรับมรดกนั้นไม่เกี่ยวข้อง)


6
TBH ฉันจะระวังตัวมากขึ้นด้วย "ไม่จำเป็น" นี่คือสิ่งที่สามารถติดแท็กในทุก ๆ คุณสมบัติของโลกแห่งความเป็นจริงและภาษาการเขียนโปรแกรมที่สมบูรณ์และดังนั้นจึงไม่ใช่ข้อโต้แย้งที่ถูกต้อง ใครต้องการกำเนิด? ใครต้องการเรียน? ภาษาการเขียนโปรแกรมเป็นเพียงน้ำตาลประโยคกับสิ่งที่เป็นรูปธรรมมากขึ้น
เซบาสเตียนมัค

6
ไม่เห็นด้วยอย่างสิ้นเชิง อาจเป็นได้ว่าคุณ "ไม่จำเป็นต้อง" หรือ "ไม่ต้องการ" แต่มีแอปพลิเคชันเพียงพอที่คุณต้องการอย่างยิ่ง ลองเช่นการเขียนโปรแกรมที่จัดการทั้ง Python และ numpy arrays ได้อย่างงดงามโดยไม่ทำให้โปรแกรมของคุณพังด้วย ...
Elmar Zander

1
จากคำตอบของ masi ฉันจะบอกว่า "คุณทำไม่ได้" ตอนนี้ไม่ถูกต้องและล้าสมัย จากการมีอยู่ของ@overloadมัณฑนากรฉันจะบอกว่า "ไม่ต้องการ" เป็นสิ่งที่ดีที่สุด จาก PEP-3124 "... ปัจจุบันเป็นรูปแบบการต่อต้านแบบทั่วไปสำหรับรหัส Python เพื่อตรวจสอบประเภทของอาร์กิวเมนต์ที่ได้รับ ... 'วิธีที่ชัดเจน' ในการทำเช่นนี้คือโดยการตรวจสอบประเภท แต่นี่เปราะและปิดเพื่อ ส่วนขยาย ... "ดูเหมือนว่าผู้คนต้องการมากพอที่จะกลายเป็นส่วนหนึ่งของ Python
Mike S

@MikeS มาตรฐาน@overloadสำหรับการพิมพ์เท่านั้น
Narfanar

@Narfanar ฉันไม่ทราบว่าการตอบสนองของคุณมีผลกับความคิดเห็นของฉันอย่างไร คุณช่วยอธิบายได้ไหม
Mike S

16

ในขณะที่ @agf ถูกต้องกับคำตอบในอดีตตอนนี้ด้วยPEP-3124เราได้รับน้ำตาลของเรา ดูการพิมพ์เอกสารเพื่อดูรายละเอียด เกี่ยวกับ@overloadมัณฑนากร แต่โปรดทราบว่านี่เป็นเพียงไวยากรณ์น้ำตาลและ IMHO นี่คือทุกคนได้รับการโต้เถียงกันมานับตั้งแต่ ส่วนตัวผมเห็นว่ามีหลายฟังก์ชั่นที่มีลายเซ็นที่แตกต่างกันทำให้มันอ่านได้มากขึ้นแล้วมีฟังก์ชั่นเดียวกับข้อโต้แย้ง 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>

สิ่งที่ฉันกำลังมองหาผู้
ชนะเลิศ

2
btw: สำหรับทุกคนที่สงสัยว่าทำไมสิ่งนี้ถึงไม่ทำงานฉันขอแนะนำให้ดูการอภิปรายในstackoverflow.com/questions/52034771/… - @overloadฟังก์ชั่น ed ไม่ควรมีการใช้งานจริง สิ่งนี้ไม่ชัดเจนจากตัวอย่างในเอกสาร Python
masi

15

ฉันเขียนคำตอบใน Python 3.2.1

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

มันทำงานอย่างไร:

  1. overloadรับ callables ได้ไม่ จำกัด จำนวนและเก็บไว้ใน tuple 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

ฉันคิดว่าคำที่คุณกำลังมองหาคือ "การบรรทุกเกินพิกัด" ไม่มีวิธีการมากไปในงูหลาม อย่างไรก็ตามคุณสามารถใช้อาร์กิวเมนต์เริ่มต้นได้ดังนี้

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 การใช้งานมากเกินไปไม่ใช่แนวคิดที่ใช้ อย่างไรก็ตามหากคุณพยายามสร้างกรณีที่คุณต้องการให้ initializer หนึ่งตัวดำเนินการหากผ่านการโต้แย้งชนิด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 ของที่เก็บที่เชื่อมโยง:

โอเวอร์โหลดเป็นโมดูลที่ให้ฟังก์ชั่นการจัดส่งขึ้นอยู่กับชนิดและจำนวนของการขัดแย้งรันไทม์

เมื่อมีการเรียกใช้ฟังก์ชันโอเวอร์โหลดดิสแพตเชอร์จะเปรียบเทียบอาร์กิวเมนต์ที่ให้มากับลายเซ็นของฟังก์ชันที่มีอยู่และเรียกใช้งานที่ให้การจับคู่ที่แม่นยำที่สุด

คุณสมบัติ

การตรวจสอบฟังก์ชั่นเมื่อลงทะเบียนและกฎการแก้ปัญหาโดยละเอียดรับประกันผลลัพธ์ที่ไม่ซ้ำใครและชัดเจนในขณะทำงาน ใช้การแคชความละเอียดของฟังก์ชันเพื่อประสิทธิภาพที่ยอดเยี่ยม รองรับพารามิเตอร์ทางเลือก (ค่าเริ่มต้น) ในการทำงานของลายเซ็น ประเมินทั้งอาร์กิวเมนต์ตำแหน่งและคำหลักเมื่อแก้ไขการจับคู่ที่ดีที่สุด รองรับฟังก์ชั่นสำรองและการใช้งานรหัสที่ใช้ร่วมกัน รองรับการโต้แย้งที่หลากหลาย รองรับคลาสและการสืบทอดรวมถึง classmethods และ staticmethods


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)

เราจำเป็นต้องระบุอาร์กิวเมนต์เพิ่มเติมหรือ * args เพื่อระบุจำนวน args ที่แตกต่างกันในการโทร

ได้รับความอนุเคราะห์จากhttps://www.geeksforgeeks.org/python-method-overloading/


นี่ไม่ใช่การบรรทุกเกินพิกัด มันเรียกว่าการเขียนทับ Python รองรับในภายหลัง ครั้งแรกที่สามารถนำมาใช้กับการตกแต่ง
Paebbels

2

Python 3.x มีไลบรารี่การพิมพ์มาตรฐานที่อนุญาตให้เมธอดโอเวอร์โหลดด้วยการใช้ @overload decorator น่าเสียดายที่นี่คือการทำให้โค้ดอ่านง่ายขึ้นเนื่องจากวิธีการตกแต่ง @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
คำว่า "The" ท้ายคำตอบของคุณทำให้ฉันคิดว่าคุณยังไม่ได้เขียนคำตอบของคุณ โปรดแก้ไขคำตอบของคุณเพื่อให้สมบูรณ์
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


สิ่งนี้ต้องการการใช้แพคเกจ multipledispatch ( pypi.org/project/multipledispatch ) ซึ่งไม่ได้เป็นส่วนหนึ่งของ python core
แอนโทนี

0

Python เพิ่ม @overload decorator ด้วยPEP-3124เพื่อให้น้ำตาล syntactic สำหรับการบรรทุกเกินพิกัดผ่านการตรวจสอบประเภท - แทนที่จะทำงานกับการเขียนทับ

ตัวอย่างรหัสการโอเวอร์โหลดผ่าน @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

ถูกแปลงโดย @ overload-decorator เป็น:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.