D
dg.google.groups
Hi all,
So I understand that properties belong to a class not an instance, but
nonetheless I want to add properties to an instance. I have a class
which when an instance is created runs some fairly complicated code
and produces a set of names which I'd like to be able to access via
properties. At the moment, I'm using something like obj.getvar(name)
but I'd like to be able to write obj.name. (Note that they can't be
just standard attributes because they only get computed when they are
accessed.) I could generate functions like obj.name() but I want it to
be obj.name instead.
The solution I've come up with is to create a new class for each
object which is just the real class with some extra properties, and
then dynamically change the class of the object to this new class.
This seems to work, but I wonder if (a) there is a nicer solution than
the one I'll post below, (b) if there are any dangers or pitfalls of
this approach. The obvious difficulty is with derived classes. At the
moment, I'm insisting that a derived class has to call a makeprops()
method to create the properties.
It's kind of similar to this recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/197965
but that recipe has a much simpler situation in which the properties
and values are known at the time of the creation of the object (by
contrast, I don't know what the properties are until the end of the
__init__ method).
Any thoughts?
Code below to illustrate my approach.
import warnings
from operator import itemgetter
class A(object):
def __init__(self,**kwds):
self._kwds = kwds
self.makeprops()
def __getitem__(self,i):
return self._kwds
def makeprops(self):
if not hasattr(self,'_madeprops'):
self._madeprops = set()
self._failedprops = set()
class _A(self.__class__):
pass
for k,v in self._kwds.items():
if not k in self._madeprops and k in dir(self):
if not k in self._failedprops:
warnings.warn("Cannot create property "+k+",
already used in object "+str(self),RuntimeWarning)
self._failedprops.add(k)
else:
setattr(_A,k,property(fget=itemgetter(k)))
self._madeprops.add(k)
self.__class__ = _A
class B(A):
def __init__(self,**kwds):
super(B,self).__init__(**kwds)
self.makeprops()
class C(A):
def __init__(self,**kwds):
self._kwds = kwds
a = A(x=1)
b = B(x=2,makeprops=3)
c = C(x=3)
print isinstance(a,A), isinstance(a,B), isinstance(a,C) # True False
False
print isinstance(b,A), isinstance(b,B), isinstance(b,C) # True True
False
print isinstance(c,A), isinstance(c,B), isinstance(c,C) # True False
True
print a.__class__ # <class '__main__._A'>
print b.__class__ # <class '__main__._A'>
print c.__class__ # <class '__main__.C'>
print a.x # 1
print b.x # 2
print b.makeprops # <bound method _A.makeprops of <__main__._A object
at 0x00A86810>>
try:
print c.x # raises exception
except AttributeError:
print "c has no element x"
c.makeprops()
print c.x # 3
print a.__class__ # <class '__main__._A'>
print b.__class__ # <class '__main__._A'>
print c.__class__ # <class '__main__._A'>
So I understand that properties belong to a class not an instance, but
nonetheless I want to add properties to an instance. I have a class
which when an instance is created runs some fairly complicated code
and produces a set of names which I'd like to be able to access via
properties. At the moment, I'm using something like obj.getvar(name)
but I'd like to be able to write obj.name. (Note that they can't be
just standard attributes because they only get computed when they are
accessed.) I could generate functions like obj.name() but I want it to
be obj.name instead.
The solution I've come up with is to create a new class for each
object which is just the real class with some extra properties, and
then dynamically change the class of the object to this new class.
This seems to work, but I wonder if (a) there is a nicer solution than
the one I'll post below, (b) if there are any dangers or pitfalls of
this approach. The obvious difficulty is with derived classes. At the
moment, I'm insisting that a derived class has to call a makeprops()
method to create the properties.
It's kind of similar to this recipe:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/197965
but that recipe has a much simpler situation in which the properties
and values are known at the time of the creation of the object (by
contrast, I don't know what the properties are until the end of the
__init__ method).
Any thoughts?
Code below to illustrate my approach.
import warnings
from operator import itemgetter
class A(object):
def __init__(self,**kwds):
self._kwds = kwds
self.makeprops()
def __getitem__(self,i):
return self._kwds
def makeprops(self):
if not hasattr(self,'_madeprops'):
self._madeprops = set()
self._failedprops = set()
class _A(self.__class__):
pass
for k,v in self._kwds.items():
if not k in self._madeprops and k in dir(self):
if not k in self._failedprops:
warnings.warn("Cannot create property "+k+",
already used in object "+str(self),RuntimeWarning)
self._failedprops.add(k)
else:
setattr(_A,k,property(fget=itemgetter(k)))
self._madeprops.add(k)
self.__class__ = _A
class B(A):
def __init__(self,**kwds):
super(B,self).__init__(**kwds)
self.makeprops()
class C(A):
def __init__(self,**kwds):
self._kwds = kwds
a = A(x=1)
b = B(x=2,makeprops=3)
c = C(x=3)
print isinstance(a,A), isinstance(a,B), isinstance(a,C) # True False
False
print isinstance(b,A), isinstance(b,B), isinstance(b,C) # True True
False
print isinstance(c,A), isinstance(c,B), isinstance(c,C) # True False
True
print a.__class__ # <class '__main__._A'>
print b.__class__ # <class '__main__._A'>
print c.__class__ # <class '__main__.C'>
print a.x # 1
print b.x # 2
print b.makeprops # <bound method _A.makeprops of <__main__._A object
at 0x00A86810>>
try:
print c.x # raises exception
except AttributeError:
print "c has no element x"
c.makeprops()
print c.x # 3
print a.__class__ # <class '__main__._A'>
print b.__class__ # <class '__main__._A'>
print c.__class__ # <class '__main__._A'>