# -*- encoding: utf8 -*-

import warnings

class MetaStruct(type):
    '''Implementa uma classe com cara de struct de C.

    Ou seja, uma classe que guarda apenas dados e não possui métodos. A
    idéia é simplificar o trabalho de criação da classe usando apenas
    dados estáticos que são convertidos automaticamente para dados de
    instância. Os valores iniciais dos dados estáticos são usados para
    inicializar os dados de instâncias.

    Os únicos métodos que são implementados automaticamente são o
    construtor e __repr__. Quaisquer outros métodos especiais são
    delegados para as classes base até chegar em object se não forem sobre-
    escritos.

    Este código foi adaptado das p. 102 e 103 do "Python in a nutshell" de
    Alex Martelli.
    '''

    def __new__(metacls, classname, bases, classdict):
        '''Faz as coisas no new pois o dicionário inteiro será alterado.
        '''

        # Define inicialmente as implementações do construtor e __repr__.

        def __init__(self, **kw):
            '''O construtor iniaciliza os dados com os valores padrão
            e modifica aqueles repasados nos parâmetros.
            '''

            for k in self.__defaults__: setattr(self, k, self.__defaults__[k])
            # Pode ter um problema aqui. Qual é?
            for k in kw: setattr(self, k, kw[k])

        def __repr__(self):
            '''Impressão dos valores, restringe-se aos valores modificados.
            '''

            rep = ['%s=%r' % (k, getattr(self, k)) for k in self.__defaults__
                   if getattr(self, k) != self.__defaults__[k]
                  ]

            return '%s(%s)' % (classname, ', '.join(rep))

        # Agora começa o trabalho sujo.

        # Cria um novo dicionário para a classe.
        newdict = {'__defaults__': {}, '__init__': __init__,
                   '__repr__': __repr__}

        for k in classdict:
            if k.startswith('__') and k.endswith('__'):
                # Sobre-escreve métodos especiais que não sejam __init__
                # e __repr__.
                if k in newdict:
                    warnings.warn('Não posso mudar o atributo %r em %r.' %
                                 (k, classname))
                else:
                    # Sobre-escreve.
                    newdict[k] = classdict[k]
            else:
                newdict['__defaults__'][k] = classdict[k]

        # Usa o __new__ de type para cosntruir a classe.
        return type.__new__(metacls, classname, bases, newdict)

class Struct:
    '''Classe de conveniência para usar a sintaxe usual de herança.
    '''
    __metaclass__ = MetaStruct

# Exemplo de uso.
if __name__ == '__main__':
    class Ponto(Struct):
        '''Representa um ponto 3d com cor.
        '''
        x = 0.0
        y = 0.0
        z = 0.0
        cor = 'azul'

    p1 = Ponto()
    p2 = Ponto(z = 3.0, cor = 'preto')
    print p1
    print p2
    p1.y = 2.0
    print p1
    
    class Ponto2(Struct):
        def __init__(self, x, y):
            self.x = x
            self.y = y
            

    

    


syntax highlighted by Code2HTML, v. 0.9.1