【问题标题】:inheritance in python - how to make a method both a classmethod and a regular method?python中的继承-如何使方法既是类方法又是常规方法?
【发布时间】:2015-04-16 05:51:16
【问题描述】:

假设我在一个类中有一个方法,其结构如下:

class TestClass(object):
   ...
   def __init__(self, ...)
       ...
       self.var1 = ...
       self.var2 = ...
       ...
   ...
   def xyz(self, other)
       if isinstance(other, TestClass):
            ...
            self.var1 = ...
            ...
       else:
            ...
            self.var2 = ...
            ...

现在假设我想编写另一个继承自 TestClass 的类 NewTestClass(即 class NewTestClass(TestClass): ... )。

xyz 方法的一个问题是它显式检查 other 变量是否是 TestClass 的实例,并且它还引用实例的属性(即 self.var1self.var2)。

(a) 如何重写方法 xyz 以便在继承时(在 NewTestClass 中),if 语句的行为类似于 if isinstance(other, NewTestClass): ...(而且我不必重写方法 xyzNewTestClass) 内?

(b) 同样,假设我在TestClass 中有另一个method,它引用TestClass 中的staticmethod,例如:

@staticmethod
def abc(x):
    ...
    val = ...
    ...
    return val

def mnp(self, ...):
    ...
    x1 = ...
    self.var1 = TestClass.abc(x1)
    ...

这里,mnp 方法使用了TestClassstaticmethod(即本例中的TestClass.abc)。如何重写TestClass.abc,以便当NewTestClass 继承TestClass 时,它会获得一个方法mnpself.var = 行视为等同于self.var1 = NewTestClass.abc(x1)(而且我不必重写mnp 内的方法 NewTestClass) ?

【问题讨论】:

    标签: python inheritance python-3.x static-methods


    【解决方案1】:

    一)

    def xyz(self, other):
        if isinstance(other, self.__class__):
           ...
    

    b) 您可以使用self 调用静态类方法:

        def mnp(self, ...):
            ....
            self.var1 = self.abc(...)
    

    【讨论】:

    • 对不起,我没有意识到你之前回答过。感谢您的回答,我标记了您的答案。我对你和@lockey 都投了赞成票。另外,isinstance(x, TestClass) 内的 classmethod 相当于什么?我应该写isinstance(x, cls.__class__) 还是isinstance(x, cls)
    • 我会说,isinstance(x, cls) 但我没有测试自己
    【解决方案2】:

    对于(a)部分,需要检查other是否是与self同类的实例:

    class TestClass(object):
      def xyz(self, other):
        if isinstance(other, self.__class__):
          ...
          self.var1 = ...
          ...
        else:
          ...
          self.var2 = ...
          ...
    

    在可能的情况下,通常认为避免isinstance 这种逻辑更“pythonic”。考虑使用 Python 的鸭子类型。以下示例并不完全符合您的需要,因为这两个属性都存在于您的类中。但是,如果您为实际代码选择适当的异常,则此技术将起作用。

    class TestClass(object):
      def xyz(self, other):
        try:
          self.var1 = ...   # The minimum code required to raise the exception
        except AttributeError:
          self.var2 = ...
        else:
          pass              # Any other code if the exception is not raised
    

    Python 的异常处理是轻量级的,旨在以这种方式使用。如果传入了一些意想不到的东西(甚至可能还没有考虑到),它会让你的代码更有弹性。

    对于(b)部分,调用静态方法时不需要指定类。可以改为在实例 (self) 上调用它。

    class TestClass(object):
      @staticmethod
      def abc(x):
        return x  # Do something useful
    
      def mnp(self, x):
        self.var1 = self.abc(x)
    

    现在当你在派生类中声明一个新的静态方法时,它会被正确调用:

    class NewTestClass(TestClass):
      @staticmethod
      def abc(x):
        return x * 2  # Do something else useful
    
    a = NewTestClass()
    a.mnp(1)
    assert a.var1 == 2  # because NewTestClass.abc was called by mnp
    

    【讨论】:

    • classmethod 内有什么可能相当于isinstance(x, TestClass)?我应该写isinstance(x, cls.__class__) 还是isinstance(x, cls)
    • 它是isinstance(x,cls) @mguijarr 上面说的。 cls.__class__type(请参阅元类文档)
    【解决方案3】:

    你可以做一些丑陋的反射来让它工作,但这不是正确的做法。如果这些类假设自然地相互扩展 - 这样做,如果不是 - 不要。

    也就是说,你可以做的一件事是改变当前的实现,而不是分配一个 var - 调用另一个这样做的函数,并在每个类中实现这个函数来修改正确的变量。

    例如,改变:

    def xyz(self, other)
       if isinstance(other, TestClass):
            ...
            self.var1 = ...
            ...
       else:
            ...
            self.var2 = ...
            ...
    

    到:

    def xyz(self, other)
       if isinstance(other, TestClass):
            ...
            self.modifyVar(value)
            ...
       else:
            ...
            self.var2 = ...
            ...
    

    然后你将在一个类中实现:

    def modifyVar(self, value):
        self.var1 = value
    

    在另一堂课中,您将拥有:

    def modifyVar(self, value):
        self.var2 = value
    

    此答案涵盖两种情况(静态和非静态函数)。

    【讨论】:

    • 不确定你是否理解我的问题。我想将if isinstance(other, TestClass) 行更改为就像TestClass 内的if isinstance(other, TestClass)NewTestClass 内的if isinstance(other, NewTestClass) 一样,这是TestClass 的继承。我对self.var1self.var2 并不在意——我只关心这两行本可以做任何需要实例的事情
    • 所以改成:if isinstance(other, TestClass) or isinstance(other, NewTestClass):
    • 如果将来我创建更多子类 - ModifiedTestClass, AnotherTestClass 怎么办?如果我按照您的建议进行操作,我将不得不继续更改 TestClass 中的代码...
    • @uday 为每个类添加 try/except 比添加 isinstance 检查更容易吗?就个人而言 - 我不喜欢这两种解决方案。我将通过创建另一个将由所有类继承的函数来解决它:isATestClass() 并且始终返回 true。我认为它会更干净。
    猜你喜欢
    • 2018-07-26
    • 2019-04-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-04
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多