【问题标题】:How to define one method as both instance method and class method (sharing the same name), each with different arguments?如何将一个方法定义为实例方法和类方法(共享相同名称),每个方法都有不同的参数?
【发布时间】:2021-10-13 13:36:25
【问题描述】:

本质上,我正在尝试完成以下操作,但 bar 和 baz 具有相同的句柄(例如,只是 bar),而不是两个不同名称的函数。

定义
import numpy as np
foo = np.add # For example; real function will be different in every instance

class MyClass(object):
    def __init__(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

    def bar(self):
        return foo(self.arg1, self.arg2)

    @classmethod
    def baz(cls, arg1, arg2):
        return foo(arg1, arg2)
示例用法
a, b = 1, 2
mine = MyClass(a, b)
print(mine.bar())
>>> 3
x, y = 3, 4
print(MyClass.baz(x, y))
>>> 7

为了便于理解,我正在尝试这样做。真正的函数名称很长,带有许多下划线,并且为我想要执行的每个函数创建两个名称略有不同的函数(例如,在一个函数名称前加上_)只会混淆已经很复杂的情况。

该函数将主要在内部使用,但我希望能够在静态上下文中调用该函数,其临时参数可能不一定与MyClass 的给定对象的实例变量匹配(事实上,如果它们不匹配,我只会这样称呼它)。我使用的是@classmethod 而不是@staticmethod,因为真正的函数使用了一些内部类变量。

我已经尝试使用bar 简单地实现上述两个函数名称,但正如预期的那样,实例方法已被类方法覆盖。

  • 我想一个尴尬的解决方案是将bar定义为:

    def bar(self, arg1=None, arg2=None):
        if arg1 is None:
            arg1 = self.arg1
        if arg2 is None:
            arg2 = self.arg2
        return foo(arg1, arg2)
    

    然后,如果我想在其他地方静态调用它,我需要使用:

    # Assuming that a specific object of MyClass does not exist yet
    useless = "not", "used"
    mine = MyClass(*useless)
    
    x, y = 6, 9
    print(mine.bar(x, y))
    

    我不太喜欢这个,因为它需要我从MyClass 的特定对象调用bar。如果我在某处已经有MyClass 的实例,这在技术上不是问题,但我想要一个类方法是有原因的:bar 在这种情况下不依赖于任何实例变量(尽管它依赖于类变量) .

  • 另一个可能的解决方案是为这个函数(和其他函数)创建一个单独的类:

    class Functions(object):
        @classmethod
        def bar(cls, arg1, arg2):
            return foo(arg1, arg2)
    

    在MyClass 内将其称为Functions.bar(self.arg1, self.arg2),在外部称为Functions.bar(arg1, arg2)

    我也不是很热衷于此,因为我将 bar 和类似函数放在 MyClass 中是有原因的:它们在概念上与 MyClass 相关。

我看到了一些使用描述符的类似 SO 帖子的答案,但我希望可能有更优雅的解决方案。

这里有任何明智的 Python 向导提供建议吗?

【问题讨论】:

    标签: python oop overloading class-method instance-methods


    【解决方案1】:

    您也可以让self 采用默认参数,以便区分mine.bar() 和MyClass.bar()。代价是其他两个参数必须是关键字参数。

    class MyClass:
    
        _sentinel = object()
    
        def bar(self=None, *, arg1=_sentinel, arg2=_sentinel):
            if self is not None:
                if arg1 is _sentinel:
                    arg1 = self.arg1
                if arg2 is _sentinel:
                    arg2 = self.arg2
            else:
                if arg1 is _sentinel:
                    raise ValueError("Missing required arg1")
                if arg2 is _sentinel:
                    raise ValueError("Missing required arg2")
    
            return foo(arg1, arg2)
    
    
    mine.bar()  # self is mine, arg1 is _sentinel, arg2 is _sentinel
    
    MyClass.bar(arg1=3, arg2=4)
    

    【讨论】:

    • 哇,我从来没有想到过!因此,如果我理解正确,来自My Class 实例的正常调用总是将对该实例的引用传递为self,因此默认值总是被覆盖。但是,在静态上下文中调用 bar 不会传递这样的引用,因此默认传递 None。这听起来对吗?
    • 只有一个问题:为什么要强制使用 kwargs?这是否会阻止第一个参数在静态调用中作为 self 传递?
    猜你喜欢
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多