2025, Nov 13 15:02

Почему наследование от turtle.Screen даёт TypeError и как исправить через _Screen

Разбираем, почему в Python наследование от turtle.Screen даёт TypeError: Screen — функция. Исправление через _Screen и альтернатива с композицией Screen.

Наследование от turtle.Screen приводит к ошибке “TypeError: function() argument 'code' must be code, not str” — и дело не в логике вашей игры. Причина проще: в turtle Screen — это не класс для наследования, а функция, возвращающая экземпляр внутреннего класса-реализации. Если пытаться наследоваться от функции, Python закономерно возражает.

Постановка задачи

Рассмотрим минимальный каркас в стиле Pong, разделённый на два файла. Проблема возникает именно из‑за наследования от Screen.

Точка запуска:

from arena import Stage
from paddle import Paddle
from orb import Orb
from tally import Tally
ARENA_W = 600
ARENA_H = 500
PAD_SIZE = 50

field = Stage(ARENA_W, ARENA_H, pad_user, pad_cpu, tally_user, tally_cpu, orb)
pad_user = Paddle(ARENA_W, ARENA_H, PAD_SIZE)
pad_cpu = Paddle(ARENA_W, ARENA_H, PAD_SIZE)
tally_user = Tally(ARENA_W, ARENA_H)
tally_cpu = Tally(ARENA_W, ARENA_H)
orb = Orb()
running = True

while running:
    field.bat_orb_touch()

И вот обёртка экрана:

from turtle import Screen, Turtle

class Stage(Screen):
    def __init__(self, w, h, pad_u, pad_c, sc_u, sc_c, orb):
        super().__init__()
        self.w = w
        self.h = h
        self.pad_u = pad_u
        self.pad_c = pad_c
        self.sc_u = sc_u
        self.sc_c = sc_c
        self.orb = orb
        self.bgcolor("black")
        self.screensize(w, h)
        draw_center_line()

    def draw_center_line(self):
        center = Turtle()
        center.pensize(20)
        center.pencolor("white")
        center.penup()
        center.goto(0, self.h-10)
        center.setheading(270)
        center.pendown()

        for step in range(self.h/(2*10)):
            center.forward(10)
            if cen_line.pencolor() == 'white':
                cen_line.pencolor('black')
            else:
                cen_line.pencolor('white')

def bat_orb_touch(self, bat):
    return all([abs(orb.xcor()-bat.xcor()) < 10, abs(orb.ycor()-bat.ycor()) < 15])

Почему это падает

Ошибка возникает на строке определения класса, где Stage пытается унаследоваться от Screen. В turtle Screen — не класс, а функция. Эта функция конструирует и возвращает экземпляр внутреннего класса. Попытка записать class Stage(Screen): в такой ситуации заканчивается именно той ошибкой, которую вы видите, потому что Python ожидает в списке базовых типов класс (или совместимый тип), а не функцию.

Внутренний класс, который действительно реализует окно, называется _Screen. Если вам нужны именно механики наследования, наследоваться следует от него.

Исправление

Используйте внутренний класс, лежащий в основе Screen. Наследование должно быть от _Screen, а не от Screen.

from turtle import _Screen, Turtle

class Stage(_Screen):
    def __init__(self, w, h, pad_u, pad_c, sc_u, sc_c, orb):
        super().__init__()
        self.w = w
        self.h = h
        self.pad_u = pad_u
        self.pad_c = pad_c
        self.sc_u = sc_u
        self.sc_c = sc_c
        self.orb = orb
        self.bgcolor("black")
        self.screensize(w, h)
        draw_center_line()

    def draw_center_line(self):
        center = Turtle()
        center.pensize(20)
        center.pencolor("white")
        center.penup()
        center.goto(0, self.h-10)
        center.setheading(270)
        center.pendown()

        for step in range(self.h/(2*10)):
            center.forward(10)
            if cen_line.pencolor() == 'white':
                cen_line.pencolor('black')
            else:
                cen_line.pencolor('white')

def bat_orb_touch(self, bat):
    return all([abs(orb.xcor()-bat.xcor()) < 10, abs(orb.ycor()-bat.ycor()) < 15])

Это изменение устраняет конкретную ошибку типов, вызванную попыткой наследоваться от функции. Остальная логика остаётся прежней.

Почему эта деталь важна

turtle предоставляет Screen как фабричный входной пункт, тогда как реальный класс-реализация — _Screen. Понимание этого избавляет от запутанных ошибок на этапе наследования. Есть и более общий архитектурный вывод, часто уместный в подобных случаях: предпочитайте композицию наследованию в конструкциях turtle — контейнер игры имеет Screen, а не является Screen.

Выводы

Если базовый «класс» вызывает TypeError прямо в объявлении класса, проверьте, от чего вы наследуетесь. Быстрая проверка типа символа перед использованием — хороший sanity‑тест. Если решите остаться с наследованием для оболочки окна, наследуйтесь от _Screen. Если хотите держаться публичной поверхности, строите систему вокруг экземпляра Screen и компонуйте объекты вокруг него. И наконец, изолируя подобные ошибки, сводите код к минимальному воспроизводимому примеру — так проще быстро найти точку сбоя и не гоняться за несвязанными частями.