是否有等效的属性部分方法?
Is there an equivalent of partialmethod for properties?
我有一个 class,其中有许多参数存储在字典中,如下所示(实际上,它有点复杂,但这提供了一个好主意)。我能够 'autogenerate' 使用 partialmethod
:
获取和设置方法
for a_name in ['x', 'y', 'z']:
locals()["get_" + a_name] = partialmethod(_get_arg,
arg_name=a_name)
locals()["set_" + a_name] = partialmethod(_set_arg,
arg_name=a_name)
理想情况下,我想使用 @property
,但前提是我可以 'autogenerate' @property
、@setter
和 @deleter
。可能吗?第二个片段显示 'manually' 添加的属性;我正在研究使用 partialmethod
的等价物来避免重复和不可维护的代码。
class C:
def __init__(self):
self.kwargs = {'x' : 0, 'y' : 3, 'z' : True}
def __get_arg(self, arg_name):
assert arg_name in self.kwargs
return self.kwargs[arg_name]
def __set_arg(self, arg_name, value):
assert arg_name in self.kwargs
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
assert arg_name in self.kwargs
del self.kwargs[arg_name]
@property
def x(self):
return self.__get_arg('x')
@x.setter
def x(self, value):
self.__set_arg('x', value)
@x.deleter
def x(self):
self.__del_arg('x')
@property
def y(self):
return self.__get_arg('y')
@y.setter
def y(self, value):
self.__set_arg('y', value)
@y.deleter
def y(self):
self.__del_arg('y')
@property
def z(self):
return self.__get_arg('z')
@x.setter
def z(self, value):
self.__set_arg('z', value)
@x.deleter
def z(self):
self.__del_arg('z')
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
您可以编写自己的描述符类型,并使用将在其上调用的 __set_name__
方法(由 class 创建机制)找出它在class:
class MyProp:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner=None):
if instance is None:
return self
return instance._get_arg(self.name)
def __set__(self, instance, value):
instance._set_arg(self.name, value)
def __delete__(self, instance):
instance._del_arg(self.name)
你会这样使用它:
class C:
# define __init__, _get_arg, etc.
x = MyProp()
y = MyProp()
z = MyProp()
请注意,因为它是来自另一个 class 调用 _X_arg
方法的代码,您可能不想进行名称修改,所以我将 __
前缀更改为只有一个 _
.
经过一段时间的努力,我想出了以下 partialproperty
描述符。
访问属性时调用__get__
方法。在其中我称之为
setter()
方法,第一个参数为 obj
作为 self
参数
接下来是解压后的 self.args
和 self.kwargs
.
class partialproperty:
"""Combine the functionality of property() and partialmethod()"""
def __init__(self, getter, setter=None, deleter=None, *args, **kwargs):
self.getter = getter
self.setter = setter
self.deleter = deleter
self.args = args
self.kwargs = kwargs
def __set_name__(self, owner, name):
self._name = name
self._owner = owner
def __get__(self, obj, objtype=None):
return self.getter(obj, *self.args, **self.kwargs)
def __set__(self, obj, value):
if self.setter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't set attribute: {self._name}")
self.setter(obj, *self.args, value, **self.kwargs)
def __delete__(self, obj):
if self.deleter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't delete attribute: {self._name}")
self.deleter(obj, *self.args, **self.kwargs)
您可以在 class 中使用它来创建 属性 属性,就像您一样
会用 partialmethod()
.
如果您不需要 setter 和删除器,您需要使用关键字
args 的参数,或显式传递 None
.
class RGBA:
def __init__(self, rgba):
self.rgba = rgba
def _component_(self, idx):
return self.rgba[idx]
# using keyword args
red = partialproperty(_component_, idx=0)
green = partialproperty(_component_, idx=1)
blue = partialproperty(_component_, idx=2)
# this has the same effect but with positional args
alpha = partialproperty(_component_, None, None, 3)
然后在您的实例中,各个属性的行为将类似于属性。
>>> color = RGBA([0, 75, 255, 1])
>>> color.red
0
>>> color.green
75
>>> color.blue
255
>>> color.alpha
1
对于您的示例,它看起来像:
class C:
def __init__(self, **kwargs):
self.kwargs = kwargs
def __get_arg(self, arg_name):
try:
return self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
def __set_arg(self, arg_name, value):
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
try:
del self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
x = partialproperty(__get_arg, __set_arg, __del_arg, "x")
y = partialproperty(__get_arg, __set_arg, __del_arg, "y")
z = partialproperty(__get_arg, __set_arg, __del_arg, "z")
>>> obj = C(x=24)
>>> obj.x
24
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
对于您的具体示例,更简单的方法可能是 subclass
dict
然后定义魔术方法 __getattr__
、__setattr__
和
__delattr__
当属性不存在时调用它们。然后你
可以调用 super()
来继承 dict
class.
的行为
class AttrDict(dict):
attrs = ("x", "y", "z")
def __getattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__getitem__(name)
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__setitem__(name, value)
except KeyError:
raise AttributeError(name)
def __delattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__delitem__(name)
except KeyError:
raise AttributeError(name)
>>> obj = AttrDict(x=24)
>>> obj.x
24
>>> obj.a = 1
AttributeError: a
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
如果您不关心对特定属性的限制,那就更容易了。
您可以简单地将每个 __*attr__
方法分配给它的对应方法
dict.__*item__
方法,这是为订阅操作调用的方法。
(即obj[key]
)
class AttrDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
>>> obj = AttrDict({"a": 1})
>>> obj.a
1
>>> obj["b"] = 2
>>> obj.b
2
>>> del obj.b
>>> obj.b
KeyError
我有一个 class,其中有许多参数存储在字典中,如下所示(实际上,它有点复杂,但这提供了一个好主意)。我能够 'autogenerate' 使用 partialmethod
:
for a_name in ['x', 'y', 'z']:
locals()["get_" + a_name] = partialmethod(_get_arg,
arg_name=a_name)
locals()["set_" + a_name] = partialmethod(_set_arg,
arg_name=a_name)
理想情况下,我想使用 @property
,但前提是我可以 'autogenerate' @property
、@setter
和 @deleter
。可能吗?第二个片段显示 'manually' 添加的属性;我正在研究使用 partialmethod
的等价物来避免重复和不可维护的代码。
class C:
def __init__(self):
self.kwargs = {'x' : 0, 'y' : 3, 'z' : True}
def __get_arg(self, arg_name):
assert arg_name in self.kwargs
return self.kwargs[arg_name]
def __set_arg(self, arg_name, value):
assert arg_name in self.kwargs
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
assert arg_name in self.kwargs
del self.kwargs[arg_name]
@property
def x(self):
return self.__get_arg('x')
@x.setter
def x(self, value):
self.__set_arg('x', value)
@x.deleter
def x(self):
self.__del_arg('x')
@property
def y(self):
return self.__get_arg('y')
@y.setter
def y(self, value):
self.__set_arg('y', value)
@y.deleter
def y(self):
self.__del_arg('y')
@property
def z(self):
return self.__get_arg('z')
@x.setter
def z(self, value):
self.__set_arg('z', value)
@x.deleter
def z(self):
self.__del_arg('z')
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
您可以编写自己的描述符类型,并使用将在其上调用的 __set_name__
方法(由 class 创建机制)找出它在class:
class MyProp:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner=None):
if instance is None:
return self
return instance._get_arg(self.name)
def __set__(self, instance, value):
instance._set_arg(self.name, value)
def __delete__(self, instance):
instance._del_arg(self.name)
你会这样使用它:
class C:
# define __init__, _get_arg, etc.
x = MyProp()
y = MyProp()
z = MyProp()
请注意,因为它是来自另一个 class 调用 _X_arg
方法的代码,您可能不想进行名称修改,所以我将 __
前缀更改为只有一个 _
.
经过一段时间的努力,我想出了以下 partialproperty
描述符。
访问属性时调用__get__
方法。在其中我称之为
setter()
方法,第一个参数为 obj
作为 self
参数
接下来是解压后的 self.args
和 self.kwargs
.
class partialproperty:
"""Combine the functionality of property() and partialmethod()"""
def __init__(self, getter, setter=None, deleter=None, *args, **kwargs):
self.getter = getter
self.setter = setter
self.deleter = deleter
self.args = args
self.kwargs = kwargs
def __set_name__(self, owner, name):
self._name = name
self._owner = owner
def __get__(self, obj, objtype=None):
return self.getter(obj, *self.args, **self.kwargs)
def __set__(self, obj, value):
if self.setter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't set attribute: {self._name}")
self.setter(obj, *self.args, value, **self.kwargs)
def __delete__(self, obj):
if self.deleter is None:
raise AttributeError(f"{self._owner.__class__.__name__} object can't delete attribute: {self._name}")
self.deleter(obj, *self.args, **self.kwargs)
您可以在 class 中使用它来创建 属性 属性,就像您一样
会用 partialmethod()
.
如果您不需要 setter 和删除器,您需要使用关键字
args 的参数,或显式传递 None
.
class RGBA:
def __init__(self, rgba):
self.rgba = rgba
def _component_(self, idx):
return self.rgba[idx]
# using keyword args
red = partialproperty(_component_, idx=0)
green = partialproperty(_component_, idx=1)
blue = partialproperty(_component_, idx=2)
# this has the same effect but with positional args
alpha = partialproperty(_component_, None, None, 3)
然后在您的实例中,各个属性的行为将类似于属性。
>>> color = RGBA([0, 75, 255, 1])
>>> color.red
0
>>> color.green
75
>>> color.blue
255
>>> color.alpha
1
对于您的示例,它看起来像:
class C:
def __init__(self, **kwargs):
self.kwargs = kwargs
def __get_arg(self, arg_name):
try:
return self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
def __set_arg(self, arg_name, value):
self.kwargs[arg_name] = value
def __del_arg(self, arg_name):
try:
del self.kwargs[arg_name]
except KeyError:
raise AttributeError(arg_name)
x = partialproperty(__get_arg, __set_arg, __del_arg, "x")
y = partialproperty(__get_arg, __set_arg, __del_arg, "y")
z = partialproperty(__get_arg, __set_arg, __del_arg, "z")
>>> obj = C(x=24)
>>> obj.x
24
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
对于您的具体示例,更简单的方法可能是 subclass
dict
然后定义魔术方法 __getattr__
、__setattr__
和
__delattr__
当属性不存在时调用它们。然后你
可以调用 super()
来继承 dict
class.
class AttrDict(dict):
attrs = ("x", "y", "z")
def __getattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__getitem__(name)
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__setitem__(name, value)
except KeyError:
raise AttributeError(name)
def __delattr__(self, name):
if not name in self.attrs:
raise AttributeError(name)
try:
return super().__delitem__(name)
except KeyError:
raise AttributeError(name)
>>> obj = AttrDict(x=24)
>>> obj.x
24
>>> obj.a = 1
AttributeError: a
>>> obj.y = 25
>>> obj.y
25
>>> del obj.y
>>> obj.y
AttributeError: y
>>> obj.kwargs["z"] = 26
>>> obj.z
26
如果您不关心对特定属性的限制,那就更容易了。
您可以简单地将每个 __*attr__
方法分配给它的对应方法
dict.__*item__
方法,这是为订阅操作调用的方法。
(即obj[key]
)
class AttrDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
>>> obj = AttrDict({"a": 1})
>>> obj.a
1
>>> obj["b"] = 2
>>> obj.b
2
>>> del obj.b
>>> obj.b
KeyError