[Python-Dev] Customizing the binding of attributes

Guido van Rossum guido@python.org
Fri, 24 Aug 2001 06:03:34 -0400


> From: "Guido van Rossum" <guido@python.org>
> > I'm not sure what you are asking about, but let me give an example.
> > You can write an auxiliary class that "describes" an instance
> > attribute.  Instances of this "descriptor" class are stored in a class
> > dict.  The descriptor class implements a method __get__ which is
> > called in two situations:
> > 
> > (a) when a class attribute is retrieved, <attr>.__get__(None, <class>)
> >     is called; this can return an unbound method
> > 
> > (b) when an instance attribute is retrieved, <attr>.__get__(<inst>, <class)
> >     is called; this can return a bound method
> > 
> > Does this help?

From: "Thomas Heller" <thomas.heller@ion-tof.com>
> Yes, thanks. Exactly what I meant. You only forgot to mention that
> the descriptor class must derive from object: is this intended?

Yes, almost all new features require you to switch to new-style
classes; the classic class implementation is left unchanged (as much
as possible) for compatibility.  In particular, classic classes can't
define __get__ to the effect above.

These descriptors semi-work as class attributes of classic classes,
but the corresponding __set__ feature does not, because classic
classes *always* store in __dict__ upon assignment; new-style classes
first look for a descriptor implementing __set__ (really the C
dispatch function tp_descr_set) and use that to override the default
assignment.

> I needed this to write
> 
> class X:
>     method = myinstancemethod(func)
> 
> instead of
> 
> class X:
>     pass
> X.method = new.instancemethod(func, None, X)
> 
> where myinstancemethod is the descriptor class, and func
> is any callable object (maybe a function implemented in C).
> 
> Two additional comments:
> 
> - Shouldn't (my)instancemethod be a builtin? Similar to
> staticmethod and classmethod?

Yes.  I just checked this in.  The new name is 'getset' unless you
have a better idea.

> - The following seems to be a bug (remember, I'm on Windows):
> 
> C:\>c:\python22\python.exe
> Python 2.2a2 (#22, Aug 22 2001, 01:24:03) [MSC 32 bit (Intel)] on win32
> Type "help", "copyright", "credits" or "license" for more information.
> >>> class myinstancemethod(object):
> ...     def __init__(self, func):
> ...         self.func = func
> ...     def __get__(self, inst, klass=None):
> ...         import new
> ...         return new.instancemethod(self.func, inst, klass)
> ...
> >>> class X:
> ...     meth = myinstancemethod(lambda *x: x)
> ...
> >>> X().meth
> <bound method X.<lambda> of <__main__.X instance at 007C7F04>>
> >>> X().meth()
> (<__main__.X instance at 007E80E4>,)
> >>> X.meth
> Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
> SystemError: NULL object passed to Py_BuildValue

Yup.  Thanks!  Try this patch:

*** typeobject.c	2001/08/22 19:24:42	2.43
--- typeobject.c	2001/08/24 10:05:25
***************
*** 2806,2811 ****
--- 2806,2815 ----
  		Py_INCREF(self);
  		return self;
  	}
+ 	if (obj == NULL)
+ 		obj = Py_None;
+ 	if (type == NULL)
+ 		type = Py_None;
  	return PyObject_CallFunction(get, "OOO", self, obj, type);
  }
  
--Guido van Rossum (home page: http://www.python.org/~guido/)