Sierpinski turtle triangle

I am working through Programming Fundamentals with Python on Udacity. The assignment was to create a recursive triangle. I am happy with the output, but I am hoping for feedback on the actual construction of the program. Could I have done this in a more simple and concise way?

import turtle

def draw_polygon(a_turtle, length, sides):
counter = 0
while (counter < sides):
a_turtle.forward(length)
a_turtle.right(360 / sides)
counter = counter + 1

def draw_triangle(a_turtle, length):
draw_polygon(a_turtle, length, 3)

def draw_fractal_triangle(a_turtle, length, depth):
if (depth == 1):
draw_triangle(a_turtle, length)
else:
for i in range(1,4):
draw_fractal_triangle(a_turtle, length/2, depth-1)
a_turtle.forward(length)
a_turtle.right(120)

def draw():
window = turtle.Screen()
window.bgcolor("black")

brent = turtle.Turtle()
brent.shape("turtle")
brent.color("yellow")

length = 200
brent.backward(length/2)
draw_fractal_triangle(brent, length, 5)
window.exitonclick()

draw()

$$$$

Nice. I only have three comments.

1. In draw_polygon() use a for loop rather than an explicit counter:

def draw_polygon(a_turtle, length, sides):
for counter in range(sides):
a_turtle.forward(length)
a_turtle.right(360 / sides)

For these kinds of tasks I usually add a top level docstring with a description of the task. Include a url to the problem if applicable. For example at the top of the file:

'''Recursive implementation of Sierpinski Triangle

Assignment from Programming Fundamentals with Python on Udacity.
'''

For any function/method etc. that has an algorithm that isn't immediately obvious from the code, add a comment or docstring explaining what/how it's doing it. For example, I would add a docstring to draw_recursive_triangle() to explain what the function is doing, any assumptions (does it matter which way the turtle is pointing?, are there min or max limits on length? are the triangles always equilateral?, etc.).

3. The functions might be useful in another program/assignment. Rather than rewriting them, you could import this file as a library if you use a if __name__ == "__main__": guard like so:

if __name__ == "__main__":
draw()

That way it runs the program if you execute the file, but not if you import it as a library

I'm a python beginner,so at the moment I can only see that you can slightly improve your code using range in the function draw_polygon, so instead of

counter = 0
while (counter < sides):
a_turtle.forward(length)
a_turtle.right(360 / sides)
counter = counter + 1

You can rewrite it in this way avoiding multiple divisions inside the loop and manual incrementing of variable counter:

angle = 360 / sides
for counter in range(sides):
a_turtle.forward(length)
a_turtle.right(angle)

Good luck in your learning, very nice start, i was much poorer in my begining! Viewing your code i suggest a structural change besides what @RootTwo and @dariosicily told.

The Spirit of Functions

Functions should be called with relevent paramenters. Currently when calling draw_triangle we need to pass the a_turtle parameter

draw_triangle(a_turtle, length)

but it would be nicer if we could call it by

draw_triangle(length)

directly.

Nicer Functions With Global Variables

By defining and using global variables, the above is achievable.

brent = turtle.Turtle()
window = turtle.Screen()

Then modifying functions using the global keyword. The global keyword allows you to use global variables within functions

def draw_polygon(length, sides):
global brent
for i in range(sides):
brent.forward(length)
brent.right(360 / sides)

The draw function

def draw():
global brent, window
window.bgcolor("black")
brent.shape("turtle")
...

Then no need to each time add the a_turtle parameter

The Class Approach

But, global variables might be a sign you need an OOP approach

This is an OOP approched by changing the above

class Sierpinski:
def __init__(self):
self.brent = turtle.Turtle()
self.window = turtle.Screen()

def draw_polygon(self, length, sides):
brent = self.brent
for i in range(sides):
brent.forward(length)
brent.right(360 / sides)

def draw_triangle(self, length):
self.draw_polygon(length, 3)

def draw_fractal_triangle(self, length, depth):
brent = self.brent
if (depth == 1):
self.draw_triangle(length)
else:
for i in range(1, 4):
self.draw_fractal_triangle(length/2, depth-1)
brent.forward(length)
brent.right(120)

def draw(self):
brent = self.brent
window = self.window

window.bgcolor("black")
brent.shape("turtle")
brent.color("yellow")
length = 200
brent.backward(length/2)
self.draw_fractal_triangle(length, 5)
window.exitonclick()

then to draw,

s = Sierpinski()
s.draw()

you could also implement

def draw_polygon(self, length, sides):
brent = self.brent
for i in range(sides):
brent.forward(length)
brent.right(360 / sides)

as

def draw_polygon(self, length, sides):
for i in range(sides):
self.brent.forward(length)
self.brent.right(360 / sides)

More control

Specifying the length and depth in the constructor might allow you to have more control, by changing values at one place, you modify it all

class Sierpinski:
def __init__(self):
self.brent = turtle.Turtle()
self.window = turtle.Screen()
self.length = 200
self.depth = 5

modifying in draw

def draw(self):
brent = self.brent
window = self.window
length = self.length
depth = self.depth

window.bgcolor("black")
brent.shape("turtle")
brent.color("yellow")

brent.backward(length/2)
self.draw_fractal_triangle(length, depth)
window.exitonclick()

Parameters

You can let users pass their own value by passing the values as parameters

class Sierpinski:
def __init__(self, length, depth):
self.brent = turtle.Turtle()
self.window = turtle.Screen()
self.length = length
self.depth = depth

usage:

s = Sierpinski(200, 5)
s.draw()

One last improvement could be adding parameters to draw instead of the class itself.

leave a line after the last  symbol to prevent this 