Я новичок в Python и пытаюсь использовать модули Turtle. В настоящее время я пытаюсь кодировать 3 ползунка (R, G, B) для настройки цветов экрана черепахи. Тем не менее, я обнаружил, что части моего кода заполнены избыточностями, не мог бы кто-нибудь сказать мне, как обрезать код, чтобы сделать его более компактным и понятным, особенно в частях draw_red, draw_green, draw_blue? Заранее спасибо.
import turtle
turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
colors = ['red', 'green', 'blue']
painters = []
red_x, green_x, blue_x = 1, 2, 3
turtle.bgcolor(0,0,0)
def spawningPainters():
global painter
for i in range(len(colors)):
painter = turtle.Turtle()
painters.append(painter)
painter.up()
painter.speed(0)
painter.setheading(90)
painter.color(colors[i])
painter.pencolor('black')
painter.shape('turtle')
painter.shapesize(5, 5, 5)
xPos = i + 1
painter.goto(xPos, 0)
def assign_painters():
global redPainter, greenPainter, bluePainter
redPainter = painters[0]
greenPainter = painters[1]
bluePainter = painters[2]
def draw_red(x, y):
redPainter.ondrag(None)
x = red_x
redPainter.goto(x, y)
redPainter.ondrag(draw_red)
update_screen_color()
def draw_green(x, y):
greenPainter.ondrag(None)
x = green_x
greenPainter.goto(x, y)
greenPainter.ondrag(draw_green)
update_screen_color()
def draw_blue(x, y):
bluePainter.ondrag(None)
x = blue_x
bluePainter.goto(x, y)
bluePainter.ondrag(draw_blue)
update_screen_color()
def update_screen_color():
red = max(min(redPainter.ycor(), 255) , 0)
green = max(min(greenPainter.ycor(), 255), 0)
blue = max(min(bluePainter.ycor(), 255), 0)
turtle.bgcolor(int(red), int(green), int(blue))
def back_to_origin():
for painterIndex in range(len(painters)):
painters[painterIndex].goto(painterIndex + 1, 0)
def listening_input():
redPainter.ondrag(draw_red)
greenPainter.ondrag(draw_green)
bluePainter.ondrag(draw_blue)
turtle.onkeypress(back_to_origin, 'c')
def main():
spawningPainters()
assign_painters()
listening_input()
main()
turtle.listen()
turtle.tracer(True)
turtle.mainloop()
2 ответа
Рассмотрение
Мне не удалось придумать способ сделать код более компактным, и, на мой взгляд, код довольно понятен. Однако у меня есть и другие отзывы.
Используйте правильный основной
Вместо:
turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
turtle.bgcolor(0,0,0)
...
def main():
spawningPainters()
assign_painters()
listening_input()
main()
turtle.listen()
turtle.tracer(True)
turtle.mainloop()
Делать:
...
if __name__ == "__main__":
turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
turtle.bgcolor(0,0,0)
spawningPainters()
assign_painters()
listening_input()
turtle.listen()
turtle.tracer(True)
turtle.mainloop()
Для получения дополнительной информации вы можете посмотреть этот пост: https://stackoverflow.com/questions/419163/what-does-if-name-main-do
Не позволяйте черепахе выходить за окно
С текущим кодом можно перетащить черепаху за пределы окна. Если вы затем прекратите перетаскивание, вы не сможете вернуть его, пока не сбросите положение. Чтобы этого не произошло, мы можем ограничить границы y
внутри функций рисования.
def draw_blue(x, y):
bluePainter.ondrag(None)
x = blue_x
y = min(y, 255)
y = max(y, 0)
bluePainter.goto(x, y)
bluePainter.ondrag(draw_blue)
update_screen_color()
Делая это, min
а также max
звонки могут быть удалены из update_screen_color
функция. Однако вы можете оставить их как форму защитного программирования на случай, если вы добавите дополнительные функции.
def update_screen_color():
red = redPainter.ycor()
green = greenPainter.ycor()
blue = bluePainter.ycor()
turtle.bgcolor(int(red), int(green), int(blue))
Обновить цвет экрана при возврате к исходной точке
Когда положение возвращается в исходное положение, цвет экрана не перерисовывается. Если это не предусмотрено, было бы хорошо позвонить в update_screen_color
функция в конце back_to_origin
функция.
Несколько советов
В global painter
переменная не используется нигде за пределами spawningPainters
функция, поэтому ее можно удалить.
Переместите colors
список в spawningPainters
функция, поскольку она больше нигде не используется.
Вы можете захотеть переименовать spawningPainters
к spawn_painters
.
Дополнительный
Добавьте набор текста в painters
список
Когда объявлен пустой список, у intellisense вашей IDE могут возникнуть проблемы с определением типа элементов, которые будет содержать список. Добавляя аннотации типов, вы можете помочь intellisense лучше понять ваш код. Вместо:
painters = []
Делать:
from typing import List
...
painters: List[turtle.Turtle] = []
РЕДАКТИРОВАТЬ: ответ оказался довольно длинным, и я не хочу, чтобы вы забыли, что вы действительно хорошо поработали с этой программой и, особенно, признав, что, вероятно, есть лучшая альтернатива, чем утроение кода, по одной части на цвет.
Это мой вариант менее повторяющейся версии одного и того же кода. Обратите внимание, что мои предложения не обязательно самый лучший, и вы, и я, и другие люди, которые в конечном итоге вмешаются, могли бы разработать еще лучшую программу 🙂
Забота о государстве
У вас была правильная идея: вам нужен контейнер со всеми черепахами, поэтому вам не нужно иметь переменную для каждой из них, но выполнить это сложно, потому что все взаимосвязано, и вам нужно, чтобы многие из ваших функций знали сразу много вещей.
На первой итерации я пытался перепроектировать ваш код, сохраняя три рисовальщика в словаре, который затем передавался бы нескольким функциям, но в итоге я использовал ООП (объектно-ориентированное программирование), потому что это тип ситуации, когда это пригодится.
Мы создаем объекты, которые связаны и связаны, и позволяем им хранить свои собственные отношения и т. Д.
Обратите внимание, что это не Только способ сделать это.
Ваша собственная черепаха
Во-первых, нам нужны наши черепахи, которые знают о себе, чтобы их .ondrag
могут знать о своем текущем положении и т. д.
import turtle
class Painter(turtle.Turtle):
def __init__(self, turtle_app, i, colour):
super().__init__()
self.turtle_app = turtle_app
self.up()
self.speed(0)
self.setheading(90)
self.color(colour)
self.pencolor('black')
self.shape('turtle')
self.shapesize(5, 5, 5)
self.goto(i, 0)
self.ondrag(self.draw)
def draw(self, x, y):
x = self.xcor()
self.goto(x, y)
self.turtle_app.update_screen_colour()
Обратите внимание, что __init__
метод — это то, что инициализирует черепаху и делает почти все, что у вас было в вашем spawningPainters
метод. Разница в том, что Painter
сам определяет свой draw
метод, и что он хранит ссылку на этот turtle_app
который является «главным контроллером» вашего приложения:
class TurtleApp:
def __init__(self):
self.spawn_painters()
def spawn_painters(self):
self.painters = {}
for i, colour in enumerate(["red", "green", "blue"], start=1):
self.painters[colour[0]] = Painter(self, i, colour)
def update_screen_colour(self):
r = max(min(self.painters["r"].ycor(), 255) , 0)
g = max(min(self.painters["g"].ycor(), 255), 0)
b = max(min(self.painters["b"].ycor(), 255), 0)
turtle.bgcolor(int(r), int(g), int(b))
def reset(self):
for painter in self.painters.values():
painter.goto(painter.xcor(), 0)
self.update_screen_colour()
В TurtleApp
объект хранит художников в словаре, индексируемом по первой букве цвета, и предоставляет вспомогательные методы, которые вам понадобились для сброса позиций, обновления цвета экрана и т. д.
Используя «правильный основной», например, предложенный другой ответ (и это хорошее предложение!), Ваш код заканчивается на
if __name__ == "__main__":
turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
turtle.bgcolor(0,0,0)
ta = TurtleApp()
turtle.onkeypress(ta.reset, 'c')
turtle.listen()
turtle.tracer(True)
turtle.mainloop()
Обратите внимание на две строки, которые я разделил, где вы создаете свой TurtleApp
а потом скажи turtle
позвонить в TurtleApp.reset
если пользователь нажимает "c"
ключ. Это было проверено и, похоже, работает так же, как и ваше оригинальное.
Удалите нерелевантные переменные
Постарайтесь сделать свой код максимально самодостаточным. У вас есть специальные переменные для хранения x
положение каждого рисовальщика, и это прекрасно, но обратите внимание, что как только вы инициализируете его, вы всегда можете запросить его x
положение с painter.xcor()
, что вы знаете, потому что вы сделали то же самое для y
с участием painter.ycor()
при обновлении цвета фона.
Теперь, если вы знаете x
положение вашего маляра, вы всегда можете предотвратить его перемещение влево или вправо, сделав что-то вроде painter.goto(painter.xcor(), y)
в draw
функции, вам не нужно оставлять x_red
, x_blue
, а также x_green
переменные с вами 🙂
Перечислить
enumerate
(который я использовал в for
цикл для инициализации художников) очень полезен, когда вам нужно перебрать набор значений (в моем случае, цвета), а также узнать их индекс (который вы используете для установки x
положение в начале). Если ты не знаешь enumerate
, вы можете прочитать об этом в документации или в этот учебник. Обратите внимание, что я также использовал часто упускаемый из виду start
необязательный аргумент, чтобы сообщить enumerate
начать отсчет с 1
вместо 0
.
Я сделал еще одну вещь:
Соглашения об именах PEP 8
Python обычно предпочитает snake_case
имена, поэтому все мои функции имеют регистр, отличный от ваших исходных.
Окончательное предложение по кодексу
Теперь все вместе:
import turtle
class Painter(turtle.Turtle):
def __init__(self, turtle_app, i, colour):
super().__init__()
self.turtle_app = turtle_app
self.up()
self.speed(0)
self.setheading(90)
self.color(colour)
self.pencolor('black')
self.shape('turtle')
self.shapesize(5, 5, 5)
self.goto(i, 0)
self.ondrag(self.draw)
def draw(self, x, y):
x = self.xcor()
self.goto(x, y)
self.turtle_app.update_screen_colour()
class TurtleApp:
def __init__(self):
self.spawn_painters()
def spawn_painters(self):
self.painters = {}
for i, colour in enumerate(["red", "green", "blue"], start=1):
self.painters[colour[0]] = Painter(self, i, colour)
def update_screen_colour(self):
r = max(min(self.painters["r"].ycor(), 255) , 0)
g = max(min(self.painters["g"].ycor(), 255), 0)
b = max(min(self.painters["b"].ycor(), 255), 0)
turtle.bgcolor(int(r), int(g), int(b))
def reset(self):
for painter in self.painters.values():
painter.goto(painter.xcor(), 0)
self.update_screen_colour()
if __name__ == "__main__":
turtle.setworldcoordinates(0, 0, 4, 255)
turtle.colormode(255)
turtle.tracer(False)
turtle.bgcolor(0,0,0)
ta = TurtleApp()
turtle.onkeypress(ta.reset, 'c')
turtle.listen()
turtle.tracer(True)
turtle.mainloop()
```