Python descriptor protocol (for more or less structured data)
Peter Otten
__peter__ at web.de
Tue Jul 30 10:35:41 EDT 2013
CWr wrote:
> Some years ago I started a small WSGI project at my university. Since then
> the project was grown up every year. Some classes have more than 600 lines
> of code with (incl. boiler-plates mostly in descriptors/properties).
>
> Many of these properties are similar or have depencies among themselves.
> The idea is to grouping similar properties like:
>
> new style:
> ----------
>>>>m = MyClass(...)
>>>>m.attr = 'some; complex:data#string'
>
>>>>m.attr.value
> 'some'
>>>>m.attr.extras
> {'complex':('data','string')}
>
> I wrote this descriptor:
>
> class Descr:
>
> def __init__(self, value):
> self.attribute = self.__class__.__name__
> self.__set__(None, value)
>
> def __get__(self, obj, Type=None):
> return getattr(obj, self.attribute, self)
>
> def __set__(self, obj, value):
> if obj is None: # descripting yourself
> # do something here ...
> self.value = value
> else:
> if hasattr(obj, self.attribute):
> self.__get__(obj).__set__(None, value)
> else:
> setattr(obj, self.attribute, type(self)(value))
You must not store per-object data in the descriptor. I suggest a naming
convention (the internal data for obj.attr is stored in obj._attr) together
with a value class that handles breaking of the string into attributes of an
instance of itself:
class StructuredAttribute:
def __init__(self, name, make_default):
self.name = name
self.make_default = make_default
def __get__(self, obj, type=None):
if obj is None:
return self
_name = "_" + self.name
try:
return getattr(obj, _name)
except AttributeError:
setattr(obj, _name, self.make_default())
return getattr(obj, _name)
def __set__(self, obj, value):
self.__get__(obj).update(value)
class Value:
def __init__(self, value):
self.update(value)
def update(self, value):
if isinstance(value, str):
self.value, sep, rest = value.partition(";")
self.extras = dict(item.partition("#")[::2] for item in
rest.split())
else:
self.value = value.value
self.extras = value.extras
def __repr__(self):
return repr("{}; {}".format(self.value, " ".join("{}:
{}".format(*item) for item in self.extras.items())))
def make_default_value():
return Value("some; complex:data#string")
class A:
attr = StructuredAttribute("alpha", make_default_value)
def show(obj):
print("attr:", obj.attr)
print("attr.value:", obj.attr.value)
print("attr.extras:", obj.attr.extras)
a = A()
show(a)
newvalue = "whatever"
print("updating value to", newvalue)
a.attr.value = newvalue
show(a)
That's the general idea if you want "setattr polymorphism". Personally I
would go with simpler standard attributes:
class A:
def __init__(self):
self.attr = Value(...)
a = A()
a.value = Value(...)
a.value.extras = ...
More information about the Python-list
mailing list