[Tutor] question about descriptors
Albert-Jan Roskam
sjeik_appie at hotmail.com
Thu Nov 12 07:11:19 EST 2015
> To: tutor at python.org
> From: __peter__ at web.de
> Date: Wed, 11 Nov 2015 20:06:20 +0100
> Subject: Re: [Tutor] question about descriptors
>
> Albert-Jan Roskam wrote:
>
> >> From: steve at 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 at python.org
> To unsubscribe or change subscription options:
> https://mail.python.org/mailman/listinfo/tutor
More information about the Tutor
mailing list