# GESP等级：二级 | GESP Python 二级考点
"""
第11课·第1节 —— def动机：定义函数与调用
============================================
课程分段：
  00:00-10:00  导入：画正方形要4行→每次画都要4行→"def=写一次,用无数次"
  10:00-30:00  知识点：①def定义函数 ②()写参数 ③:后面缩进函数体 ④调用:函数名()
  30:00-40:00  演示：def draw_square 画正方形
  40:00-55:00  实操：补全def / 定义并调用draw_triangle / 定义draw_star
  55:00-60:00  小测："def和调用的区别?"
  GESP考点：#30 函数定义与调用
  记忆钩子："def=写菜谱 调用=做菜 光写不调用=白写"

运行方式：
  每个函数独立运行，按教学节奏调用。
"""

import turtle
import time


# ============================================================
# 阶段一：导入——为什么要用def？（00:00-10:00）
# ============================================================
def demo_intro_why_def():
    """
    用"画正方形"引入def动机。
    老师演示：没有def → 每次画正方形要写4行代码，重复多次。
    引出def的作用：把代码打包成一个名字，写一次用无数次。
    """
    print("=" * 60)
    print("导入：为什么要用def？")
    print("=" * 60)
    print()

    print("--- 第1步：画一个正方形 ---")
    print("画一个边长100的正方形需要4行代码：")
    print("    forward(100)")
    print("    right(90)")
    print("    forward(100)")
    print("    right(90)")
    print("    forward(100)")
    print("    right(90)")
    print("    forward(100)")
    print("    right(90)")

    t = turtle.Turtle()
    t.speed(2)

    for i in range(4):
        t.forward(100)
        t.right(90)
    print("正方形画完了\n")

    time.sleep(1)
    t.clear()

    print("--- 第2步：画3个正方形，每个4行，一共12行！---")
    print("问题：如果要画3个正方形，就要复制粘贴3次……")
    print("如果要画10个呢？100个呢？每次都写4行？\n")

    print("--- 第3步：def登场！---")
    print()
    print("核心思想：把4行代码打包成一个名字——draw_square")
    print()
    print("    def draw_square():        ← 定义函数")
    print("        for i in range(4):    ← 函数体（缩进）")
    print("            forward(100)")
    print("            right(90)")
    print()
    print("    draw_square()             ← 调用函数，一行顶四行！")
    print("    draw_square()             ← 再调用一次，又是一行！")
    print()

    print("结论：")
    print("   def = 把代码打包起个名字 → 写一次，用无数次")
    print("   记忆钩子：def=写菜谱 调用=做菜 光写不调用=白写")

    t.penup()
    t.goto(-200, 0)
    t.pendown()
    # 演示用def的雏形（循环）画3个正方形
    for _ in range(3):
        for i in range(4):
            t.forward(80)
            t.right(90)
        t.penup()
        t.forward(120)
        t.pendown()

    print("\n用循环画了3个正方形（实际就是def的雏形）")

    turtle.done()


# ============================================================
# 阶段二：知识点讲解（10:00-30:00）
# ============================================================
def demo_def_syntax():
    """知识点①~④：def完整语法拆解"""
    print("=" * 60)
    print("知识点：def 定义与调用")
    print("=" * 60)
    print()

    print("--- ① def 定义函数 ---")
    print()
    print("    def 函数名():           ← def 关键字 + 函数名 + 小括号 + 冒号")
    print("        代码1               ← 缩进的函数体")
    print("        代码2")
    print("        代码3")
    print()
    print("   'def' 是 define（定义）的缩写")
    print("   函数名要见名知意，如 draw_square、draw_triangle")
    print()

    print("--- ② () 里面写参数 ---")
    print()
    print("   小括号 () 是函数的重要标志")
    print("   目前括号里是空的，后面学参数后可以传数据进去")
    print("   例子：")
    print("       def say_hello():          ← 括号里没东西——不需要传数据")
    print("           print('你好！')")
    print()
    print("       def draw_shape(size):     ← 括号里有参数——需要传长度")
    print("           for i in range(4):")
    print("               forward(size)")
    print("               right(90)")
    print()

    print("--- ③ : 后面缩进函数体 ---")
    print()
    print("   冒号 : 表示'接下来是函数的内容'")
    print("   缩进部分（统一4个空格）是函数体——函数要执行的代码")
    print("   例子：")
    print("       def draw_square():    ← 冒号表示开始")
    print("           for i in range(4):   ← 缩进4空格")
    print("               forward(100)     ← 再缩进4空格（循环体）")
    print("               right(90)")
    print()
    print("   缩进规则：")
    print("     ① def 下一行 → 缩进4空格（函数体）")
    print("     ② for 下一行 → 再缩进4空格（循环体）")
    print("     ③ 同一层级的缩进要对齐")
    print()

    print("--- ④ 调用：函数名() ---")
    print()
    print("   定义函数 = 写了菜谱（存着还没做）")
    print("   调用函数 = 按照菜谱做菜（真正执行）")
    print()
    print("   调用方式：")
    print("       函数名()         ← 直接写函数名加括号")
    print()
    print("   例子：")
    print("       def say_hello():      # 定义（写菜谱）")
    print("           print('你好！')")
    print()
    print("       say_hello()           # 调用（做菜）→ 输出：你好！")
    print("       say_hello()           # 再调用一次 → 又输出：你好！")
    print()

    print("--- 对比：定义 vs 调用 ---")
    print()
    print("     定义 def                |  调用 函数名()")
    print("     ------------------------|---------------------")
    print("     写菜谱                   |  做菜")
    print("     只写一次                 |  可以执行无数次")
    print("     不会执行代码             |  真正运行代码")
    print("     需要 def 关键字 + 冒号    |  直接写函数名 + 括号")
    print("     先定义才能调用           |  调用之前必须先定义")
    print()

    print("记忆钩子：")
    print("   def = 写菜谱（定义）")
    print("   调用 = 做菜（执行）")
    print("   光写不调用 = 白写！（只定义不调用，函数永远不会执行）")


def demo_def_flow():
    """演示定义和调用的执行流程"""
    print("=" * 60)
    print("演示：定义 vs 调用的执行流程")
    print("=" * 60)
    print()

    print("--- 执行流程演示 ---")
    print()

    print("代码：")
    print(" ① def say_hello():         ← 第1步：读到def，记住这个菜谱")
    print(" ②     print('你好！')      ← 第2步：记住函数体内容（不执行！）")
    print(" ③ print('准备调用')         ← 第3步：普通代码，执行")
    print(" ④ say_hello()              ← 第4步：调用函数，跳回函数体执行")
    print(" ⑤ print('调用结束')         ← 第5步：函数执行完，继续往下")
    print()

    print("--- 实际运行效果 ---")
    print()

    # 定义函数（只是记住，不执行）
    def say_hello():
        print("   [函数体] 你好！(这是函数体里的代码)")

    # 主流程
    print('   [1] 程序开始')
    print('   [2] 读到def say_hello——记住函数体，但不执行')
    print('   [3] 准备调用：')
    print()
    print('   >>> 调用 say_hello()   ← 开始执行函数体')
    say_hello()
    print('   >>> 函数执行完毕  ← 回到主程序')
    print()
    print('   [4] 再调用一次 say_hello()')
    say_hello()
    print('   [5] 调用结束')
    print()

    print("关键理解：")
    print("   def 只是'登记'代码，不是'执行'代码")
    print("   只有调用（函数名()）才会真正执行函数体")


# ============================================================
# 阶段三：演示——def draw_square 画正方形（30:00-40:00）
# ============================================================
def demo_draw_square():
    """
    演示用def定义draw_square函数。
    对比：不用def vs 用def。
    """
    print("=" * 60)
    print("演示：def draw_square 画正方形")
    print("=" * 60)
    print()

    t = turtle.Turtle()
    t.speed(3)

    # ===== 方式1：没有def——写4行代码画正方形 =====
    print("--- 方式1：没有def——画1个正方形要4行 ---")
    print()

    t.penup()
    t.goto(-300, 100)
    t.pendown()

    for i in range(4):
        t.forward(80)
        t.right(90)
    print("   用4行循环画了1个正方形\n")

    # ===== 方式2：用def——定义一次，调用多次 =====
    print("--- 方式2：用def——定义一次，想画几次画几次 ---")
    print()

    def draw_square():
        """画一个边长80的正方形"""
        for i in range(4):
            t.forward(80)
            t.right(90)

    # 在3个不同位置画正方形
    positions = [(-300, -50), (-100, -50), (100, -50)]

    for pos in positions:
        t.penup()
        t.goto(pos[0], pos[1])
        t.pendown()
        draw_square()  # 一行调用，顶4行代码！
        print(f"   在{pos}位置调用draw_square()")

    print()
    print("对比总结：")
    print("   不用def → 每次画正方形都要写4行")
    print("   用def   → 定义1次，每调用1次 = 执行4行代码")
    print("   调用3次 → 执行了12行代码，但只写了1次函数体！")
    print()

    turtle.done()


# ============================================================
# 阶段四：实操练习参考答案（40:00-55:00）
# ============================================================
def demo_practice_answers():
    """实操练习参考答案"""
    print("=" * 60)
    print("实操练习参考答案")
    print("=" * 60)
    print()

    # === 练习1：补全def ===
    print("--- 练习1：补全def ---")
    print()
    print("题目：补全下面代码，定义一个画正方形的函数")
    print()
    print("    ____ draw_square():")
    print("        for i _____ range(4):")
    print("            forward(100)")
    print("            right(90)")
    print()
    print("答案：")
    print("    def draw_square():")
    print("        for i in range(4):")
    print("            forward(100)")
    print("            right(90)")
    print()

    # === 练习2：定义并调用draw_triangle ===
    print("--- 练习2：定义并调用draw_triangle ---")
    print()
    print("题目：定义函数draw_triangle画等边三角形，并调用它")
    print()
    print("答案：")
    print("    def draw_triangle():")
    print("        for i in range(3):")
    print("            forward(100)")
    print("            left(120)       # 等边三角形外角120度")
    print()
    print("    draw_triangle()   ← 调用")
    print()

    # 用turtle实际演示
    t = turtle.Turtle()
    t.speed(3)

    def draw_triangle():
        """画等边三角形"""
        for i in range(3):
            t.forward(100)
            t.left(120)

    t.penup()
    t.goto(-200, 150)
    t.pendown()
    print("正在运行draw_triangle()……")
    draw_triangle()
    print("三角形画好了！\n")

    # === 练习3：定义draw_star ===
    print("--- 练习3：定义draw_star ---")
    print()
    print("题目：定义函数draw_star画五角星，并调用它")

    def draw_star():
        """画一个五角星"""
        for i in range(5):
            t.forward(150)
            t.right(144)  # 五角星转角 = 180 - 180/5 = 144度

    t.penup()
    t.goto(50, 150)
    t.pendown()
    print("正在运行draw_star()……")
    draw_star()
    print("五角星画好了！\n")

    # === 错误示例 ===
    print("--- 常见错误 ---")
    print()
    print("错误1：忘记冒号")
    print("    def draw_square()    ← 缺少冒号!")
    print("    → SyntaxError: expected ':'")
    print()

    print("错误2：忘记缩进")
    print("    def draw_square():")
    print("    for i in range(4):   ← 没有缩进！")
    print("    → IndentationError: expected an indented block")
    print()

    print("错误3：函数名写错")
    print("    def draw_square():")
    print("        for i in range(4):")
    print("            forward(100)")
    print("            right(90)")
    print("    drawsquare()         ← 函数名下划线错误")
    print("    → NameError: name 'drawsquare' is not defined")
    print()

    print("错误4：只定义不调用")
    print("    def draw_square():")
    print("        print('画正方形')")
    print("    # 没有调用 → 什么都不会输出！")
    print("    记忆钩子：光写不调用=白写")
    print()

    turtle.done()


# ============================================================
# 阶段五：小测（55:00-60:00）
# ============================================================
def demo_quiz():
    """小测题目及正确答案"""
    print("=" * 60)
    print("小测：def 和 调用 有什么区别？")
    print("=" * 60)
    print()

    print("正确答案：")
    print()
    print("  def（定义）            |  调用（执行）")
    print("  -----------------------|----------------------------")
    print("  用 def 关键字开头       |  直接用函数名 + 小括号")
    print("  有冒号 :               |  没有冒号")
    print("  函数体需要缩进         |  不需要缩进")
    print("  定义时不会执行代码     |  调用时才会执行函数体")
    print("  只写一遍               |  可以执行任意多次")
    print()

    print("--- 记忆钩子 ---")
    print("  def = 写菜谱（定义函数，保存代码）")
    print("  调用 = 做菜（执行函数，运行代码）")
    print("  光写不调用 = 白写（只定义不调用，函数永远不会执行）")
    print()

    print("--- 补充判断题 ---")
    print()
    print("1. def draw_square():  ← 这是定义还是调用？")
    print("   → 定义（有def、冒号、下一行要缩进）")
    print()
    print("2. draw_square()      ← 这是定义还是调用？")
    print("   → 调用（没有def、没有冒号、直接写函数名+括号）")
    print()
    print("3. 以下代码会输出什么？")
    print("   def say_hi():")
    print("       print('你好')")
    print("   # 没有调用")
    print("   → 什么都不输出（只定义没调用）")
    print()
    print("4. 以下代码会输出什么？")
    print("   def say_hi():")
    print("       print('你好')")
    print("   say_hi()")
    print("   say_hi()")
    print("   → 输出两次'你好'（定义一次，调用两次）")


# ============================================================
# 入口
# ============================================================
if __name__ == "__main__":
    print("第11课第1节 —— def动机：定义函数与调用")
    print("=" * 60)
    print()
    print("请选择要运行的功能：")
    print("  1  - 导入：为什么要用def？（00:00-10:00）")
    print("  2  - 知识点：def完整语法（10:00-30:00）")
    print("  3  - 演示：定义vs调用的执行流程")
    print("  4  - 演示：def draw_square 画正方形（30:00-40:00）")
    print("  5  - 实操练习参考答案（40:00-55:00）")
    print("  6  - 小测答案（55:00-60:00）")
    print()

    try:
        choice = int(input("请输入数字 (1-6)，直接回车默认 4: ") or "4")
    except (ValueError, EOFError):
        choice = 4

    print()

    functions = {
        1: demo_intro_why_def,
        2: demo_def_syntax,
        3: demo_def_flow,
        4: demo_draw_square,
        5: demo_practice_answers,
        6: demo_quiz,
    }

    func = functions.get(choice)
    if func:
        func()
    else:
        print("无效选择，运行draw_square演示")
        demo_draw_square()
