Eu tive uma ajuda realmente incrível nas minhas perguntas anteriores para detectar patas e dedos dentro de uma pata , mas todas essas soluções funcionam apenas para uma medição de cada vez.
Agora eu tenho dados que consistem em:
- cerca de 30 cães;
- cada um possui 24 medições (divididas em vários subgrupos);
- cada medição possui pelo menos 4 contatos (um para cada pata) e
- cada contato é dividido em 5 partes e
- possui vários parâmetros, como tempo de contato, localização, força total etc.
Obviamente, colocar tudo em um grande objeto não vai ser suficiente, então imaginei que precisava usar classes em vez da atual quantidade de funções. Mas mesmo que eu tenha lido o capítulo sobre classes do Learning Python, não consigo aplicá-lo ao meu próprio código ( link do GitHub )
Também sinto que é bastante estranho processar todos os dados toda vez que quero obter algumas informações. Depois de conhecer os locais de cada pata, não há motivo para calcular isso novamente. Além disso, quero comparar todas as patas do mesmo cão para determinar qual contato pertence a qual pata (dianteira / traseira, esquerda / direita). Isso se tornaria uma bagunça se eu continuasse usando apenas funções.
Então, agora estou procurando conselhos sobre como criar classes que me permitam processar meus dados ( link para os dados compactados de um cão ) de maneira sensata.
fonte
Respostas:
Como projetar uma classe.
Anote as palavras. Você começou a fazer isso. Algumas pessoas não sabem e se perguntam por que têm problemas.
Expanda seu conjunto de palavras em declarações simples sobre o que esses objetos farão. Ou seja, anote os vários cálculos que você fará sobre essas coisas. Sua lista curta de 30 cães, 24 medições, 4 contatos e vários "parâmetros" por contato é interessante, mas apenas parte da história. Seus "locais de cada pata" e "comparam todas as patas do mesmo cão para determinar qual contato pertence a qual pata" são o próximo passo no design de objetos.
Sublinhe os substantivos. Seriamente. Algumas pessoas debatem o valor disso, mas acho que para os desenvolvedores de OO pela primeira vez isso ajuda. Sublinhe os substantivos.
Revise os substantivos. Substantivos genéricos como "parâmetro" e "medida" precisam ser substituídos por substantivos específicos e concretos que se aplicam ao seu problema no domínio do seu problema. Informações específicas ajudam a esclarecer o problema. Os genéricos simplesmente ignoram os detalhes.
Para cada substantivo ("contato", "pata", "cachorro" etc.), anote os atributos desse substantivo e as ações nas quais esse objeto se envolve. Não atalho isso. Todo atributo. "O conjunto de dados contém 30 cães", por exemplo, é importante.
Para cada atributo, identifique se esse é um relacionamento com um substantivo definido ou algum outro tipo de dado "primitivo" ou "atômico", como uma string ou um float ou algo irredutível.
Para cada ação ou operação, você deve identificar qual substantivo tem a responsabilidade e quais substantivos apenas participam. É uma questão de "mutabilidade". Alguns objetos são atualizados, outros não. Objetos mutáveis devem possuir total responsabilidade por suas mutações.
Nesse ponto, você pode começar a transformar substantivos em definições de classe. Alguns substantivos coletivos são listas, dicionários, tuplas, conjuntos ou nomeados, e você não precisa fazer muito trabalho. Outras classes são mais complexas, devido a dados derivados complexos ou a alguma atualização / mutação realizada.
Não se esqueça de testar cada classe isoladamente usando o mais unido.
Além disso, não há lei que diga que as classes devem ser mutáveis. No seu caso, por exemplo, você quase não possui dados mutáveis. O que você tem são dados derivados, criados por funções de transformação do conjunto de dados de origem.
fonte
Os conselhos a seguir (semelhantes aos conselhos de @ S.Lott) são do livro Beginning Python: From Novice to Professional
Para refinar a turma, o livro também aconselha que possamos fazer o seguinte:
fonte
Eu gosto da abordagem TDD ... Então comece escrevendo testes para o que você quer que seja o comportamento. E escreva o código que passa. Neste ponto, não se preocupe muito com o design, basta obter um conjunto de testes e software que passam. Não se preocupe se você terminar com uma única classe grande e feia, com métodos complexos.
Às vezes, durante esse processo inicial, você encontrará um comportamento difícil de testar e precisa ser decomposto, apenas para testabilidade. Isso pode ser uma dica de que uma classe separada é necessária.
Então a parte divertida ... refatoração. Depois de ter o software funcionando, você pode ver as peças complexas. Muitas vezes, pequenos focos de comportamento se tornam aparentes, sugerindo uma nova classe, mas, se não, procure maneiras de simplificar o código. Extraia objetos de serviço e objetos de valor. Simplifique seus métodos.
Se você estiver usando o git corretamente (você está usando o git, não é?), Poderá experimentar rapidamente uma decomposição específica durante a refatoração, abandoná-lo e reverter se isso não simplificar as coisas.
Ao escrever o código de trabalho testado primeiro, você deve obter uma visão íntima do domínio do problema que não conseguiu obter facilmente com a abordagem do design primeiro. Os testes de escrita e o código levam você a superar a paralisia "por onde começar".
fonte
Toda a idéia do design do OO é fazer com que seu código seja mapeado para o seu problema; portanto, quando, por exemplo, você desejar o primeiro passo de um cachorro, faça algo como:
Agora, pode ser que, para o seu caso, você precise ler seu arquivo de dados brutos e calcular os locais dos passos. Tudo isso pode estar oculto na função Passo a Passo () para que isso aconteça apenas uma vez. Algo como:
[Agora, esse é um tipo de padrão de cache. Na primeira vez em que ele lê os dados dos passos, nas vezes subsequentes, obtém-os de self._footsteps.]
Mas sim, obter o design correto do OO pode ser complicado. Pense mais sobre o que você deseja fazer com seus dados e isso informará quais métodos você precisará aplicar a quais classes.
fonte
Escrever seus substantivos, verbos e adjetivos é uma ótima abordagem, mas eu prefiro pensar no design de classe como fazer a pergunta que dados devem ser ocultados ?
Imagine que você tinha um
Query
objeto e umDatabase
objeto:O
Query
objeto ajudará você a criar e armazenar uma consulta - armazenar, é a chave aqui, pois uma função pode ajudá-lo a criar uma com a mesma facilidade. Talvez você poderia ficar:Query().select('Country').from_table('User').where('Country == "Brazil"')
. Não importa exatamente a sintaxe - esse é o seu trabalho! - a chave é que o objeto está ajudando a ocultar algo , neste caso os dados necessários para armazenar e gerar uma consulta. O poder do objeto vem da sintaxe de usá-lo (neste caso, um encadeamento inteligente) e da necessidade de não saber o que ele armazena para fazê-lo funcionar. Se feito corretamente, oQuery
objeto pode gerar consultas para mais de um banco de dados. Internamente, ele armazenava um formato específico, mas poderia ser facilmente convertido para outros formatos ao produzir (Postgres, MySQL, MongoDB).Agora vamos pensar no
Database
objeto. O que isso esconde e armazena? Bem, claramente, ele não pode armazenar todo o conteúdo do banco de dados, pois é por isso que temos um banco de dados! Então qual é o ponto? O objetivo é ocultar como o banco de dados funciona das pessoas que usam oDatabase
objeto. Boas classes simplificarão o raciocínio ao manipular o estado interno. Para esseDatabase
objeto, você pode ocultar como as chamadas de rede funcionam, ou consultas em lote ou atualizações, ou fornecer uma camada de armazenamento em cache.O problema é que esse
Database
objeto é ENORME. Ele representa como acessar um banco de dados; portanto, por baixo das cobertas, ele pode fazer tudo e qualquer coisa. Claramente, é difícil lidar com redes, cache e lotes, dependendo do sistema, portanto, escondê-los seria muito útil. Mas, como muitas pessoas observam, um banco de dados é incrivelmente complexo e, quanto mais longe das chamadas brutas de banco de dados que você recebe, mais difícil é ajustar o desempenho e entender como as coisas funcionam.Essa é a troca fundamental da OOP. Se você escolher a abstração correta, simplificará a codificação (String, Matriz, Dicionário), se você escolher uma abstração muito grande (Banco de Dados, EmailManager, NetworkingManager), poderá se tornar muito complexo para realmente entender como funciona ou o que Espero. O objetivo é ocultar a complexidade , mas é necessária alguma complexidade. Uma boa regra é começar a evitar
Manager
objetos e, em vez disso, criar classes parecidasstructs
- tudo o que eles fazem é armazenar dados, com alguns métodos auxiliares para criar / manipular os dados para facilitar sua vida. Por exemplo, no caso deEmailManager
iniciar com uma função chamadasendEmail
que pega umEmail
objeto. Este é um ponto de partida simples e o código é muito fácil de entender.Como no seu exemplo, pense em quais dados precisam estar juntos para calcular o que você está procurando. Se você quisesse saber a que distância um animal estava andando, por exemplo, você poderia ter
AnimalStep
eAnimalTrip
(coleção de AnimalSteps) classes. Agora que cada viagem tem todos os dados da etapa, deve ser capaz de descobrir coisas sobre isso, talvezAnimalTrip.calculateDistance()
faça sentido.fonte
Depois de analisar o código vinculado, parece-me que é melhor não criar uma classe Dog neste momento. Em vez disso, você deve usar Pandas e quadros de dados . Um quadro de dados é uma tabela com colunas. Você trama de dados teria colunas, tais como:
dog_id
,contact_part
,contact_time
,contact_location
, etc. Pandas utiliza matrizes Numpy nos bastidores, e tem muitos métodos de conveniência para você:my_measurements['dog_id']=='Charly'
my_measurements.save('filename.pickle')
pandas.read_csv()
vez de ler manualmente os arquivos de texto.fonte