# Solução de problemas

Nós exploramos várias partes da linguagem Python e agora vamos dar uma olhada em como todas essas partes se encaixam, projetando e escrevendo um programa que faz algo util. A idéia é aprender a escrever um script Python sozinho.

O problema

O problema que queremos resolver é:

Eu quero um programa que cria um backup de todos os meus arquivos importantes.

Embora, este seja um problema simples, não há informações suficientes para que possamos começar a solução. Um pouco mais analises É necessário. Por exemplo, como especificamos quais os arquivos devem ser copiados? Como eles estão armazenados? Onde eles estão armazenados?

Depois de analisar o problema corretamente, nós desenhamos nosso programa. Nós fazemos uma lista de coisas sobre como o nosso programa deve funcionar. Nesse caso, criei a seguinte lista sobre como EU quer que funcione. Se você fizer o projeto, você não pode apresentar o mesmo tipo de análise, uma vez que cada pessoa tem sua própria maneira de fazer as coisas, então está perfeitamente bem.

  • Os arquivos e diretórios a serem copiados são especificados em uma lista.
  • O backup deve ser armazenado em um diretório de backup principal.
  • Os arquivos são copiados em um arquivo zip.
  • O nome do arquivo zip é a data e a hora atuais.
  • Usamos o padrão zip comando disponível por padrão em qualquer distribuição padrão GNU / Linux ou Unix. Observe que você pode usar qualquer comando de arquivamento desejado, desde que tenha uma interface de linha de comando.

Para usuários do Windows

Usuários do Windows podem instalar o comando zip da Página do projeto GnuWin32 e adicionar C:\Program Files\GnuWin32\bin ao seu sistema PATH variável de ambiente, semelhante a o que fizemos por reconhecer o próprio comando python.

A solução

Como o design do nosso programa é agora razoavelmente estável, podemos escrever o código que é uma implementação da nossa solução.

Salvar como backup_ver1.py:

import os
import time

# 1. The files and directories to be backed up are
# specified in a list.
# Example on Windows:
# source = ['"C:\\My Documents"']
# Example on Mac OS X and Linux:
source = ['/Users/swa/notes']
# Notice we have to use double quotes inside a string
# for names with spaces in it.  We could have also used
# a raw string by writing [r'C:\My Documents'].

# 2. The backup must be stored in a
# main backup directory
# Example on Windows:
# target_dir = 'E:\\Backup'
# Example on Mac OS X and Linux:
target_dir = '/Users/swa/backup'
# Remember to change this to which folder you will be using

# 3. The files are backed up into a zip file.
# 4. The name of the zip archive is the current date and time
target = target_dir + os.sep + \
         time.strftime('%Y%m%d%H%M%S') + '.zip'

# Create target directory if it is not present
if not os.path.exists(target_dir):
    os.mkdir(target_dir)  # make directory

# 5. We use the zip command to put the files in a zip archive
zip_command = 'zip -r {0} {1}'.format(target,
                                      ' '.join(source))

# Run the backup
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup FAILED')

Saida:

$ python backup_ver1.py
Zip command is:
zip -r /Users/swa/backup/20140328084844.zip /Users/swa/notes
Running:
  adding: Users/swa/notes/ (stored 0%)
  adding: Users/swa/notes/blah1.txt (stored 0%)
  adding: Users/swa/notes/blah2.txt (stored 0%)
  adding: Users/swa/notes/blah3.txt (stored 0%)
Successful backup to /Users/swa/backup/20140328084844.zip

Agora, estamos em teste fase em que testamos que nosso programa funciona corretamente. Se não se comportar como esperado, então devemos debug nosso programa, ou seja, remova os bugs (erros) do programa.

Se o programa acima não funcionar para você, copie a linha impressa após a Zip command is linha na saída, cole-o no shell (em GNU / Linux e Mac OS X) / cmd (no Windows), veja qual é o erro e tente corrigi-lo. Verifique também o manual do comando zip sobre o que poderia estar errado. Se esse comando for bem-sucedido, o problema poderá estar no próprio programa Python, então verifique se ele corresponde exatamente ao programa escrito acima.

Como funciona

Você notará como nós convertimos nossos design em código em um passo a passo.

Fazemos uso do os e time módulos primeiro importando-os. Em seguida, especificamos os arquivos e diretórios a serem copiados na lista source. O diretório de destino é onde armazenamos todos os arquivos de backup e isso é especificado no target_dir variável. O nome do arquivo zip que vamos criar é a data e a hora atuais que geramos usando o time.strftime() função. Também terá a .zip extensão e será armazenada no target_dir directory.

Observe o uso do os.sep variável - isso dá o separador de diretório de acordo com seu sistema operacional, ou seja, será '/' em GNU / Linux, Unix, MacOS, e será '\\' no Windows. Usando os.sep Em vez de esses personagens diretamente, o nosso programa será portátil e funcionará em todos esses sistemas.

A função time.strftime() leva uma especificação como a que usamos no programa acima. A %Y especificação será substituída pelo ano pelo século. O %m a especificação será substituída pelo mês como um número decimal entre 01 e 12 e assim por diante. A lista completa dessas especificações pode ser encontrada no Python Reference Manual.

Criamos o nome do arquivo zip alvo usando o operador de adição que concentra as cordas, isto é, junta as duas cordas e retorna uma nova. Então, criamos uma string zip_command que contém o comando que vamos executar. Você pode verificar se esse comando funciona executando-o no shell (terminal GNU / Linux ou prompt do DOS).

O zip comando que estamos usando tem algumas opções disponíveis, e uma dessas opções é -r. A opção -r especifica que o comando zip deve funcionar recursivamente para diretórios, isto é, deve incluir todos os subdiretórios e arquivos. As opções são seguidas pelo nome do arquivo zip para criar, seguido pela lista de arquivos e diretórios para backup. Nós convertemos o source listar uma string usando o join método de cordas que já vimos como usar.

Então, finalmente, run o comando usando o os.system function que executa o comando como se fosse executado a partir do sistema isto é, no shell - ele retorna 0 if the command was successfully, else it returns an error number.

Dependendo do resultado do comando, imprimimos a mensagem apropriada de que o backup falhou ou conseguiu.

É isso mesmo, criamos um script para fazer um backup de nossos arquivos importantes!

Nota para usuários do Windows

Em vez de duas seqüências de escape da barra invertida, você também pode usar cordas cruas. Por exemplo, use 'C:\\Documents' ou r'C:\Documents'. No entanto, não use 'C:\Documents' since you end up using an unknown escape sequence \D.

Agora que temos um script de backup de trabalho, podemos usá-lo sempre que quisermos fazer um backup dos arquivos. Isso é chamado de operação fase ou o deployment fase do software.

O programa acima funciona corretamente, mas (geralmente) os primeiros programas não funcionam exatamente como você espera. Por exemplo, pode haver problemas se você não projetou o programa corretamente ou se cometeu um erro ao digitar o código, etc. De forma apropriada, você terá que voltar para a fase de projeto ou terá que depurar seu programa.

Segunda Versão

A primeira versão do nosso script funciona. No entanto, podemos fazer alguns aprimoramentos para que ele possa funcionar melhor diariamente. Isso é chamado de manutenção fase do software.

Um dos refinamentos que eu senti foi útil é um melhor mecanismo de nomeação de arquivos - usando o tempo como o nome do arquivo dentro de um diretório com o atual data como um diretório dentro do diretório de backup principal. A primeira vantagem é que seus backups são armazenados de forma hierárquica e, portanto, é muito mais fácil de gerenciar. A segunda vantagem é que os nomes dos arquivos são muito mais curtos. A terceira vantagem é que diretórios separados irão ajudá-lo a verificar se você fez um backup para cada dia, já que o diretório seria criado somente se você fizer um backup nesse dia.

Salvar como backup_ver2.py:

import os
import time

# 1. The files and directories to be backed up are
# specified in a list.
# Example on Windows:
# source = ['"C:\\My Documents"', 'C:\\Code']
# Example on Mac OS X and Linux:
source = ['/Users/swa/notes']
# Notice we had to use double quotes inside the string
# for names with spaces in it.

# 2. The backup must be stored in a
# main backup directory
# Example on Windows:
# target_dir = 'E:\\Backup'
# Example on Mac OS X and Linux:
target_dir = '/Users/swa/backup'
# Remember to change this to which folder you will be using

# Create target directory if it is not present
if not os.path.exists(target_dir):
    os.mkdir(target_dir)  # make directory

# 3. The files are backed up into a zip file.
# 4. The current day is the name of the subdirectory
# in the main directory.
today = target_dir + os.sep + time.strftime('%Y%m%d')
# The current time is the name of the zip archive.
now = time.strftime('%H%M%S')

# The name of the zip file
target = today + os.sep + now + '.zip'

# Create the subdirectory if it isn't already there
if not os.path.exists(today):
    os.mkdir(today)
    print('Successfully created directory', today)

# 5. We use the zip command to put the files in a zip archive
zip_command = 'zip -r {0} {1}'.format(target,
                                      ' '.join(source))

# Run the backup
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup FAILED')

Saida:

$ python backup_ver2.py
Successfully created directory /Users/swa/backup/20140329
Zip command is:
zip -r /Users/swa/backup/20140329/073201.zip /Users/swa/notes
Running:
  adding: Users/swa/notes/ (stored 0%)
  adding: Users/swa/notes/blah1.txt (stored 0%)
  adding: Users/swa/notes/blah2.txt (stored 0%)
  adding: Users/swa/notes/blah3.txt (stored 0%)
Successful backup to /Users/swa/backup/20140329/073201.zip

Como funciona

A maior parte do programa continua a ser a mesma. As mudanças são que nós verificamos se há um diretório com o dia atual como seu nome dentro do diretório de backup principal usando a função os.path.exists. Se não existe, criamos isso usando a função os.mkdir.

Terceira Versão

A segunda versão funciona bem quando faço muitos backups, mas quando há muitos backups, acho difícil diferenciar o que os backups foram para! Por exemplo, talvez eu tenha feito algumas mudanças importantes em um programa ou apresentação, então eu quero associar o que essas alterações são com o nome do arquivo zip. Isso pode ser facilmente alcançado, anexando um comentário fornecido pelo usuário ao nome do arquivo zip.

AVISO: O programa a seguir não funciona, por isso não fique alarmado, por favor siga, porque há uma lição aqui.

Salvar como backup_ver3.py:

import os
import time

# 1. The files and directories to be backed up are
# specified in a list.
# Example on Windows:
# source = ['"C:\\My Documents"', 'C:\\Code']
# Example on Mac OS X and Linux:
source = ['/Users/swa/notes']
# Notice we had to use double quotes inside the string
# for names with spaces in it.

# 2. The backup must be stored in a
# main backup directory
# Example on Windows:
# target_dir = 'E:\\Backup'
# Example on Mac OS X and Linux:
target_dir = '/Users/swa/backup'
# Remember to change this to which folder you will be using

# Create target directory if it is not present
if not os.path.exists(target_dir):
    os.mkdir(target_dir)  # make directory

# 3. The files are backed up into a zip file.
# 4. The current day is the name of the subdirectory
# in the main directory.
today = target_dir + os.sep + time.strftime('%Y%m%d')
# The current time is the name of the zip archive.
now = time.strftime('%H%M%S')

# Take a comment from the user to
# create the name of the zip file
comment = input('Enter a comment --> ')
# Check if a comment was entered
if len(comment) == 0:
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' + 
        comment.replace(' ', '_') + '.zip'

# Create the subdirectory if it isn't already there
if not os.path.exists(today):
    os.mkdir(today)
    print('Successfully created directory', today)

# 5. We use the zip command to put the files in a zip archive
zip_command = "zip -r {0} {1}".format(target,
                                      ' '.join(source))

# Run the backup
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup FAILED')

Saida:

$ python backup_ver3.py
  File "backup_ver3.py", line 39
    target = today + os.sep + now + '_' +
                                        ^
SyntaxError: invalid syntax

Como isso (não funciona)

Este programa não funciona! Python diz que há um erro de sintaxe, o que significa que o script não satisfaz a estrutura que a Python espera ver. Quando observamos o erro dado pela Python, também nos informa o local onde detectou o erro também. Então começamos depurar nosso programa dessa linha.

Com uma observação cuidadosa, vemos que a única linha lógica foi dividida em duas linhas físicas, mas não especificamos que essas duas linhas físicas pertencem juntas. Basicamente, a Python encontrou o operador de adição () sem qualquer operando naquela linha lógica e, portanto, não sabe como continuar. Lembre-se de que podemos especificar que a linha lógica continua na próxima linha física pelo uso de uma barra invertida no final da linha física. Então, fazemos essa correção para o nosso programa. Esta correção do programa quando encontramos erros é chamada corrigindo erro.

Quarta Versão

Save as backup_ver4.py:

import os
import time

# 1. The files and directories to be backed up are
# specified in a list.
# Example on Windows:
# source = ['"C:\\My Documents"', 'C:\\Code']
# Example on Mac OS X and Linux:
source = ['/Users/swa/notes']
# Notice we had to use double quotes inside the string
# for names with spaces in it.

# 2. The backup must be stored in a
# main backup directory
# Example on Windows:
# target_dir = 'E:\\Backup'
# Example on Mac OS X and Linux:
target_dir = '/Users/swa/backup'
# Remember to change this to which folder you will be using

# Create target directory if it is not present
if not os.path.exists(target_dir):
    os.mkdir(target_dir)  # make directory

# 3. The files are backed up into a zip file.
# 4. The current day is the name of the subdirectory
# in the main directory.
today = target_dir + os.sep + time.strftime('%Y%m%d')
# The current time is the name of the zip archive.
now = time.strftime('%H%M%S')

# Take a comment from the user to
# create the name of the zip file
comment = input('Enter a comment --> ')
# Check if a comment was entered
if len(comment) == 0:
    target = today + os.sep + now + '.zip'
else:
    target = today + os.sep + now + '_' + \
        comment.replace(' ', '_') + '.zip'

# Create the subdirectory if it isn't already there
if not os.path.exists(today):
    os.mkdir(today)
    print('Successfully created directory', today)

# 5. We use the zip command to put the files in a zip archive
zip_command = 'zip -r {0} {1}'.format(target,
                                      ' '.join(source))

# Run the backup
print('Zip command is:')
print(zip_command)
print('Running:')
if os.system(zip_command) == 0:
    print('Successful backup to', target)
else:
    print('Backup FAILED')

Saida:

$ python backup_ver4.py
Enter a comment --> added new examples
Zip command is:
zip -r /Users/swa/backup/20140329/074122_added_new_examples.zip /Users/swa/notes
Running:
  adding: Users/swa/notes/ (stored 0%)
  adding: Users/swa/notes/blah1.txt (stored 0%)
  adding: Users/swa/notes/blah2.txt (stored 0%)
  adding: Users/swa/notes/blah3.txt (stored 0%)
Successful backup to /Users/swa/backup/20140329/074122_added_new_examples.zip

Como funciona

Este programa agora funciona! Deixe-nos passar pelos aprimoramentos reais que fizemos na versão 3. Aceitamos os comentários do usuário usando o input Funcione e depois verifique se o usuário realmente entrou algo descobrindo o comprimento da entrada usando o len função. Se o usuário acabou de pressionar enter sem entrar em nada (talvez fosse apenas um backup rotineiro ou nenhuma alteração especial fosse feita), então, seguimos como fizemos antes.

No entanto, se um comentário foi fornecido, isso é anexado ao nome do arquivo zip logo antes da .zip extensão. Observe que estamos substituindo espaços no comentário com underscores - isso ocorre porque gerenciar nomes de arquivos sem espaços é muito mais fácil.

Mais refinamentos

A quarta versão é um script de trabalho satisfatório para a maioria dos usuários, mas sempre há margem para melhorias. Por exemplo, você pode incluir um verbosidade nível para o comando zip, especificando um -v opção para tornar o seu programa mais conversador ou uma -q opção para fazê-lo quiet.

Outro possível aprimoramento seria permitir que arquivos e diretórios adicionais fossem passados para o script na linha de comando. Podemos obter esses nomes com sys.argv list e podemos adicioná-los ao nosso source lista usando o extend método fornecido pela classe list.

O refinamento mais importante seria não usar o os.system maneira de criar arquivos e, em vez disso, usar o zipfile ou tarfile módulos internos para criar esses arquivos. Eles fazem parte da biblioteca padrão e já estão disponíveis para você usar sem dependências externas no programa zip para estar disponível no seu computador.

No entanto, eu tenho usado o os.system maneira de criar um backup nos exemplos acima, puramente para fins pedagógicos, para que o exemplo seja simples o suficiente para ser entendido por todos, mas real o suficiente para ser útil.

Você pode tentar escrever a quinta versão que usa o zipfile módulo ao invés de chamar os.system?

O processo de desenvolvimento de software

Passamos agora pelos vários fases no processo de redação de um software. Essas fases podem ser resumidas da seguinte forma:

  1. What (Analysis)
  2. How (Design)
  3. Do It (Implementation)
  4. Test (Testing and Debugging)
  5. Use (Operation or Deployment)
  6. Maintain (Refinement)

Uma maneira recomendada de escrever programas é o procedimento que seguimos na criação do script de backup: faça a análise e o design. Comece a implementar com uma versão simples. Teste e depua-o. Use-o para garantir que ele funciona como esperado. Agora, adicione os recursos desejados e continue a repetir o ciclo Do-Test-Use quantas vezes for necessário.

Lembrar:

O software é cultivado, não construído. -- Bill de hÓra

Resumo

Nós vimos como criar nossos próprios programas / scripts Python e as várias etapas envolvidas na escrita de tais programas. Você pode achar útil criar seu próprio programa exatamente como fizemos neste capítulo para que você se sinta confortável com o Python, bem como com a resolução de problemas.

Em seguida, vamos discutir a programação orientada a objetos.