3
\$\begingroup\$

I made a program to draw the Olympic flag in Python using the turtle module. I am able to make a basic version of it but i think that there is still room for improvement!

Quick note: I am new to python and my code may not be of the best quality...

Ideally the outcome should look like this...

enter image description here

As you can see there is this 'overlapping' of the circles in the picture which makes the the circles look rather like rings that are on one another. I cannot implement that in my code. Like i said this is a basic version of it.

My code output:

enter image description here

Compare both pictures and you can see the difference.

Here is my code:

import turtle

def c1():
    t = turtle.Pen()
    t.reset()
    t.up()
    t.goto(-165,0)
    t.down()
    t.width(10)
    t.color("blue")
    t.circle(75)

def c2():
    t = turtle.Pen()
    t.reset()
    t.up()
    t.goto(0,0)
    t.down()
    t.width(10)
    t.color("black")
    t.circle(75)

def c3():
    t = turtle.Pen()
    t.reset()
    t.up()
    t.goto(165,0)
    t.down()
    t.width(10)
    t.color("red")
    t.circle(75)

def c4():
    t = turtle.Pen()
    t.reset()
    t.up()
    t.goto(-82.5,-85)
    t.down()
    t.width(10)
    t.color("yellow")
    t.circle(75)

def c5():
    t = turtle.Pen()
    t.reset()
    t.up()
    t.goto(82.5,-85)
    t.down()
    t.width(10)
    t.color("green")
    t.circle(75)

c1()
c2()
c3()
c4()
c5()

It would be awesome if anyone could provide some advice to improve the program in any way.

\$\endgroup\$
  • 3
    \$\begingroup\$ We can review your code and suggest improvements, but requesting a change in functionality is off-topic. \$\endgroup\$ – 200_success May 4 '17 at 13:31
3
\$\begingroup\$

Your function kind-of works as intended, except the obvious deviation occurring at the crossings of rings. However, you can only draw the logo at one specific size and you need a heck of a lot of code to do it.

Let's start with a little clean-up and I'll explain the how and the why afterwards:

import turtle


OFFSET = 165
HALF_OFFSET = OFFSET/2
BORDER = 10
UPPER_LINE = 0
LOWER_LINE = UPPER_LINE - (HALF_OFFSET + BORDER/4)


def draw_circle(turtle, x=0, y=0, width=10, color="black", size=75):
    """
    Take a turtle, draw a circle.
    Takes 6 arguments, including the turtle.

    turtle: The turtle to use. Save the world, re-use your turtle.
    x: X coordinate offset from 0
    y: Y coordinate offset from 0
    width: width of the border of the ring
    color: color of the ring
    size: size of the ring
    """
    turtle.up()
    turtle.goto(x, y)
    turtle.down()
    turtle.width(width)
    turtle.color(color)
    turtle.circle(size)


def main():
    t = turtle.Pen()
    draw_circle(t, -OFFSET, UPPER_LINE, BORDER, "blue")
    draw_circle(t)
    draw_circle(t, OFFSET, UPPER_LINE, BORDER, "red")
    draw_circle(t, -HALF_OFFSET, LOWER_LINE, BORDER, "yellow")
    draw_circle(t, HALF_OFFSET, LOWER_LINE, BORDER, "green")

    input("Press any key...")

if __name__ == "__main__":
    main()

Which, apart from the input part, does exactly the same thing as your code does. That part halts the program so you can actually enjoy the creation on your screen before the rings disappear again. I've also removed the reset(), since it wasn't necessary.

I apologize for the poor naming, but at least it adheres to PEP8. Consistency is good, re-inventing the wheel unnecessary so just follow the same convention as everyone else. Keeps code readable for most developers at the same time. Good, no?

First, I've reduced the amount of functions. A lot. How? By using arguments. I can even use default arguments to save a lot of duplication. You can see this most of all in the second call to draw_circle, where a black circle is drawn with standard dimensions.

Note that many arguments are read from a value set at the top, which is calculated frome one single value: OFFSET. By changing one value, all other values scale with it.

This improves re-usability and readability by a lot. Your future self will thank you.

Another way to improve re-usability is with the following standard technique:

if __name__ == "__main__":
    main()

If you dive deeper into Python, you'll start encountering special variables like __name__. In this case, __name__ allows us to see whether a piece of code was included from somewhere else or executed directly. Now, everything inside main will only be executed whenever you call the function directly and not when it's included in a different file.

Now, assuming I have 2 scripts, CR_162598.py and foo.py. CR_162598.py is the cleaned-up version of your code. foo.py could look like this:

import turtle
from CR_162498 import draw_circle


t = turtle.Pen()
draw_circle(t, 50, 50)

input("Press any key...")

And all of a sudden, a black ring appears at coordinates 50, 50. Now, you can re-use your code without actually touching your old code. All you need to know is you can use the function draw_circle from the file CR_162498 and what arguments it takes. Noticed the docstring on top of the function? It's there for (among others) exactly that reason. Telling your user (or future self) how to use the function.

On default arguments:
Default arguments (default parameter values) help with setting a default ring. If we 'just want a ring' and don't care about it's size and color, simply calling the function will do. They are the values used if no other values are provided. Basically, giving a default gives you the option of making that parameter optional instead of obligatory. If it's missing, the function will simply take it's default instead.

Note: size is probably your radius, diameter or a value derived from either of those. Renaming it to something more sensible would be beneficial.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.