代码之家  ›  专栏  ›  技术社区  ›  joelostblom

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

  •  0
  • joelostblom  · 技术社区  · 2 年前

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

    我认为这可能是可行的 Python's descriptors (可能会压倒一切 __get__ __call__ ),但我不知道这会是什么样子。我知道这种行为可能会令人困惑,但如果可能的话(以及是否有任何其他主要的注意事项),我很感兴趣。

    我已经看到,通过重写,可以对类和函数执行类似的操作 __repr__ ,但在类中找不到类似的方法。我的返回值也不会总是字符串,这似乎禁止 __repr__ -基于这两个问题中提到的方法:

    0 回复  |  直到 2 年前
        1
  •  1
  •   joelostblom    2 年前

    感谢Joel的最低限度的实现。我发现剩下的问题是缺少对父级的初始化,因为我没有找到初始化它的通用方法,所以我需要在list/dict的情况下检查属性,并相应地将初始化值添加到父级。

    添加到代码中应该可以使其适用于列表/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)
    
        2
  •  1
  •   N1ngu    2 年前

    正如jasonharper评论的那样,

    MyClass.my_method() 通过查找工作 MyClass.my_method ,然后尝试调用该对象。因此 MyClass.my_method 不能是纯字符串、int或其他常见数据类型[…]

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

    但仅为了它的利益,您可以尝试用一个对象来代理属性的值,该对象在调用时会返回原始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']
    
    

    今天,鸭子类型会滥用你,迫使你将各种神奇的方法委托给代理类中的代理值,直到你想要注入的糟糕的代码库对这些值的嘎嘎声感到满意。

        3
  •  0
  •   joelostblom    2 年前

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

    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)
    

    这和原始答案都适用于单个值,但列表和字典似乎是空的,而不是预期值,我不知道为什么这里需要帮助:

    enter image description here