Python中一个类中具有相同名称的方法

Posted

技术标签:

【中文标题】Python中一个类中具有相同名称的方法【英文标题】:Methods with the same name in one class in Python 【发布时间】:2011-07-02 01:33:34 【问题描述】:

如何在一个类中声明几个同名但参数数量不同或类型不同的方法?

我必须在下面的课程中改变什么?

class MyClass:
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
    def my_method(self,parameter_A_that_Must_Be_String):
        print parameter_A_that_Must_Be_String

    def my_method(self,parameter_A_that_Must_Be_String,parameter_B_that_Must_Be_String):
        print parameter_A_that_Must_Be_String
        print parameter_B_that_Must_Be_String

    def my_method(self,parameter_A_that_Must_Be_String,parameter_A_that_Must_Be_Int):
        print parameter_A_that_Must_Be_String * parameter_A_that_Must_Be_Int

【问题讨论】:

您可能想阅读此内容。 dirtsimple.org/2004/12/python-is-not-java.html 和这个dirtsimple.org/2004/12/java-is-not-python-either.html 在试验了一会儿之后,我发现在Python中编写多个同名的函数似乎是有效的,但是每次编写另一个同名的函数时,解释器完全忘记了具有该名称的先前函数。 相关:functools.singledispatch @TomWillis 我认为您链接的标题应该说“Python 不是 OOP 语言。”多态性不是 Java 概念。它是 OOP 的 4~5 个核心原则之一。曾经有一段时间,这些被认为是一种语言需要公开的最小 api,以便称自己为面向对象。半实现的功能集只是通过强制变通方法来要求反模式。我仍然喜欢这种语言。它只需要选择一条车道并保持车轮稳定。 【参考方案1】:

您可以拥有一个接受可变数量参数的函数。

def my_method(*args, **kwds):
    # Do something

# When you call the method
my_method(a1, a2, k1=a3, k2=a4)

# You get:
args = (a1, a2)
kwds = 'k1':a3, 'k2':a4

所以你可以修改你的函数如下:

def my_method(*args):
    if len(args) == 1 and isinstance(args[0], str):
        # Case 1
    elif len(args) == 2 and isinstance(args[1], int):
        # Case 2
    elif len(args) == 2 and isinstance(args[1], str):
        # Case 3

【讨论】:

这是正确的方法,除了你应该在故障安全情况下引发异常。像else: raise TypeError('Parameter 1 should be a string and parameter 2 should be a string, int or omitted entirely') 这样的东西。而且,正如@delnan 所说,使用basestring 而不是str,除非你有充分的理由不想要unicode。 谢谢!我迟到了几年,但这解决了我对同一方法的不同签名的头痛(子类覆盖父类)。【参考方案2】:

你不能。没有重载或多方法或类似的东西。一个名字代表一件事。无论如何,就语言而言,您始终可以自己模拟它们...您可以使用isinstance检查类型(但请正确执行 - 例如在Python 2中,使用basestring检测字符串和unicode),但它很丑陋,通常不鼓励并且很少有用。如果这些方法做不同的事情,给它们起不同的名字。还要考虑多态性。

【讨论】:

如果您考虑 python 中的多态性,那么您将返回使用 isinstance() 和其他 type() 检查,不是吗?你是什​​么意思python中的多态性? @RetroCode 我的意思是面向对象编程中的多态性,即一组具有相同方法的类(可能有些继承自其他类,但不一定在 Python 中由于鸭子类型)具有兼容签名的名称。 当我可以做 get_user(id) 和 get_user(name) 重载时,我觉得不需要做类似的事情:get_user_by_id(id) 和 get_user_by_name(name)。参数列表用于区分两者,清晰简洁。如果不重载,您可以获得荒谬的函数名称。我希望 Python 将来会增加重载。【参考方案3】:

使用 Python 3.5 或更高版本,您可以使用 @typing.overload 为重载的函数/方法提供类型注释。

From the docs:

@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>

【讨论】:

【参考方案4】:

简短回答:你不能 (see this previous discussion)。通常你会使用类似的东西(你可以添加更多的类型检查和重新排序):

def my_method(self,parameter_A, parameter_B=None):
  if isinstance(parameter_B, int):
    print parameter_A * parameter_B
  else:
    print parameter_A
    if parameter_B is not None:
      print parameter_B

【讨论】:

【参考方案5】:

您可以在 Python 中尝试多种方法:

http://www.artima.com/weblogs/viewpost.jsp?thread=101605

但我不相信多方法是一种可行的方法。相反,您传递给方法的对象应该具有公共接口。您正在尝试实现类似于 C++ 中的方法重载,但在 Python 中很少需要它。一种方法是使用isinstance 级联ifs,但这很难看。

【讨论】:

这是 IMO 的最佳解决方案(也是唯一真正的解决方案)。一个包可用pypi.org/project/multimethod。【参考方案6】:

Python 与 Java 完全不同。

没有真正的类型,只有带有方法的对象。

有一种方法可以测试传递的对象是否来自某个类,但这主要是不好的做法。

但是,您要为前两种方法生成的代码应该类似于

class MyClass(object):
    def my_method(self, str1, str2=None):
        print str1
        if str2: print str2

对于第三个,好吧...使用不同的名称...

【讨论】:

(1) 有类型,只是不要通过静态类型来固定名称的类型。 (2) if x 是检查None 的可怕方式。它确实检查x 是否“虚假”。 (3) 可选参数不适用于示例,也不适用于一般的重载。【参考方案7】:

这行不通。无论您有多少参数,名称 m 都将被第二个 m 方法覆盖。

class C:
    def m(self):
        print('m first')
    def m(self, x):
        print(f'm second x')


ci=C();
#ci.m() # will not work TypeError: m() missing 1 required positional argument: 'x'
ci.m(1) # works

输出很简单:

m second 1

【讨论】:

【参考方案8】:

您可能需要类似于以下的模式: 请注意,将“_”添加到方法名称的开头是标记私有方法的约定。

class MyClass:
    """"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
    def my_method(self,parameter_A_that_Must_Be_String, param2=None):
        if type(param2) == str:
            return self._my_method_extra_string_version(parameter_A_that_Must_Be_String, param2)
        elif type(param2) == int:
            return self._my_method_extra_int_version(parameter_A_that_Must_Be_String, param2)
        else:
            pass # use the default behavior in this function
        print parameter_A_that_Must_Be_String

    def _my_method_extra_string_version(self,parameter_A_that_Must_Be_String, parameter_B_that_Must_Be_String):
        print parameter_A_that_Must_Be_String
        print parameter_B_that_Must_Be_String

    def _my_method_extra_int_version(self,parameter_A_that_Must_Be_String, parameter_A_that_Must_Be_Int):
        print parameter_A_that_Must_Be_String * parameter_A_that_Must_Be_Int

【讨论】:

这对于python来说可能有点过度设计。您可能希望开始该方法,将 param2 设置为默认值,然后在方法开始时根据需要将其更改为字符串或 int。【参考方案9】:
class MyClass:
    def __init__(this, foo_str, bar_int):
        this.__foo = foo_str
        this.__bar = bar_int

    def foo(this, new=None):
        if new != None:
            try:
                this.__foo = str(new)
            except ValueError:
                print("Illegal value. foo unchanged.")

        return this.__foo

    def bar(this, new=None):
        if new != None:
            try:
                this.__bar = int(new)
            except ValueError:
                print("Illegal value. bar unchanged.")

        return this.__bar

obj = MyClass("test", 42)
print(obj.foo(), obj.bar())

print(obj.foo("tset"), obj.bar(24))

print(obj.foo(42), obj.bar("test"))

Output:
    test 42
    tset 24
    Illegal value. bar unchanged.
    42 24

【讨论】:

解释一下。【参考方案10】:

我认为所有答案都缺少一个非常简单的例子,那就是:当方法的变体之间的唯一区别是参数的数量时该怎么办。答案仍然是使用具有可变数量参数的方法。

比如说,你从一个需要使用两个参数的方法开始

def method(int_a, str_b):
    print("Got arguments: '0' and '1'".format(int_a, str_b)

那么你需要添加一个只有第二个参数的变体(比如说,因为整数是多余的),解决方案很简单:

def _method_2_param(int_a, str_b):
    print("Got arguments: '0' and '1'".format(int_a, str_b))

def _method_1_param(str_b):
    print("Got argument: '0'".format(str_b))

def method(*args, **kwargs):
    if len(args) + len(kwargs) == 2:
        return _method_2_param(args, kwargs)
    elif len(args) + len(kwargs) == 1:
        return _method_1_param(args, kwargs)
    else:
        raise TypeError("Method requires one or two arguments")

这个解决方案的好处是,无论调用代码之前使用关键字参数还是位置参数,它仍然可以继续工作。

【讨论】:

以上是关于Python中一个类中具有相同名称的方法的主要内容,如果未能解决你的问题,请参考以下文章

C#中具有相同名称和签名但返回类型不同的方法

C#中具有相同名称和签名但返回类型不同的方法

如何从具有相同简单名称的类中查找某个方法?

派生类中具有相同名称但不同签名的函数

java的重写

在Python编程中覆盖具有相同名称的方法[重复]