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__
的方法:
- Possible to change a function's repr in python?
- How to create a custom string representation for a class object?
感谢 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,直到你想要注入的糟糕代码库满意为止那些价值观嘎嘎叫。
具体来说,我希望 MyClass.my_method
用于在 class 字典中查找值,但 MyClass.my_method()
是一种接受参数并执行计算的方法更新 MyClass
中的属性,然后 returns MyClass
及其所有属性(包括更新后的属性)。
我认为 Python's descriptors 可能可行(也许覆盖 __get__
或 __call__
),但我无法弄清楚这看起来如何。我知道这种行为可能会令人困惑,但如果可能的话(以及是否有任何其他主要警告)我很感兴趣。
我看到您可以通过覆盖 __repr__
为 classes 和函数做类似的事情,但我找不到 class 中的方法的类似方法.我的返回值也不会总是一个字符串,这似乎禁止了这两个问题中提到的基于 __repr__
的方法:
- Possible to change a function's repr in python?
- How to create a custom string representation for a class object?
感谢 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 upMyClass.my_method
, and then attempting to call that object. So the result ofMyClass.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,直到你想要注入的糟糕代码库满意为止那些价值观嘎嘎叫。