[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