> To: tutor@python.org > From: __pete...@web.de > Date: Wed, 11 Nov 2015 20:06:20 +0100 > Subject: Re: [Tutor] question about descriptors > > Albert-Jan Roskam wrote: > > >> From: st...@pearwood.info > > >> Fortunately, Python has an mechanism for solving this problem: > >> the `__getattr__` method and friends. > >> > >> > >> class ColumnView(object): > >> _data = {'a': [1, 2, 3, 4, 5, 6], > >> 'b': [1, 2, 4, 8, 16, 32], > >> 'c': [1, 10, 100, 1000, 10000, 100000], > >> } > >> def __getattr__(self, name): > >> if name in self._data: > >> return self._data[name][:] > >> else: > >> raise AttributeError > >> def __setattr__(self, name, value): > >> if name in self._data: > >> raise AttributeError('read-only attribute') > >> super(ColumnView, self).__setattr__(name, value) > >> def __delattr__(self, name): > >> if name in self._data: > >> raise AttributeError('read-only attribute') > >> super(ColumnView, self).__delattr__(name) > > > > That also seems very straightforward. Why does "if name in self._data:" > > not cause a recursion? self._data calls __getattr__, which has self._data > > in it, which...etc. > > __getattr__() is only invoked as a fallback when the normal attribute lookup > fails:
Aha.. and "normal attributes" live in self.__dict__? > >>> class A(object): > ... def __getattr__(self, name): > ... return self.data[name] > ... > >>> a = A() > >>> a.data = dict(foo="bar") > >>> a.foo > 'bar' > >>> del a.data > >>> import sys > >>> sys.setrecursionlimit(10) > >>> a.foo > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > File "<stdin>", line 3, in __getattr__ > File "<stdin>", line 3, in __getattr__ > File "<stdin>", line 3, in __getattr__ > RuntimeError: maximum recursion depth exceeded while calling a Python object > > If you need to intercept every attribute lookup use __getattribute__(): Fantastic, thank you for the clear explanation. Do you happen to know whether the __getattr__ vs. __getattribute__ distinction was (a) a deliberate design decision or (b) a historic anomaly? If one considers the distinction between "normal attributes" vs. "attributes of which the read/write/delete properties*) may be changed" , I'd say (a). *) with files these are called "attributes", so one could call them attributes with attributes. :-) > >>> class B(A): > ... def __getattribute__(self, name): > ... print "looking for", name > ... return super(B, self).__getattribute__(name) > ... > >>> b = B() > >>> b.data = dict(foo="bar") > >>> b.foo > looking for foo > looking for data > 'bar' > > > _______________________________________________ > Tutor maillist - Tutor@python.org > To unsubscribe or change subscription options: > https://mail.python.org/mailman/listinfo/tutor _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor