Path: csiph.com!usenet.pasdenom.info!weretis.net!feeder4.news.weretis.net!feeds.phibee-telecom.net!newsfeed.xs4all.nl!newsfeed2.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.000 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; 'else:': 0.03; 'subject:Python': 0.06; 'none:': 0.07; 'string': 0.09; 'attributes': 0.09; 'obj': 0.09; 'received:80.91': 0.09; 'received:80.91.229': 0.09; 'received:gmane.org': 0.09; 'received:list': 0.09; 'try:': 0.09; 'def': 0.12; 'stored': 0.12; 'suggest': 0.14; 'wrote': 0.14; 'mostly': 0.14; '"_"': 0.16; 'a()': 0.16; 'attr': 0.16; 'attributes:': 0.16; 'descriptor.': 0.16; 'hasattr(obj,': 0.16; 'naming': 0.16; 'newvalue': 0.16; 'obj,': 0.16; 'received:80.91.229.3': 0.16; 'received:dip0.t-ipconnect.de': 0.16; 'received:plane.gmane.org': 0.16; 'received:t-ipconnect.de': 0.16; 'str):': 0.16; 'wsgi': 0.16; 'wrote:': 0.18; '(the': 0.22; 'handles': 0.22; 'header:User- Agent:1': 0.23; 'simpler': 0.24; 'skip:{ 20': 0.24; '----------': 0.26; 'header:X-Complaints-To:1': 0.27; 'idea': 0.28; 'rest': 0.29; 'properties': 0.29; 'code': 0.31; 'lines': 0.31; 'breaking': 0.31; 'grouping': 0.31; 'class': 0.32; 'ago': 0.33; 'skip:_ 10': 0.34; 'skip:d 20': 0.34; 'subject: (': 0.35; 'classes': 0.35; 'except': 0.35; 'skip:s 30': 0.35; 'something': 0.35; 'skip:> 10': 0.36; 'subject:data': 0.36; 'similar': 0.36; 'project': 0.37; 'convention': 0.38; 'to:addr:python-list': 0.38; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'received:org': 0.40; 'year.': 0.61; 'new': 0.61; 'name': 0.63; 'subject:more': 0.64; 'more': 0.64; 'here': 0.66; '600': 0.68; 'yourself': 0.78; '(incl.': 0.84; 'self.value': 0.84; 'university.': 0.93 X-Injected-Via-Gmane: http://gmane.org/ To: python-list@python.org From: Peter Otten <__peter__@web.de> Subject: Re: Python descriptor protocol (for more or less structured data) Date: Tue, 30 Jul 2013 16:35:41 +0200 Organization: None References: <040f33f6-6435-4aea-98ae-eabf8c16b167@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset="ISO-8859-1" Content-Transfer-Encoding: 7Bit X-Gmane-NNTP-Posting-Host: p5084a07c.dip0.t-ipconnect.de User-Agent: KNode/4.7.3 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 110 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1375194933 news.xs4all.nl 15978 [2001:888:2000:d::a6]:47333 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:51561 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 = ...