CMU 15-112: Fundamentals of Programming and Computer Science
Class Notes: Style
Learning Goal: learn and practice good style to improve your code. In particular:
- Use style rules for clarity so that others (and you!) can understand your code
- Use style rules for robustness to make your program easier to test, debug, and iterate
- Recognize good style vs poor style and understand its importance
Some Style Facts:
- Good style is...
- Both an art and a science.
- Open to some debate, yet mostly agreed upon.
- The product of years of suffering due to bad style.
- Good style reduces time spent debugging, and...
- Good style reduces errors, and...
- Some Style Guides (which are not OUR style guide):
- The Official PEP-8 Python Style Guide
- Google Python Style Guide
- Many, many others...
15-112 Style Rubric
- Clarity Rules
- Ownership
You must include your name, andrewId, and section in a comment at the top of every file you submit.
This is good practice for later in life, when you will want to document all code that you contribute to projects.
2-point error: not writing your name/andrewId/section in a submitted file - Comments
You should write concise, clear, and informative comments that supplement your code and improve understanding.
Comments should be included with any piece of code that is not self-documenting.
Comments should also be included at the start of every function (including helper functions).
Comments should not be written where they are not needed.
5-point error: not writing any comments at all.
2-point error: writing too many or too few comments, or writing bad comments.
Here is an example comment for a palindrome function from the string notes:
#Return True if the characters are identical at forward and reversed indices def isPalindrome3(s): for i in range(len(s)): if (s[i] != s[-1-i]): return False return True - Helper Functions (Top-Down Design)
You should use top-down design to break large programs down into helper functions where appropriate.
This also means that no function should become too long (and therefore unclear).
5-point error: not using any helper functions (where helper functions are needed).
2-point error: using too many or too few helper functions.
2-point error: writing a function that is more than 30 lines long.- Exceptions: blank lines and comments do not count towards this line limit.
- Variable Names
Use meaningful variable and function names (whenever possible).
Variables and functions should be written in the camelCase format. In this format, the first letter is in lowercase, and all following words are uppercased (eg: tetrisPiece).
Meaningful variable names should make it easy for you and anyone reading your code to understand what that variable does. For example, some good variable names for a graphics problem with a flag might include numStars, numStripes, stripeColor, or stripeWidth. You can probably guess what each of those variables does. On the other hand, it can be hard to know what variables named a, b, and c do, especially if you haven't written any comments either!
Sometimes it's ok to use short variable names if it is still relatively obvious what they do. For example, cx and cy commonly indicate the center coordinates of a circle, and r is probably the circle's radius. If you have many circles, though, you might want to clarify those. You'll figure this out with practice!
Variable names should not overwrite built-in function names; for example, str is a bad name for a string variable. Common built-in keywords to avoid include dict, dir, id, input, int, len, list, map, max, min, next, object, set, str, sum, and type. If you're using Pyzo and want to make it highlight built-in names, run this script in Pyzo to change the IDE's settings.
5-point error: not having any meaningful variable names (assuming variables are used).
2-point error: using some non-meaningful variable names.- Exceptions: i/j for index/iterator, c for character, s for string, and n/x/y for number.
2-point error: using a built-in function name as a variable. - Unused Code
Your code should not include any dead code (code that will never be executed).
Additionally, all debugging code should be removed once your program is complete, even if it has been commented out.
2-points error: having any dead or debugging code. - Formatting
Your code formatting should make your code readable. This includes:
- Not exceeding 80 characters in any one line (including comments!).
- Indenting consistently. Use spaces, not tabs, with 4 spaces per indent level (most editors let you map tabs to spaces automatically).
- Using consistent whitespace throughout your code.
- Good whitespace: x=y+2, x = y+2, or x = y + 2
- Bad whitespace: x= y+2, x = y +2, or x = y + 2
- Ownership
- Robustness Rules
- Test Functions
You should always write test functions for each function that is reasonably testable.
At the beginning of the semester we provide some test functions in the homework starter files, but we will phase these out over time. You should start supplementing our tests with your own as of hw3.
5-point error: not writing any test functions.
2-point error: not writing enough test functions.- Exceptions: you do not need to test interactive, random, graphics, data initialization, or event functions.
- Efficiency
As this is an introductory course, we do not expect you to write the most efficient solutions generally. However, your code may not be "grossly" inefficient, especially if you had simple, clear, and reasonably efficient options to choose from.
5-point error: writing a function that works correctly but takes more than 30 seconds to test (with some exceptions).
2-point error: writing a function that works correctly but takes more than 5 seconds to test (with some exceptions). - Repetitive Code
In general, you should not have repetitive code.
One example of this is duplicate code, code that has been copy-pasted. This should instead be put into a helper function.
Another example is "numbered"-code, where there are multiple variables like foo0, foo1, foo2. These should be represented by a loop or list instead.
5-point error: having 10 or more instances of repeated code.
2-point error: having 3 or more instances of repeated code. - Magic Numbers
A magic number is a number used outside of a variable assignment in code that does not have an immediate and clear meaning.
In general, every number besides -1, 0, 0.5, 1, 2, and 10 is magic.
If you must use a magic number, store the number or an expression using the number in a variable, then use that variable.
2-point error: using any magic numbers.
Example: Here's code to draw a grid of clocks, similar to what you saw in the graphics notes. For numbers like 80 (the width and height of the clocks) note that we're storing them in variables so that we can change the behavior of our code easily.
# Draw a whole grid of clocks! def drawClockGrid(canvas, width, height): clockWidth = 80 clockHeight = 80 numRows=3 numCols=4 margin = 5 hour = 0 minute = 0 incrementHour = 13 for row in range(numRows): for col in range(numCols): left = col * clockWidth + margin top = row * clockHeight + margin right = left + clockWidth - margin bottom = top + clockHeight - margin hour += incrementHour drawClock(canvas, left, top, right, bottom, hour, minute)
Now, this is the same code, but written using magic numbers. This code is hard to understand, changing this code's behavior is difficult, and it's very easy to break. Don't do this!# Magic numbers! Don't do this! def drawClockGrid(canvas, width, height): hour = 0 for row in range(3): for col in range(4): left = col * 80 + 5 top = row * 80 + 5 right = col * 80 + 80 bottom = row * 80 + 80 hour += 13 drawClock(canvas, left, top, right, bottom, hour, 0)
This is even worse!# DEFINITELY don't do this! def drawClockGrid(canvas, width, height): hour = 0 for row in range(3): for col in range(4): hour += 13 drawClock(canvas, col*80+5, row*80+5, col*80+80, row*80+80, hour, 0) - If/Else Statements
When reasonable, multiple if statements should be joined into an if-elif-else chain. For example, the following code:if (x < 2): foo() if (x > 10): bar() if (2 <= x <= 10): oh()should beif (x < 2): foo() elif (x > 10): bar() else: oh()2-point error: not joining-up multiple ifs where appropriate. - Global Variables
Global variables can be useful while programming, but you will not need them in 15-112.
In fact, if you use global variables in this class, you're probably using them incorrectly.
2-point error: using any global variables.
- Test Functions
Check 3.9