Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the wp-pagenavi domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114

Notice: 函数 _load_textdomain_just_in_time 的调用方法不正确twentyseventeen 域的翻译加载触发过早。这通常表示插件或主题中的某些代码运行过早。翻译应在 init 操作或之后加载。 请查阅调试 WordPress来获取更多信息。 (这个消息是在 6.7.0 版本添加的。) in /var/www/blog.zhujinhui.net/wp-includes/functions.php on line 6114
开发日记 – 煅魂-JeffreyChu的修炼屋

turtle,一个让Python编程变得快乐的库

今天是六一儿童节,为了回归童心,所以今天学习下Python的turtle库,写几个小游戏玩玩。

简介

Python的turtle库源自Logo编程语言,用于绘制图形和制作简单动画。它通过控制“海龟”在屏幕上移动绘制图像而得名。

turtle绘图

turtle库广泛用于编程教学,可以帮助零基础的初学者理解编程概念和逻辑结构。它通过可视化反馈,增强学习体验,并用于绘制图形、创建简单游戏和模拟物理现象。

安装与设置

因为turtle库是Python标准库的一部分,所以大多数情况下只需要确保安装了Python,然后就可以直接使用turtle库了。

基础用法

要使用Turtle库绘图,首先需要创建一个Turtle图形窗口Screen

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
screen.title("Turtle图形窗口示例")  # 设置窗口标题
# 进入主循环,保持窗口打开
screen.mainloop()

创建Turtle对象后,可以开始绘图。以下是画一条线和一个圆的示例:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 画一条线
t.forward(100)  # 向前移动100像素
# 画一个圆
t.circle(50)  # 画一个半径为50像素的圆
# 进入主循环,保持窗口打开
screen.mainloop()

要创建一个Turtle对象并进行移动:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 向前移动150像素
t.forward(150)
# 向后移动50像素
t.backward(50)
# 进入主循环,保持窗口打开
screen.mainloop()

可以控制Turtle对象的方向和速度:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 向前移动100像素
t.forward(100)
# 向右转90度
t.right(90)
# 向前移动100像素
t.forward(100)
# 设置Turtle的移动速度(1到10,1最慢,10最快)
t.speed(5)
# 进入主循环,保持窗口打开
screen.mainloop()

可以改变Turtle对象的颜色和形状,使其更加有趣:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 改变Turtle的形状('turtle', 'arrow', 'circle', 'square', 'triangle', 'classic'等)
t.shape('turtle')
# 改变Turtle的颜色
t.color('blue')
# 改变Turtle的填充颜色
t.fillcolor('red')
# 向前移动100像素
t.forward(100)
# 画一个圆
t.circle(50)
# 进入主循环,保持窗口打开
screen.mainloop()

进阶用法

图形绘制

使用循环可以绘制出复杂且对称的图形,例如螺旋图案或对称的多边形。以下示例展示了如何使用循环绘制一个螺旋图形:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 设置Turtle的速度
t.speed(10)

# 使用循环绘制螺旋图形
for i in range(50):
    t.forward(i * 10)
    t.right(144)

# 进入主循环,保持窗口打开
screen.mainloop()

使用Turtle库可以很容易地绘制多边形和星形。以下是绘制五边形和五角星的示例:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 设置Turtle的速度
t.speed(5)

# 绘制五边形
for _ in range(5):
    t.forward(100)
    t.right(72)

# 移动Turtle到新位置绘制星形
t.penup()
t.goto(-200, 0)
t.pendown()

# 绘制五角星
for _ in range(5):
    t.forward(200)
    t.right(144)

# 进入主循环,保持窗口打开
screen.mainloop()

填充与颜色

使用begin_fill()end_fill()进行图形填充
turtle库允许使用begin_fill()end_fill()方法进行图形填充,比如填充一个矩形:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 设置填充颜色
t.fillcolor("yellow")
# 开始填充
t.begin_fill()

# 绘制矩形
for _ in range(4):
    t.forward(100)
    t.right(90)

# 结束填充
t.end_fill()
# 进入主循环,保持窗口打开
screen.mainloop()

虽然turtle库不直接支持渐变效果,但可以通过逐步改变颜色实现渐变效果:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
# 创建一个Turtle对象
t = turtle.Turtle()
# 设置初始颜色
r = 0
g = 0
b = 0
# 设置Turtle的速度
t.speed(10)

# 使用循环绘制渐变色螺旋图形
for i in range(50):
    t.pencolor(r/255, g/255, b/255)
    t.forward(i * 10)
    t.right(144)
    r = (r + 20) % 256
    g = (g + 25) % 256
    b = (b + 30) % 256


# 进入主循环,保持窗口打开
screen.mainloop()
五角星渐变色

文本和字体

turtle库提供了在绘图窗口中添加文本的方法。

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()

# 创建一个Turtle对象
t = turtle.Turtle()

# 在窗口中写入文本
t.penup()
t.goto(0, 0)
t.pendown()
t.write("Hello, Turtle!", align="center", font=("Arial", 24, "normal"))

# 进入主循环,保持窗口打开
screen.mainloop()

可以通过指定字体名称、大小和样式来自定义文本的外观:

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()

# 创建一个Turtle对象
t = turtle.Turtle()

# 在窗口中写入文本,设置字体样式和大小
t.penup()
t.goto(0, 0)
t.pendown()
t.write("Custom Font!", align="center", font=("Times New Roman", 30, "bold"))

# 进入主循环,保持窗口打开
screen.mainloop()

更复杂应用

以上的使用场景都是没有加上键盘绑定事件的(即键盘按下某些按键后会触发一些变化事件),所以加了键盘绑定后会更加复杂。

以开发一个小时候都玩过的《井字棋》为例, 源码地址:

https://gitee.com/jeffrey2021/children-day20240601/blob/master/jingziqi.py

以下是部分源码展示(不全)

import turtle

# 创建一个屏幕对象
screen = turtle.Screen()
screen.title("井字棋游戏")
screen.setup(width=600, height=600)
screen.setworldcoordinates(-300, -300, 300, 300)
# 初始化游戏状态
board = [["" for _ in range(3)] for _ in range(3)]
current_player = "X"  # "X" starts the game
positions = {
    "1": (-200, 200), "2": (0, 200), "3": (200, 200),
    "4": (-200, 0), "5": (0, 0), "6": (200, 0),
    "7": (-200, -200), "8": (0, -200), "9": (200, -200)
}

# 画井字棋盘
def draw_board():
    board_drawer = turtle.Turtle()
    board_drawer.speed(0)
    board_drawer.hideturtle()
    for i in range(1, 3):
        board_drawer.penup()
        board_drawer.goto(-300, 200 * i - 300)
        board_drawer.pendown()
        board_drawer.goto(300, 200 * i - 300)
        board_drawer.penup()
        board_drawer.goto(200 * i - 300, -300)
        board_drawer.pendown()
        board_drawer.goto(200 * i - 300, 300)
    board_drawer.penup()

draw_board()

# 定义绘制X和O的函数
def draw_x(x, y):
    pen = turtle.Turtle()
    pen.pencolor("blue")
    pen.penup()
    pen.goto(x - 50, y - 50)
    pen.pendown()
    pen.setheading(45)
    pen.forward(141)
    pen.penup()
    pen.goto(x + 50, y - 50)
    pen.pendown()
    pen.setheading(135)
    pen.forward(141)
    pen.hideturtle()


def draw_o(x, y):
    pen = turtle.Turtle()
    pen.pencolor("red")
    pen.penup()
    pen.goto(x, y - 50)
    pen.pendown()
    pen.circle(50)
    pen.hideturtle()

# 检查胜利条件
def check_winner():
    pass

# 定义放置棋子的函数
def place_mark(key):
    global current_player

    if key not in positions:
        return

    x, y = positions[key]
    row = int((y + 300) // 200)
    col = int((x + 300) // 200)

    if board[row][col] == "":
        if current_player == "X":
            draw_x(x, y)
            board[row][col] = "X"
            current_player = "O"
        else:
            draw_o(x, y)
            board[row][col] = "O"
            current_player = "X"

        winner = check_winner()
        if winner:
            screen.title(f"井字棋游戏 - {winner} 赢了!")
            screen.ontimer(reset_game, 2000)
        elif all(all(cell != "" for cell in row) for row in board):
            screen.title("井字棋游戏 - 平局!")
            screen.ontimer(reset_game, 2000)

# 重置游戏
def reset_game():
    pass

def bind_key():
    # 键盘事件绑定
    for key in positions.keys():
        screen.onkey(lambda k=key: place_mark(k), key)

bind_key()
# 让屏幕开始监听键盘事件
screen.listen()
# 进入主循环,保持窗口打开
screen.mainloop()
井字棋

Turtle调试与优化

常见问题与解决方法

  • 图形显示异常:可能是因为坐标计算错误或绘制逻辑有误,检查坐标计算和绘制逻辑,确保每个图形元素的位置和大小正确。同时,使用try-except结构捕获异常,输出调试信息以排查错误。
  • Turtle响应慢的问题,可能是由于绘制过程中的重复计算或不必要的绘制操作。尽量减少不必要的绘制操作。使用合适的数据结构来缓存和重复利用计算结果。

性能优化

  • tracer()函数: 用于控制Turtle绘制过程的速度,可通过设置参数来调节绘制速度和刷新频率。
  • update()函数: 用于手动刷新Turtle窗口,可在绘制完成后调用以一次性刷新窗口,提高绘制效率。

控制绘制速度

  • 时间控制: 使用time模块来控制绘制速度,例如使用time.sleep()函数来在绘制过程中添加延迟,调节绘制速度。
  • 循环控制: 在绘制循环中适当添加条件判断,控制绘制过程的速度和流畅度,避免过快或过慢的绘制效果。

总结

Python的turtle库使用还是相对比较简单,很适合新手用来练习,主要是写完之后有结果出来,能刺激学习欲望。
除了开发《井字棋》以外,turtle库还可以开发其他经典游戏,比如《1024》、《贪吃蛇》、《坦克大战》等,都可以尝试用来练练手。

参考文献

  • Python官方文档:https://docs.python.org/zh-cn/3/library/turtle.html#turtle.onkeypress

最后,祝大家六一儿童节快乐,玩游戏快乐!

简单的打印,还可以玩得这么花?

在 Python 编程中,print函数是最基础但却非常重要的工具之一。它不仅用于输出信息到控制台,还可以用于调试和展示程序的运行状态。

print 函数在python3里已经从python2关键字升级为了函数,这让它有了更丰富的用法和玩法,今天我们将深入探讨print函数的各种用法和参数设置。

常用玩法

最简单的

最简单的 print 函数调用只需要一个参数,通常是我们的第一个python程序的内容

print("Hello, world!")

这段代码将输出 Hello, world! 到控制台。

Hello, world!
多个参数

print 函数可以接受多个参数,并将它们以空格分隔的形式输出:

print("Hello", "world", 2024)

输出结果为:

Hello world 2024
自定义分隔符

通过sep参数,可以自定义多个参数之间的分隔符(默认情况下sep参数指定为空格符)。例如,指定使用逗号和空格作为分隔符:

print("Hello", "world", 2024, sep=", ")

输出结果为:

Hello, world, 2024
自定义结束符

默认情况下,print 函数输出内容后会自动添加一个换行符。可以通过 end 参数自定义结束符,例如使用空格代替换行符:

print("Hello, world", end=" ")
print("Welcome to Python!")

输出结果为:

Hello, world Welcome to Python!
输出到文件

print 函数的 file 参数允许我们将输出重定向到文件而不是控制台。例如,将内容写入一个文本文件:

with open("output.txt", "w") as file:
    print("Hello, world!", file=file)

这段代码会在当前目录下创建一个名为 output.txt 的文件,并在其中写入 Hello, world!

flush 参数

flush 参数控制输出缓冲区的刷新。如果设置为 Trueprint 函数会立即将内容输出到目标设备,而不是等待缓冲区满或程序结束时再输出:

import time

print("Loading", end="", flush=True)
for i in range(5):
    time.sleep(1)
    print(".", end="", flush=True)

输出结果为:

Loading.....

上面代码每隔一秒钟会打印一个点,是不是很像某个下载或者进度加载过程?

高级用法

格式化输出

使用 f-string(格式化字符串)来更灵活地格式化输出内容,这样输出的内容更加连贯(而不是像空格有割离感):

name = "Jeff"
age = 25
print(f"Name: {name}, Age: {age}")

输出结果为:

Name: Jeff, Age: 25

另外,Python3.8以上的版本甚至可以这样打印

name = "Jeff"
age = 25
print(f"{name=}, {age=}")

输出为:

name='Jeff', age=25
多行输出

可以使用三重引号(””” 或 ”’)来输出多行字符串:

print("""
Line 1
Line 2
Line 3
""")

输出结果为:

Line 1
Line 2
Line 3

花活玩法

带颜色字符串

使用 ANSI 转义序列可以在支持这些序列的终端中输出带颜色的文本。以下是一些常见的 ANSI 转义序列:

前景色:

  • 黑色: \033[30m
  • 红色: \033[31m
  • 绿色: \033[32m
  • 黄色: \033[33m
  • 蓝色: \033[34m
  • 紫色: \033[35m
  • 青色: \033[36m
  • 白色: \033[37m
    背景色:
  • 黑色: \033[40m
  • 红色: \033[41m
  • 绿色: \033[42m
  • 黄色: \033[43m
  • 蓝色: \033[44m
  • 紫色: \033[45m
  • 青色: \033[46m
  • 白色: \033[47m
    重置颜色:
  • \033[0m

例如:

print("\033[32m这是绿文本\033[0m")
print("\033[33m这是黄色文本\033[0m")
print("\033[31m\033[47m这是红色白底文本\033[0m")

输出:

循环加载

上面已经有一个loading加载的例子了,但是有些时候,我们重复在某一行进行循环加载,尤其是模拟网络下载时(比如像npm install某些库时的命令行动态效果)。

因为涉及到同行循环,所以会用上退格符 \b,来回退覆盖已经输出的字符串。

import time

print("Loading", end="", flush=True)
for _ in range(5):
    for _ in range(3):
        time.sleep(1)
        print(".", end="", flush=True)
    time.sleep(1)
    print("\b\b\b   \b\b\b", end="", flush=True)

其实还有其他特殊字符(如\t\r等)可以和print进行联动

打印形状
打印爱心
print('\n'.join([''.join([('*' if ((x * 0.04) ** 2 + (y * 0.1) ** 2 - 1) ** 3 - (x * 0.04) ** 2 * (y * 0.1) ** 3 <= 0 else ' ') for x in range(-30, 30)]) for y in range(15, -15, -1)]))

效果图:

“佛祖保佑,永无BUG”,这个打印可以用在程序启动的时候,有buff加成,非常玄学。

注意,这里用的是raw stingr字符串,主要是避免里面的\符号被转义了。

print("""
                  _ooOoo_
                 o8888888o
                 88" . "88
                 (| -_- |)
                 O\  =  /O
              ____/`---'\____
            .'  \\|     |//  `.
           /  \\|||  :  |||//  \\
          /  _||||| -:- |||||-  \\
          |   | \\\  -  /// |   |
          | \_|  ''\---/''  |   |
          \  .-\__  `-`  ___/-. /
        ___`. .'  /--.--\  `. . __
     ."" '<  `.___\_<|>_/___.'  >'"".
    | | :  `- \`.;`\ _ /`;.`/ - ` : | |
    \  \ `-.   \_ __\ /__ _/   .-` /  /
=====`-.____`-.___\_____/___.-`____.-'======
                  `=---='

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
         佛祖保佑       永无BUG
""")

输出:

打印logo

以打印“X秘书”为例

print("""
X         X         X      X                 X     X                          
 X       X       X X         X   X        XXXXXXXX  X 
   X   X           X       X    X            X   X
     X          XXXXXXX  X X   X  X      XXXXXXXXXXXX                        
   X   X           X    X  X X     X         X      X
 X       X       X X X     X    x            X    xXX    
X         X     X  X  X  X XXXXXX            X
"""
)

输出:

结论
print 函数是 Python 中最基本但却功能强大的工具之一。通过了解和掌握 print 的各种参数和用法,可以更高效地调试和展示程序的运行结果。

如果你还发现print有其他玩法,欢迎讨论交流