Làm cách nào để sử dụng nạp chồng phương thức trong Python?


169

Tôi đang cố gắng thực hiện nạp chồng phương thức trong Python:

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

ob=A()
ob.stackoverflow(2)

nhưng đầu ra là second method 2; tương tự:

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

ob=A()
ob.stackoverflow()

cho

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

Làm thế nào để tôi thực hiện công việc này?


39
Trong Python, hãy nghĩ về các phương thức như một tập hợp " thuộc tính " đặc biệt và chỉ có thể có một " thuộc tính " (và do đó là một phương thức) của một tên đã cho cho một đối tượng. Phương thức cuối cùng ghi đè lên bất kỳ phương thức nào trước đó. Trong Java, các phương thức không phải là công dân hạng nhất (chúng không phải là "thuộc tính của các đối tượng"), mà được gọi bằng "gửi tin nhắn" được giải quyết tĩnh dựa trên loại gần nhất (đó là nơi quá tải xuất hiện).



6
Tại sao không có câu trả lời cho câu hỏi này được chấp nhận? Chỉ cần nhấp vào dấu kiểm nằm ngoài bên trái câu trả lời yêu thích của bạn ...
glglgl

Câu trả lời:


150

Đó là phương thức nạp chồng không phải phương thức ghi đè . Và trong Python, bạn làm tất cả trong một hàm:

class A:

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

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

Bạn không thể có hai phương thức có cùng tên trong Python - và bạn không cần phải làm như vậy.

Xem phần Giá trị đối số mặc định của hướng dẫn Python. Xem "Ít ngạc nhiên nhất" và Đối số mặc định có thể thay đổi để biết một lỗi phổ biến cần tránh.

Chỉnh sửa : Xem PEP 443 để biết thông tin về các hàm chung gửi đơn mới trong Python 3.4.


119
và bạn không cần phải - IMHO đôi khi sẽ rất tiện khi có quá tải phương thức, ví dụ như trong C ++. Ok, nó không phải là 'cần thiết' theo nghĩa là không thể thực hiện được bằng cách sử dụng các cấu trúc khác - nhưng nó sẽ làm cho một số thứ dễ dàng và đơn giản hơn.
Andreas Florath

7
@AndreasFlorath Tôi không đồng ý. Học cách yêu vịt gõ và viết từng phương thức để nó chỉ làm một việc, và không cần quá tải phương thức.
agf

4
+1 để bắt tôi đọc về "lỗi phổ biến cần tránh" trước khi tôi bị bắt
mdup

43
Tôi muốn không đồng ý một chút;) ... quá tải thường làm cho mã sạch hơn, bởi vì bạn không đóng gói phương thức với quá nhiều câu lệnh if-other để xử lý các trường hợp khác nhau. Theo một nghĩa nào đó, toàn bộ gam của các ngôn ngữ chức năng sử dụng ý tưởng tương tự, ví dụ như so khớp mẫu-đối số. Điều đó có nghĩa là bạn sẽ có các phương thức sạch nhỏ hơn .. thay vì các phương thức khổng lồ không thể đọc được.
sten

2
@ user1019129 Đó là mục đích của các hàm chung gửi đơn được thêm vào Python 3.4 và được liên kết trong câu trả lời của tôi.
agf

61

Bạn cũng có thể sử dụng 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
Tôi nghĩ đó là câu trả lời hợp lệ duy nhất cho câu hỏi. Tôi sẽ tăng gấp đôi nếu tôi có thể.
Michael

3
nó tốt, nhưng nó không hoạt động trên các hàm thô, chỉ là các phương thức trong một lớp.
ngăn xếp

1
@LegitStack Chức năng đó cũng có thể được thêm vào. Nó không phải là không thể.
Ehsan Keshavarzian

3
@LegitStack Tôi đã cập nhật mã trên GitHub, bây giờ nó cũng hoạt động với các chức năng.
Ehsan Keshavarzian

1
@Paulprice Đúng vậy. Tôi đã cập nhật câu trả lời của mình và xóa phần hỗ trợ chính thức. Bạn vẫn có thể sử dụng mã của tôi để gửi quá tải. Bây giờ nó hoạt động với cả phương thức và chức năng. Lấy mã từ GitHub. Tôi chưa cập nhật PyPi.
Ehsan Keshavarzian

48

Trong Python, bạn không làm mọi thứ theo cách đó. Khi mọi người làm điều đó trong các ngôn ngữ như Java, họ thường muốn có một giá trị mặc định (nếu không, họ thường muốn một phương thức có tên khác). Vì vậy, trong Python, bạn có thể có các giá trị mặc định .

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'

Như bạn có thể thấy, bạn có thể sử dụng điều này để kích hoạt hành vi riêng biệt thay vì chỉ có một giá trị mặc định.

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

2
Chủ yếu Nonelà hữu ích khi bạn muốn một giá trị mặc định có thể thay đổi. Hành vi riêng biệt nên được trong các chức năng riêng biệt.
agf

@agf: Nonecũng có thể hữu ích như một giá trị mặc định chính hãng.
Chris Morgan

Có, nhưng tôi đã đề cập đến việc sử dụng nó như một giá trị sentinel, đó là cách bạn sử dụng nó trong câu trả lời của bạn và như tôi nghĩ nhận xét của tôi cho thấy rõ.
agf

bạn nói "nói chung"? bạn đang ám chỉ điều này không phải lúc nào cũng vậy sao?
joel

24

Bạn không thể, không bao giờ cần và không thực sự muốn.

Trong Python, mọi thứ đều là một đối tượng. Các lớp học là những thứ, vì vậy chúng là đối tượng. Phương pháp cũng vậy.

Có một đối tượng được gọi Alà một lớp. Nó có một thuộc tính được gọi là stackoverflow. Nó chỉ có thể có một thuộc tính như vậy.

Khi bạn viết def stackoverflow(...): ..., điều xảy ra là bạn tạo một đối tượng là phương thức và gán nó cho stackoverflowthuộc tính của A. Nếu bạn viết hai định nghĩa, thì định nghĩa thứ hai sẽ thay thế định nghĩa thứ nhất, giống như cách gán luôn luôn hành xử.

Hơn nữa, bạn không muốn viết mã mà hoang dã hơn trong số những thứ mà quá tải đôi khi được sử dụng cho. Đó không phải là cách ngôn ngữ hoạt động.

Thay vì cố gắng xác định một chức năng riêng cho từng loại điều bạn có thể được cung cấp (điều này rất ít có ý nghĩa vì dù sao bạn không chỉ định loại cho tham số chức năng), hãy ngừng lo lắng về những điều đó gì và bắt đầu suy nghĩ về những gì chúng có thể làm .

Bạn không chỉ không thể viết một cái riêng để xử lý một tuple so với một danh sách, mà còn không muốn hoặc không cần .

Tất cả những gì bạn làm là tận dụng thực tế là cả hai, ví dụ, có thể lặp lại (tức là bạn có thể viết for element in container:). (Việc họ không liên quan trực tiếp đến việc thừa kế là không liên quan.)


6
TBH, tôi sẽ cẩn thận hơn với "không bao giờ cần". Đây là một cái gì đó có thể được gắn thẻ theo mọi tính năng của bất kỳ thế giới thực nào và sử dụng ngôn ngữ lập trình hoàn chỉnh, và do đó không phải là một đối số hợp lệ. Ai cần máy phát điện? Ai cần lớp học? Ngôn ngữ lập trình chỉ là cú pháp đường đến một cái gì đó cụ thể hơn.
Sebastian Mach

6
Hoàn toàn không đồng ý. Có thể là bạn "không bao giờ cần" hoặc "không bao giờ muốn", nhưng có đủ các ứng dụng mà bạn rất muốn. Ví dụ: thử viết một chương trình xử lý cả hai mảng Python và numpy một cách duyên dáng mà không xả rác chương trình của bạn bằng ...
Elmar Zander

1
Dựa trên câu trả lời của masi, tôi nói rằng "bạn không thể" bây giờ không chính xác và lỗi thời. Dựa trên sự tồn tại của người @overloadtrang trí, tôi muốn nói rằng "không thực sự muốn" là tốt nhất có thể tranh cãi. Từ PEP-3124, "... hiện tại đây là một kiểu chống phổ biến cho mã Python để kiểm tra các loại đối số nhận được ... 'cách rõ ràng' để thực hiện điều này là bằng cách kiểm tra loại, nhưng điều này dễ vỡ và bị đóng tiện ích mở rộng ... "Vì vậy, dường như đủ người muốn, rằng nó đã trở thành một phần của Python.
Mike S

@MikeS, tiêu chuẩn @overloadchỉ dành cho gõ.
Narfanar

@Narfanar Tôi không biết phản hồi của bạn áp dụng như thế nào đối với bình luận của tôi. Bạn có thể giải thích?
Mike S

16

Mặc dù @agf đã đúng với câu trả lời trong quá khứ với PEP-3124, chúng tôi đã nhận được cú pháp đường. Xem tài liệu đánh máy để biết chi tiết về @overloadtrang trí nhưng lưu ý rằng đây thực sự chỉ là cú pháp đường và IMHO đây là tất cả mọi người đã tranh cãi kể từ đó. Cá nhân tôi đồng ý rằng có nhiều chức năng có ký hiệu khác làm cho nó dễ đọc hơn sau đó có một chức năng duy nhất với hơn 20 đối số tất cả các thiết lập để một giá trị mặc định ( Nonehầu hết thời gian) và sau đó cần phải fiddle xung quanh sử dụng bất tận if, elif, elsechuỗi để tìm hiểu những gì người gọi thực sự muốn chức năng của chúng ta thực hiện với bộ đối số được cung cấp. Điều này đã quá hạn lâu sau Python Zen

Đẹp thì tốt hơn xấu.

và cũng có thể tranh luận

Đơn giản là tốt hơn phức tạp.

Trực tiếp từ tài liệu Python chính thức được liên kết ở trên:

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>

chính xác những gì tôi đang tìm kiếm, gọn gàng hơn là xác định trang trí quá tải của riêng mình
pcko1

2
btw: cho bất cứ ai tự hỏi tại sao điều này không làm việc tôi muốn đề nghị để có một cái nhìn tại các cuộc thảo luận trong stackoverflow.com/questions/52034771/... - các @overloadchức năng ed đang không được phép có bất cứ thực hiện thực tế. Điều này không rõ ràng từ ví dụ trong tài liệu Python.
Masi

15

Tôi viết câu trả lời của mình trong Python 3.2.1.

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

Làm thế nào nó hoạt động:

  1. overloadlấy bất kỳ số lượng các cuộc gọi và lưu trữ chúng trong tuple functions, sau đó trả lại lambda.
  2. Lambda nhận bất kỳ số lượng đối số nào, sau đó trả về kết quả của hàm gọi được lưu trữ trong lệnh functions[number_of_unnamed_args_passed]gọi với các đối số được truyền cho lambda.

Sử dụng:

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

Tôi nghĩ rằng từ bạn đang tìm kiếm là "quá tải". Không có phương pháp quá tải trong python. Tuy nhiên, bạn có thể sử dụng các đối số mặc định, như sau.

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

Khi bạn truyền cho nó một đối số, nó sẽ tuân theo logic của điều kiện đầu tiên và thực thi câu lệnh in đầu tiên. Khi bạn vượt qua nó không có đối số, nó sẽ đi vào elseđiều kiện và thực thi câu lệnh in thứ hai.


13

Tôi viết câu trả lời của mình bằng Python 2.7:

Trong Python, quá tải phương thức là không thể; nếu bạn thực sự muốn truy cập cùng chức năng với các tính năng khác nhau, tôi khuyên bạn nên dùng phương thức ghi đè.

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

Trong Python, quá tải không phải là một khái niệm được áp dụng. Tuy nhiên, nếu bạn đang cố gắng tạo một trường hợp, ví dụ, bạn muốn một trình khởi tạo được thực hiện nếu truyền một đối số kiểu foovà một trình khởi tạo khác cho một đối số kiểu bar, vì mọi thứ trong Python được xử lý như đối tượng, bạn có thể kiểm tra tên của loại lớp của đối tượng được thông qua và viết xử lý có điều kiện dựa trên đó.

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'
         ...

Khái niệm này có thể được áp dụng cho nhiều kịch bản khác nhau thông qua các phương pháp khác nhau khi cần thiết.


7

Trong Python, bạn sẽ làm điều này với một đối số mặc định.

class A:

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

5

Chỉ cần bắt gặp https://github.com/bintoro/overloading.py này cho bất kỳ ai có thể quan tâm.

Từ readme của kho lưu trữ được liên kết:

nạp chồng là một mô-đun cung cấp chức năng gửi dựa trên các loại và số lượng đối số thời gian chạy.

Khi một hàm quá tải được gọi, bộ điều phối sẽ so sánh các đối số được cung cấp với các chữ ký hàm có sẵn và gọi việc thực hiện cung cấp khớp chính xác nhất.

Đặc trưng

Xác thực chức năng khi đăng ký và quy tắc giải quyết chi tiết đảm bảo một kết quả duy nhất, được xác định rõ khi chạy. Triển khai bộ nhớ đệm giải quyết chức năng cho hiệu suất tuyệt vời. Hỗ trợ các tham số tùy chọn (giá trị mặc định) trong chữ ký hàm. Đánh giá cả đối số vị trí và từ khóa khi giải quyết kết quả phù hợp nhất. Hỗ trợ chức năng dự phòng và thực thi mã chia sẻ. Hỗ trợ đa hình đối số. Hỗ trợ các lớp và kế thừa, bao gồm cả classmethod và staticmethod.


3

Python không hỗ trợ nạp chồng phương thức như Java hay C ++. Chúng tôi có thể quá tải các phương thức nhưng chỉ có thể sử dụng phương thức được xác định mới nhất.

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

Chúng ta cần cung cấp các đối số tùy chọn hoặc * args để cung cấp số lượng đối số khác nhau khi gọi.

Phép lịch sự từ https://www.geekforgeek.org/python-method-overloading/


Đây không phải là quá tải. Nó được gọi là ghi đè. Cái sau được Python hỗ trợ. Việc đầu tiên có thể được thực hiện với trang trí.
Paebbels

2

Python 3.x bao gồm thư viện gõ tiêu chuẩn cho phép nạp chồng phương thức với việc sử dụng trang trí @overload. Thật không may, điều này là để làm cho mã dễ đọc hơn, vì các phương thức trang trí @overload sẽ cần phải được theo sau bởi một phương thức không được trang trí để xử lý các đối số khác nhau. Nhiều hơn có thể được tìm thấy ở đây nhưng ví dụ của bạn:

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" ở cuối câu trả lời của bạn khiến tôi nghĩ rằng bạn chưa viết xong câu trả lời của mình. Vui lòng chỉnh sửa câu trả lời của bạn để hoàn thành nó.
Artjom B.

0

Trong tệp 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

Trong tệp Main.txt

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

Chúng ta có thể quá tải phương thức bằng cách sử dụng đa bội


Điều này yêu cầu sử dụng gói đa bội ( pypi.org/project/multipledispatch ), không phải là một phần của lõi python.
Antony

0

Python đã thêm trình trang trí @overload với PEP-3124 để cung cấp đường cú pháp cho quá tải thông qua kiểm tra loại - thay vì chỉ làm việc với ghi đè.

Ví dụ mã về quá tải thông qua @overload từ 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

được chuyển đổi bởi @ quá tải-trang trí thành:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.