I'm kind of new to Python, and I'm trying to improve (especially) my object oriented programming, and getting to know Python syntax better.
I'd love to know if there is something to improve, (especially) in the matter of object oriented, or making the code more Pythonic (Syntactic sugar).
import turtle
RIGHT = 'r'
LEFT = 'l'
class Dragon_Curve(turtle.Turtle):
def __init__(self, iterations, length):
super(Dragon_Curve, self).__init__()
self.iterations = iterations
self.length = length
self.hideturtle()
self.speed(50)
self.sequence = self.create_seq("", iterations)
self.draw_sequence(self.sequence)
def draw_sequence(self, dragon_sequence):
for i in dragon_sequence:
if i == RIGHT:
self.right(90)
self.forward(length)
elif i == LEFT:
self.left(90)
self.forward(length)
# Create the dragon's path, depending on the number of iterations.
def create_seq(self, given_seq, num_iterations):
self.seq = ""
if num_iterations == 0:
print("the dragon's path is {}".format(given_seq))
return given_seq
else:
self.seq += given_seq
self.seq += 'r'
self.seq += self.opposite(given_seq)[::-1]
return self.create_seq(self.seq, num_iterations - 1)
# flip the given sequence.
# If the argument is 'rrl', it will return 'llr'
def opposite(self, str):
seq = ""
for i in str:
if i == 'r':
seq += 'l'
elif i == 'l':
seq += 'r'
return seq
if __name__ == '__main__':
iterations = int(input("Enter number of iterations - "))
length = int(input("Enter length size (recommended 5) - "))
dragon = Dragon_Curve(iterations, length)
turtle.Screen().title("The Dragon Curve")
turtle.done()
2 Answers 2
Here are some suggestions and remarks, in no particular order:
i
is not a great name for a variable in most cases, and especially not when it's not short for "index", as is the case in yourdraw_sequence
function. I would suggest naming itdirection
.- The official Python PEP8 style guide states that class names should be CamelCased, so your class should be named
DragonCurve
, without the underscore. - There's no point in making
self.seq
an instance/member variable, since it's only used by thecreate_seq
function and its value is reset on each call. It can safely be turned into a local variable. - Stick to using the
LEFT
/RIGHT
constants instead of'l'
/'r'
. - One small improvement that you could make is replacing your empty strings
""
with a constant namedEMPTY_SEQUENCE
. This would completely hide the fact that sequences are represented as strings from the reader, and would make it easy for you to change the representation if you would ever want to. - Function names should be verbs, so
opposite
is not a good name for a function. Perhaps something like "flip" or "invert" would be more suitable. - The parameter named
str
will shadow the built-in functionstr
. This is generally not a good idea, so maybe it should be namedseq
orsequence
, since that is actually the abstraction that the function operates on.
The code is good, I have some minor improvements to remove some repetition and excessive verbosity:
Same code in both branches
You can simplify draw_sequence
to:
def draw_sequence(self, dragon_sequence):
for i in dragon_sequence:
if i == RIGHT:
self.right(90)
elif i == LEFT:
self.left(90)
self.forward(length)
Avoiding to repeat self.forward(length)
twice.
You could also avoid repeating 90
but it may become hard to read so this is at reader's discretion:
def draw_sequence(self, dragon_sequence):
for i in dragon_sequence:
turn = self.right if i == RIGHT else self.left(90)
turn(90)
self.forward(length)
opposite
: ''.join
and generator comprehension
The oppositefunction (that should be named
flip` as
rreillo remarks) is very long even if it has a very simple purpose, so you should shorten it using a generator comprehension:
def opposite(self, sequence):
return ''.join(RIGHT if LEFT else LEFT for char in sequence)
Now the logic of the function is much clearer without the repetition of the seq
temporary variable.
The method should become a @staticmethod
because self
is not used.
Standard Test/Documentation Practice
You provided documentation and an example for your function over its name, but the standard is to write it below in triple quotes like this:
def flip(self, sequence):
"""
Flips each item in the sequence.
>>> Dragon_Curve.flip("rrl")
"llr"
>>> Dragon_Curve.flip("llr")
"rrl"
"""
return ''.join(RIGHT if LEFT else LEFT for char in sequence)
(I added a second test to show that applying this function twice gets back to the start)
This way you can get help when you call >>> help(Dragon_Curve.flip)
and you can test automatically with doctest.testmod()
Terminal friendly
In Unix programs are usually written to be terminal friendly, so you could consider using the argparse
library to take inputs from the command line instead of from the prompt, also you could avoid printing the path as on successful execution usually nothing is printed.
Explore related questions
See similar questions with these tags.