Python class dynamic lazy properties

I have a Record class that is used to store the results of reading a text file. The file contains a simple database with fields and tags. I would like each record instance to only have properties associated with its database. Basically:

R1 = Record("file1")
R2 = Record("file2")
print(R1.TI) #"Record 1 title"
print(R2.TI) #AttributeError: 'Record' object has no attribute 'TI'

      

Unfortunately, some of the fields may require a lot of processing to return something useful, and these values ​​may never be needed. So I would like the value to be determined on first call not on object initialization.

Since I only know tags, I only tried:

 class tagWrapper(object):
     def __init__(self, tag):
         self.tag = tag
         self.data = None
     def __get__(self, instance, owner):
         if self.data == None:
             try:
                 #tagToFunc is a dictionary that maps tags to their processing function
                 self.data = tagToFunc[self.tag](instance._rawDataDict[self.tag]) 
             except KeyError: #I do not know the full list of tags
                 self.data = instance._rawDataDict[self.tag]
         return self.data

 class Record(object):
     def __init__(self, file):
         #Reading file and making _rawDataDict
         setattr(self, tag, tagWrapper(tag))

      

This causes it to R1.TI

create a wrapper object, not the value I want. So I suspect that I am citing something using the get method.

Note. I am trying to make attributes part of individual class instances and not evaluated until needed. I can implement one or the other, but have not been able to figure out how to do it.

+3


source to share


2 answers


I have a solution that seems to work, albeit rather ugly:

class Record(object):
   def __init__(self, file):
     self._unComputedTags = set() #needs to be initialized first
      #stuff
      self._unComputedTags = set(self._fieldDict.keys())
      for tag in self._fieldDict:
            self.__dict__[tag] = None

   def __getattribute__(self, name):
       if name == '_unComputedTags':
           #This may be unnecessary if I play with things a bit
           return object.__getattribute__(self, '_unComputedTags')
       if name in self._unComputedTags:
           try:
               tagVal = tagToFunc[name](self._fieldDict[name])
           except KeyError:
               tagVal = self._fieldDict[name]
           setattr(self, name, tagVal)
           self._unComputedTags.remove(name)
       return object.__getattribute__(self, name)

      



I don't like rewriting __getattribute__

, but it works.

0


source


You are implementing the descriptor protocol and the descriptor belongs to the class instead of an instance of the class, so you cannot assign it to an instance attribute.



class Tag(object):
     def __init__(self, tag):
         self.tag = tag
         self.data = None

     def __get__(self, instance, owner):
         if not instance:  # if accessed with the class directly, ie. Record.T1, just return this descriptor
             return self
         if self.data is None:
             print "Reading data"
             self.data = range(10)
         return self.data

 class Record(object):
     T1 = Tag('T1')

      

0


source







All Articles