Python程序的最佳风格:你有什么建议?

时间:2010-06-12 14:22:51

标签: python encryption coding-style

我的一个朋友想要帮助学习编程,所以他给了我为他以前的课程写的所有程序。他写的最后一个程序是加密程序,在用Python重写所有程序后,这就是他的加密程序结果(在添加我自己的要求之后)。

#! /usr/bin/env python

################################################################################

"""\
CLASS INFORMATION
-----------------
    Program Name: Program 11
    Programmer:   Stephen Chappell
    Instructor:   Stephen Chappell for CS 999-0, Python
    Due Date:     17 May 2010

DOCUMENTATION
-------------
    This is a simple encryption program that can encode and decode messages."""

################################################################################

import sys

KEY_FILE = 'Key.txt'

BACKUP = '''\
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO\
 PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
_@/6-UC'GzaV0%5Mo9g+yNh8b">Bi=<Lx [sQn#^R.D2Xc(\
Jm!4e${lAEWud&t7]H\`}pvPw)FY,Z~?qK|3SOfk*:1;jTrI''' #`

################################################################################

def main():
    "Run the program: loads key, runs processing loop, and saves key."
    encode_map, decode_map = load_key(KEY_FILE)
    try:
        run_interface_loop(encode_map, decode_map)
    except SystemExit:
        pass
    save_key(KEY_FILE, encode_map)

def run_interface_loop(encode_map, decode_map):
    "Shows the menu and runs the appropriate command."
    print('This program handles encryption via a customizable key.')
    while True:
        print('''\
MENU
====
(1) Encode
(2) Decode
(3) Custom
(4) Finish''')
        switch = get_character('Select: ', tuple('1234'))
        FUNC[switch](encode_map, decode_map)

def get_character(prompt, choices):
    "Gets a valid menu option and returns it."
    while True:
        sys.stdout.write(prompt)
        sys.stdout.flush()
        line = sys.stdin.readline()[:-1]
        if not line:
            sys.exit()
        if line in choices:
            return line
        print(repr(line), 'is not a valid choice.')

################################################################################

def load_key(filename):
    "Gets the key file data and returns encoding/decoding dictionaries."
    plain, cypher = open_file(filename)
    return dict(zip(plain, cypher)), dict(zip(cypher, plain))

def open_file(filename):
    "Load the keys and tries to create it when not available."
    while True:
        try:
            with open(filename) as file:
                plain, cypher = file.read().split('\n')
                return plain, cypher
        except:
            with open(filename, 'w') as file:
                file.write(BACKUP)

def save_key(filename, encode_map):
    "Dumps the map into two buffers and saves them to the key file."
    plain = cypher = str()
    for p, c in encode_map.items():
        plain += p
        cypher += c
    with open(filename, 'w') as file:
        file.write(plain + '\n' + cypher)

################################################################################

def encode(encode_map, decode_map):
    "Encodes message for the user."
    print('Enter your message to encode (EOF when finished).')
    message = get_message()
    for char in message:
        sys.stdout.write(encode_map[char] if char in encode_map else char)

def decode(encode_map, decode_map):
    "Decodes message for the user."
    print('Enter your message to decode (EOF when finished).')
    message = get_message()
    for char in message:
        sys.stdout.write(decode_map[char] if char in decode_map else char)

def custom(encode_map, decode_map):
    "Allows user to edit the encoding/decoding dictionaries."
    plain, cypher = get_new_mapping()
    for p, c in zip(plain, cypher):
        encode_map[p] = c
        decode_map[c] = p

################################################################################

def get_message():
    "Gets and returns text entered by the user (until EOF)."
    buffer = []
    while True:
        line = sys.stdin.readline()
        if line:
            buffer.append(line)
        else:
            return ''.join(buffer)

def get_new_mapping():
    "Prompts for strings to edit encoding/decoding maps."
    while True:
        plain = get_unique_chars('What do you want to encode from?')
        cypher = get_unique_chars('What do you want to encode to?')
        if len(plain) == len(cypher):
            return plain, cypher
        print('Both lines should have the same length.')

def get_unique_chars(prompt):
    "Gets strings that only contain unique characters."
    print(prompt)
    while True:
        line = input()
        if len(line) == len(set(line)):
            return line
        print('There were duplicate characters: please try again.')

################################################################################

# This map is used for dispatching commands in the interface loop.
FUNC = {'1': encode, '2': decode, '3': custom, '4': lambda a, b: sys.exit()}

################################################################################

if __name__ == '__main__':
    main()

对于所有那些Python程序员,请求您的帮助。如何格式化(不一定是编码改变以适合Python的样式指南?我的朋友不需要学习不正确的东西。如果你对代码有建议,也可以随意发布到这个wiki。


编辑:对于有兴趣的人,以下是我朋友给我翻译的C中的原始代码来源。

/******************************************************************************
 CLASS INFORMATION
 -----------------
   Program Name: Program 11 - Encodes/Decodes
   Programmer:   Evgnto nAl.Wi a 
   Instructor:   fsSP21 .C emgr2rCHoMal4 gyia ro-rm n ,
   Date Due:     MAY 4, 2010

 PLEDGE STATEMENT
 ---------------- 
   I pledge that all of the code in this program is my own _original_ work for 
   the spring 2010 semester. None of the code in this program has been copied 
   from anyone or anyplace unless I was specifically authorized to do so by my
   CS 214 instructor. This program has been created using properly licensed 
   software.
                                     Signed: ________________________________

 DOCUMENTATION
 -------------
   This program Encodes and decodes a program. It also gives you the option of
   making your own code.     
******************************************************************************/

#include <stdio.h>
#define ENCODE 1
#define DECODE 2 
#define OWNCODE 3
#define QUIT 4

void printPurpose();
void encode(char input[], char code1[]);
void decode(char input[], char code1[]);
void ownCode();

int main()
{
   char charIn;
   char code1[100] = "";
   char input[100] = ""; 

   int a;
   int b;
   int closeStatus;
   int number;
   a = 0;
   b = 0;

   FILE *code;

   printPurpose();

   if (!(code = fopen("code.txt", "r")))
   {
      printf("Error opening code.txt for reading");

      return 100; 
   }

   while ((charIn = fgetc(code)) != '\n')
   {   
      input[a] = charIn;
      a++;
   }
   while ((charIn = fgetc(code)) != EOF)
   { 
      code1[b] = charIn;
      b++;
   }  

   scanf("%d", &number);

   while (number != QUIT)
   {
      if (number == ENCODE)
         encode(input, code1);
      else if (number == DECODE)   
         decode(input, code1);
      else if (number == OWNCODE)
         ownCode();

      printf("\n\n");
      printPurpose();
      scanf("%d", &number);
   }
   printf("Thank you for using my program Mr. Halsey!!!!!!!!\n");

   closeStatus = fclose(code);

   if (closeStatus == EOF)
   {   
       printf("File close error.\n");
       return 201;
   }
   else
      printf("Exit successfully.\n");

   return 0;

}
/******************************************************************************
 Prints the purpose of the program
******************************************************************************/
void printPurpose()
{
   printf("This program Encodes, Decodes, and allows you to make your own"); 
   printf("code.\n");
   printf("Enter a 1 to Encode\n");
   printf("Enter a 2 to Decode\n");
   printf("Enter a 3 to make your own code\n");
   printf("Enter a 4 to Quit\n");
}

/******************************************************************************
 Encodes the sentence entered
******************************************************************************/
void encode(char input[], char code1[])
{
   char sentence[100] = "";
   char x;
   char y;

   int index;
   int index2;
   index = 0;

   printf("Enter your sentence\n");

   gets(sentence);
   gets(sentence);
   printf("Your encoded message is >");
   for (index2 = 0; (y = sentence[index2]) != '\0'; index2++)
   {
      while ((x = input[index]) != y)
         index++;
         if (x == y)
            printf("%c", code1[index]);        
         else 
            printf("error");
         index = 0;
   }

   return;
}

/******************************************************************************
 Decodes the sentence entered
******************************************************************************/
void decode(char input[], char code1[])
{
   char encodedMessage[100] = "";
   char x;
   char y;

   int index1;
   int index2;
   index2 = 0;

   printf("Enter encoded message\n");
   gets(encodedMessage);
   gets(encodedMessage);
   printf("Your decoded message is >");
   for (index1 = 0; (y = encodedMessage[index1]) != '\0'; index1++)
   {
      while (( x = code1[index2]) != y)
         index2++;
         if (x == y)
            printf("%c", input[index2]);         
         else
            printf("error");
         index2 = 0;
   }

   return;
}

/******************************************************************************
 Reads in your code and encodes / decodes 
******************************************************************************/
void ownCode()
{
   char charactersUsed[50] = "";
   char codeUsed[50]       = "";
   char sentence[50]       = "";

   int index1; 
   int index2;
   int number;
   int x;
   int y;
   index2 = 0;
   y      = 0;

   printf("Enter the characters you want to use.\n");
   printf("Use less than 50 characters and DO NOT REPEAT the characters.\n");

   gets(charactersUsed);
   gets(charactersUsed);

   printf("Enter the code for the characters.\n");
   gets(codeUsed);

   printf("\nEnter 1 to encode and 2 to decode or 3 to return to main menu.\n");
   scanf("%d", &number);

   while (number != OWNCODE)
   {
      if (number == ENCODE)
      {
         printf("Enter your sentence.\n");

         gets(sentence);
         gets(sentence);

         printf("Your encoded message is > ");

         for (index1 = 0; (y = sentence[index1]) != '\0'; index1++)
         {
            while (( x = charactersUsed[index2]) != y)
               index2++;
               if (x == y)
                  printf("%c", codeUsed[index2]);
               else
                  printf("error");
               index2 = 0;
         }
      }
      else if (number == DECODE)
      {
         printf("Enter your encoded sentence.\n");

         gets(sentence);
         gets(sentence);

         printf("Your decoded message is > ");

         for (index1 = 0; (y = sentence[index1]) != '\0'; index1++)
         {
            while (( x = codeUsed[index2]) != y)
               index2++;
            if (x == y)
               printf("%c", charactersUsed[index2]);
            else
               printf("error");
               index2 = 0;
         }
      }
      printf("\nEnter 1 to encode and 2 to decode or 3 to return to main menu.\n");
      scanf("%d", &number);
   }

   return;
}

5 个答案:

答案 0 :(得分:8)

由于您询问了格式和样式,我很惊讶没有其他人提到过PEP 8。它名义上是希望包含在标准库中的模块的指南,但我发现它的大部分指南几乎适用于所有地方。

答案 1 :(得分:6)

不要使用裸露的例外;

try:
    with open(filename) as file:
        plain, cypher = file.read().split('\n')
        return plain, cypher
**except:**
    with open(filename, 'w') as file:
        file.write(BACKUP)

使用裸露的例外几乎总是错误的,在这种情况下,这是错误的。捕获您想要捕获的异常,在这种情况下,很可能是IOException,如果文件不包含'\ n',则可能会出现错误,并且您的元组解包失败(提示:在单独的except子句中捕获它们)。此外,您可能有兴趣检查如果用户没有文件名的写入权限会发生什么。

你有没有在这里使用print和raw_input()的原因?

def get_character(prompt, choices):
    "Gets a valid menu option and returns it."
    while True:
        sys.stdout.write(prompt)
        sys.stdout.flush()
        line = sys.stdin.readline()[:-1]
        if not line:
            sys.exit()
        if line in choices:
            return line
        print(repr(line), 'is not a valid choice.')

您可能希望使用str.translate():

    sys.stdout.write(encode_map[char] if char in encode_map else char)

答案 2 :(得分:3)

在我看来,整个计划都值得怀疑。事物被划分为函数的选择有点笨拙,并且它被记录为类项目,而不是真实的系统。文档类型掩盖了整个平庸的组织。整个计划显示出对清晰度的冗长偏见。

经常看到构造'''\。三重引用构造的要点是回车和换行工作正常。没有必要\逃脱换行符。

我将解决我在例子中看到具体问题的功能:

该表格中是否应该存在get_message是值得商榷的。整个功能可以换成一行。

def get_message():
    "Gets and returns text entered by the user (until EOF)."
    return sys.stdin.read()

mainrun_interface_loopget_characterFUNC互动的方式令人困惑,而不是处理问题的最佳方式。特别是,在这种情况下,捕捉SystemExit并不是表示你的计划结束的好方法。

def main():
    encode_map, decode_map = load_key(KEY_FILE)
    run_interface_loop(encode_map, decode_map)
    save_key(KEY_FILE, encode_map)

def run_interface_loop():
    exit_picked = False
    while not exit_picked:
        print '''
MENU
====
(1) Encode
(2) Decode
(3) Custom
(4) Finish'''
        choice = sys.stdin.readline()
        if choice == '':
            break  # EOF was reached, no more asking the user for stuff
        choice = choice.strip()
        if choice == '1':
            encode(encode_map, decode_map) # This reads until EOF
            exit_picked = True # and so we might as well exit
        elif choice == '2':
            decode(encode_map, decode_map)
            exit_picked = True # Same here
        elif choice == '3':
            encode_map, decode_map = custom()
        elif choice == '4':
             exit_picked = True
        else:
             print "%s isn't a valid choice, try again." % (choice,)

答案 3 :(得分:2)

永远不要在函数内调用sys.exit()或引发SystemExit。相反,请改为提出其他例外。

答案 4 :(得分:1)

另请参阅Google的Python编码指南。它们大多与上面提到的PEP一致,但在某些方面有点限制。

Google Python Style Guide