Return 当 class 方法作为属性访问时的自定义值,但仍允许它在调用时执行计算?

Return a custom value when a class method is accessed as an attribute, but still allow for it to perform a computation when called?

具体来说,我希望 MyClass.my_method 用于在 class 字典中查找值,但 MyClass.my_method() 是一种接受参数并执行计算的方法更新 MyClass 中的属性,然后 returns MyClass 及其所有属性(包括更新后的属性)。

我认为 Python's descriptors 可能可行(也许覆盖 __get____call__),但我无法弄清楚这看起来如何。我知道这种行为可能会令人困惑,但如果可能的话(以及是否有任何其他主要警告)我很感兴趣。

我看到您可以通过覆盖 __repr__ 为 classes 和函数做类似的事情,但我找不到 class 中的方法的类似方法.我的返回值也不会总是一个字符串,这似乎禁止了这两个问题中提到的基于 __repr__ 的方法:

感谢 Joel 的最小实现。我发现剩下的问题是 parent 没有初始化,因为我没有找到通用的初始化方法,我需要检查 list/dict 的属性,并添加相应地初始化值 parent。

代码的这一添加应该适用于 lists/dicts:

def classFactory(parent, init_val, target):
    class modifierClass(parent):
        def __init__(self, init_val):
            super().__init__()
            dict_attr = getattr(parent, "update", None)
            list_attr = getattr(parent, "extend", None)
            if callable(dict_attr):  # parent is dict
                self.update(init_val)
            elif callable(list_attr):  # parent is list
                self.extend(init_val)
            self.target = target

        def __call__(self, *args):
            self.target.__init__(*args)

    return modifierClass(init_val)


class myClass:
    def __init__(self, init_val=''):
        self.method = classFactory(init_val.__class__, init_val, self)

不幸的是,我们需要逐个添加,但这会按预期工作。

下面是一种稍微不那么冗长的写法:

def classFactory(parent, init_val, target):
    class modifierClass(parent):
        def __init__(self, init_val):
            if isinstance(init_val, list):
                self.extend(init_val)
            elif isinstance(init_val, dict):
                self.update(init_val)
            self.target = target

        def __call__(self, *args):
            self.target.__init__(*args)

    return modifierClass(init_val)


class myClass:
    def __init__(self, init_val=''):
        self.method = classFactory(init_val.__class__, init_val, self)

这是 Guillherme 的答案的最小实现,它更新了方法而不是单独的可修改参数:

def classFactory(parent, init_val, target):
    class modifierClass(parent):
        def __init__(self, init_val):
            self.target = target

        def __call__(self, *args):
            self.target.__init__(*args)

    return modifierClass(init_val)


class myClass:
    def __init__(self, init_val=''):
        self.method = classFactory(init_val.__class__, init_val, self)

这个答案和原始答案都适用于单个值,但列表和字典似乎返回为空而不是预期值,我不确定为什么在这里感谢帮助:

正如 jasonharper 评论的那样,

MyClass.my_method() works by looking up MyClass.my_method, and then attempting to call that object. So the result of MyClass.my_method cannot be a plain string, int, or other common data type [...]

问题特别来自于为这两个属性重复使用相同的名称,正如您所说,这非常令人困惑。所以,不要这样做。

但是为了它的唯一利益,您可以尝试使用一个对象来代理 属性 的值,该对象在调用时会 return 原始 MyClass 实例,使用实际的 setter 执行您想要的任何计算,并将任意属性转发给代理值。

class MyClass:

    _my_method = whatever

    @property
    def my_method(self):

        my_class = self

        class Proxy:
            def __init__(self, value):
                self.__proxied = value

            def __call__(self, value):
                my_class.my_method = value
                return my_class

            def __getattr__(self, name):
                return getattr(self.__proxied, name)

            def __str__(self):
                return str(self.__proxied)

            def __repr__(self):
                return repr(self.__proxied)

        return Proxy(self._my_method)

    @my_method.setter
    def my_method(self, value):
        # your computations
        self._my_method = value

a = MyClass()
b = a.my_method('do not do this at home')

a is b
# True
a.my_method.split(' ')
# ['do', 'not', 'do', 'this', 'at', 'home']

而今天,duck typing 会虐待你,迫使你将各种神奇的方法委托给代理中的代理值 class,直到你想要注入的糟糕代码库满意为止那些价值观嘎嘎叫。