Class return key and value of my attributes

2

I have the following class:

class RegC100:
    def __init__(self,linha):
        def convFloat(valor):
            if (valor != ''):
                valor = float(valor.replace(",", "."))
                return valor
            else:
                return ''

        def convInt(valor):
            if(valor !=''):
                valor = int(valor)
                return valor
            else:
                return ''

        def convData(valor):
            if(valor != ''):
                valorDia = valor[0:2]
                valorMes = valor[2:4]
                valorAno = valor[4:8]
                valor = str(valorAno+'-'+valorMes+'-'+valorDia)
                return valor
            else:
                return ''

        self.linha = linha
        l = linha.split('|')
        self.reg = l[1]
        self.indOper = l[2]
        self.indEmit = l[3]
        self.codPart = l[4]
        self.codMod = l[5]
        self.codSit = l[6]
        self.ser = convInt(l[7])
        self.numDoc = convInt(l[8])
        self.chvNfe = l[9]
        self.dtDoc = convData(l[10])
        self.dtES = l[11]
        self.vlDoc = convFloat(l[12])
        self.indPgto = convInt(l[13])
        self.vlDesc = convFloat(l[14])
        self.vlAbatNt = convFloat(l[15])
        self.vlMerc = convFloat(l[16])
        self.indFrt = l[17]
        self.vlFrt = convFloat(l[18])
        self.vlSeg = convFloat(l[19])
        self.vlOutDa = convFloat(l[20])
        self.vlBcIcms = convFloat(l[21])
        self.vlIcms = convFloat(l[22])
        self.vlBcIcmsSt = convFloat(l[23])
        self.vlIcmsSt = convFloat(l[24])
        self.vlIpi = convFloat(l[25])
        self.vlPis = convFloat(l[26])
        self.vlCofins = convFloat(l[27])
        self.vlPisSt = convFloat(l[28])
        self.vlCofinsSt = convFloat(l[29])

which receives the following data:

|C100|0|1|99900821|55|00|2|000021255|23121207792435000327550020000212551005939150|20122012|20122012|899,00|2|||899,00|0||||0|0||||||||

I would like to make this my class return the values of my attributes as key and value or it may be an option to prepare this data to add to the database without having to be typing each attribute again. Is there any way? As I am very new to this python world, I also wanted to know if there is anything I can improve on in my class. Thanks in advance for your help.

    
asked by anonymous 24.01.2017 / 13:32

2 answers

5

A practical way is to add the to_dict method to the class:

def to_dict(self):
    return self.__dict__

To get values as a key / value, just invoke the method: obj.to_dict() .

See the official documentation for __dict__ here .

  

See the working code here .

    
24.01.2017 / 13:43
5

Yes - there are several ways to do this, and the more sensible ones will work together with your need to assign multiple fields from a formatted line.

Of course objects in Python have the __dict__ attribute, as in the other responses, which stores all the attributes of the object for you. Using __dict__ directly however is not something much done on a daily basis, because it does not discriminate who is who - for example, its self.linha attribute will be present in __dict__ along with the other fields. >

A very nice mechanism to do this kind of thing is to create a base class with a mechanism to access several fields by name from a configuration of the class itself that indicates characteristics of each field.

In this case, you only need the name of the field, its position on the line, and conversion function. The position can be given by the order you place the fields - then you can agree for example a class attribute fields , which is a sequence of pairs (name, conversion function), and put a method that populates and another that retrieves all those values.

class Base:
    fields = [('reg', str), ('indOper', str), ..., ('vlDoc',convFloat), ...]
    def __init__(self, linha):
          self.linha = linha.split('|')

And in fact, it's more practical if this "_fields" is a dictionary containing information about that field. As Python allows you to execute code in class creation, you can declare _fields in the most convenient way - for example, as a single string, and create the appropriate dictionary with a function:

converters = {'float': convFloat,  'int': convInt,  'date': convDate,  'str': str }

def create_fields(spec):
     from collections import OrderedDict
     fields = OrderedDict()
     for i, field_spec in enumerate(spec.split(',')):
          field_spec = field_spec.strip()  # remove espaços em branco
          field_name, converter_name = field_spec.split()
          # em cada campo guarda sua posição e a função de conversão:
          fields[field_name] = i, converters[converter_name] 

And now you can declare each class as:

class reg100(Base):
     fields = create_fields("reg str, indOper str, ..., vlDoc float, ...")

There is also __getattr__ : it is a special function that is called when Python does not find an attribute in an object - there Python calls this method by passing the name of the attribute. This allows the attribute name to be configured in some other way than as an explicitly settable in hardcoded code.

    def __getattr__(self, attr):
        try:       
            field = self.fields[attr]
        except IndexError:
            raise AttributeError
        return field[1](self.linha[field[0]]

With this, and only one row per derived class (and using the __init__ and __getattr__ function declared in the base), you define all classes in your project. And now, with that information in place, you can define common methods at will to retrieve all fields.

For example, to have all fields converted as a list - and more, with the decorator "property" can make it look like an attribute of the class:

@property
def field_values(self):
     return [converter(self.linha[position]) for position, converter in self.fields.values()]

@property
def field_dict(self):
    return {attr_name:value[1](self.linha[value[0]) for name, value in self.fields.items()   }

Place these 4 methods in your base class, and then declare your various classes as:

class Reg100(Base):
    fields = create_fields(""reg str, indOper str, ..., vlDoc float, ...")

and will ration all fields:

>>> r = Reg100(linha)
>>> r.reg 
(retorna o valor correspontente)
>>> r.field_values
(retorna uma lista com os valores, já convertidos para tipos Python)

Remembering that since your goal is to insert everything into a relational database - you could use an ORM like SQLALchmy to make that part easier. ORM alone will not assign linah values to class attributes, or prevent you from digitizing a more complex specification for each field - but you can combine the use of ORM with hints from these hints.

    
24.01.2017 / 14:57