【问题标题】:Methods with the same name in one class in PythonPython中一个类中具有相同名称的方法
【发布时间】: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

【问题讨论】:

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

标签: python overloading


【解决方案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,但这很难看。

      【讨论】:

      【解决方案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")
        

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

        【讨论】:

          猜你喜欢
          • 2013-01-21
          • 2018-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-04
          • 1970-01-01
          • 2013-06-13
          • 2020-03-20
          相关资源
          最近更新 更多