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

2 投票
5 回答
1931 浏览
提问于 2025-04-15 23:53

我有个朋友想学编程,于是他把他之前上课写的所有程序都给了我。最后一个他写的程序是一个加密程序,我把他所有的程序都用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 个回答

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,)
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'导致你的元组解包失败时可能出现的错误(提示:把它们放在不同的异常捕获块中)。另外,你可能还想检查一下,如果用户没有对文件名的写权限,会发生什么。

你为什么不在这里使用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)
8

既然你提到了格式和风格,我很惊讶其他人还没有提到 PEP 8。这个文档主要是给那些想要被纳入标准库的模块提供指导,但我发现它的很多建议在其他地方也都适用。

撰写回答