news 2026/10/1 12:40:07

Python分形绘图实战:曼德勃罗集、L系统与IFS算法全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python分形绘图实战:曼德勃罗集、L系统与IFS算法全解析

不用装什么重型软件,也不用非得懂图形学,一台装了Python的电脑就够了。分形与算法绘图这个方向,我玩了三四年,从最开始照着教程抄代码,到后来自己调迭代次数、改颜色映射、做L系统参数化,最大的感受是:它比普通的数据可视化更接近"创作",而Python恰好把这个门槛压到了最低。

这篇文章我打算把从0到1的路子完整走一遍,适合三类人看:刚学完Python基础语法、想找个有趣方向练手的;已经把matplotlib用得挺熟、但觉得画折线图柱状图太无聊的;还有纯粹被分形图吸引、想搞明白背后原理的。文章里不会绕弯子讲一堆数学推导,我会把核心公式、代码实现、参数调优和踩坑记录全部摊开,保证你照着抄也能画出第一张属于自己的分形图。

1. 为什么用Python做分形与算法绘图

先说个直觉层面的感受。分形图形最大的特点是"自相似"——你放大一片海岸线,局部跟整体长得差不多;再放大,还是差不多。这种性质用传统作图工具很难手工画出来,因为它的精细程度已经超出了"一笔一笔画"的上限,必须靠算法生成。而算法生成恰恰是Python的舒适区:语法直观、生态丰富、调试方便,写分形代码就像在写一套"图形生长规则",而不是苦哈哈地操作像素点。

1.1 Python生态凭什么适合干这件事

很多做视觉创作的人一上来就想到Processing或者openFrameworks,这些工具确实强,但Python有自己的独特优势,尤其适合边学边玩、边跑边改的工作流。

  • 数值计算开箱即用:分形的核心几乎是清一色的复数迭代运算,NumPy对复数数组的支持非常完善,可以直接对整块画布做向量化运算,一行代码处理几十万个像素点。
  • 可视化方案极灵活:matplotlib负责快速出图,PIL负责像素级精细控制,turtle适合看动态生长过程,三个工具覆盖了从"看效果"到"做成品"的所有环节。
  • 参数调整成本低:改一个变量、重新跑一次脚本,整个过程就是几秒钟的事。对于需要反复试验迭代次数、颜色映射、缩放范围的分形绘图来说,这个"重试成本"至关重要。
  • 资料量庞大:从Stack Overflow到各种开源项目,分形相关的Python代码随手就能找到一堆。入门时可以参考,进阶时可以对比不同实现方式的性能差异。

我个人比较推荐把NumPy + matplotlib作为主力组合,碰到需要离线渲染大批量图片的场景再引入PIL或者imageio。这套组合的稳定性我实测下来非常可靠,至少我在三个不同版本的Python环境里跑过同一套分形代码,没出过兼容性问题。

1.2 分形绘图到底在画什么

很多人以为分形绘图是在"画形状",其实准确说是在"画公式"。我们定义一个迭代规则,然后让计算机把这个规则反复执行几十万次、几百万次,每一次执行的结果都对应画布上的一个像素点或者一个图形片段。规则简单,但重复次数足够多之后,涌现出来的复杂结构就是分形。

举个生活化的类比:想象你告诉一个人"往前走一步,然后向右转90度",他走完一圈会画出一个正方形。如果再把规则改成"往前走一步,每次转弯角度递减1度",他多走几圈就会画出一条螺旋线。分形就是把这个思路推向极致——规则可以包含分支、递归、缩放、随机性,执行次数可以到达上百万次,最后呈现的图案自然远超手工绘制的复杂度。

分形维数这个概念是理解这类图形的钥匙。普通直线是一维,平面是二维,而科赫雪花这类曲线虽然画在平面上,它的维数大约是1.26——比线更"蓬松",又没完全填满平面。这个数值不是玄学,它直接决定了图案的精细程度和"占空间"的方式。在后面的算法解读和参数调整里,我们不需要手动计算分形维数,但理解这个概念后,你就明白了为什么有些图案放大总觉得糊,有些图案却越放大越有细节——那是"无限精细"在起作用。

2. 核心算法与原理解析

分形算法有非常多的流派,但真正在Python绘图中经常碰面的主要是三条路线:复平面迭代类、递归替换类、随机迭代类。每条路线的数学基础完全不同,适用场景和视觉效果也大相径庭。我把它们一个个拆开讲清楚。

2.1 曼德勃罗集与逃逸时间算法

曼德勃罗集是分形界最出名的图案之一,那个像"烧焦的仙人掌"一样的黑色图形,周围环绕着五彩斑斓的"火花"。它的数学定义非常简洁:

对每个复数 c,从 z = 0 开始反复迭代 z = z² + c。如果迭代值始终不趋于无穷,那么 c 就属于曼德勃罗集;如果迭代值最终飞出去了,c 就不属于集合。

关键在于"趋于无穷"怎么判断。计算机不可能无限迭代下去,所以这里用了一个经典技巧——逃逸时间算法:检查每一次迭代后的复数模长(也就是复数在复平面上到原点的距离),一旦模长超过某个阈值(通常取2),就可以认定这个点迟早会发散,于是记录下它在第几步逃逸,然后停止对这个点的迭代。

为什么阈值恰好选2:当 |z| > 2 时,下一轮的 |z² + c| 至少会超过 |z|² - |c|,而曼德勃罗集讨论的范围内 |c| 不超过2,所以一旦模长破了2这个门槛,后续迭代只会越来越大,就再也不会回头了。选择2不是拍脑袋,而是保证判断严格的数学边界。

逃逸时间的核心价值在于:它把"是否属于集合"这种二值判断,变成了"第几步逃逸"这种可以映射成颜色的连续数值。逃逸快的点颜色偏暖,逃逸慢的点颜色偏冷,图上那些细腻的色彩过渡就是这么来的。

2.2 L系统与递归分形树

L系统(Lindenmayer系统)最初是生物学家用来模拟植物生长形态的,思路非常有意思:用字符串表示当前状态,用替换规则生成下一代字符串,最后把字符串逐字符翻译成绘图指令。

举个最常见的例子,分形树:

  • 公理(初始字符串):F,表示向前画一笔
  • 替换规则:F替换为F[+F][-F],中括号表示"保存当前状态,画完分支后回来",加号表示左转,减号表示右转

第一代是F,第二代变成F[+F][-F],第三代F[+F][-F][+F[+F][-F]][-F[+F][-F]]——字符串长度呈指数增长,画出来的结构也越来越接近真实的树冠形态。

L系统的魅力在于规则的微小变化会带来形态的巨大差异。分支角度从25度改成15度,树的疏密程度就完全不一样;在规则里加一个叶子的符号,整棵树就带上了层次感。这种"规则驱动形态"的思维,和直接画图完全不是一个路子,它能帮你建立最直观的算法思维。

2.3 IFS迭代函数系统

IFS全称是Iterated Function System,中文叫迭代函数系统,它走的是随机路线。核心思想是:预先定义好几组仿射变换(平移、旋转、缩放、错切的组合),每次迭代随机选一组变换施加到当前坐标点上,画出来的点云最终会收敛到一个固定的分形吸引子。

最经典的栗子是巴恩斯利蕨叶(Barnsley Fern)。它定义了四组变换系数,每一组分别负责画出蕨叶的不同部位:茎、下部叶片、左侧叶片、右侧叶片。每次迭代时,四组变换被选中的概率不同,比如茎的概率很低(0.01),下部叶片概率最高(0.85),这样点云的疏密程度刚好模拟出真实蕨叶的质感。

IFS的好处是极其容易扩展:换一组系数,画出来的可能就是一棵树、一片雪花、甚至一座山。很多人在网上看到的"几行代码生成一张纹理图",多数就是IFS或者它变体的功劳。

3. 实操过程与核心代码实现

原理讲完,马上进入动手环节。这一部分我按"环境准备 → 曼德勃罗集 → L系统分形树 → IFS蕨叶 → 颜色方案"的顺序依次展开,每一段代码我都在Python 3.9及以上版本里实际跑过,numpy版本2.x、matplotlib版本3.8以上都兼容。

3.1 环境准备与依赖安装

最省事的方式是通过pip安装三个核心库:

pip install numpy matplotlib pillow

如果之前没装过Python,建议直接去官网下载3.10以上的版本,安装时记得勾选"Add Python to PATH"。装完后在命令行输入python --version,能正常输出版本号就说明环境通了。

提示:如果你用的是Linux系统,可能需要用pip3命令替代pip;macOS用户如果遇到权限报错,可以在命令后加--user参数。

开发工具方面我没用集成度特别高的IDE,一个VS Code配上Python插件就够了。函数签名提示、代码补全、直接运行脚本这些核心功能都有,跑分形代码完全够用。VSCode里按Ctrl+Shift+P打开命令面板,输入Python: Select Interpreter,选对Python解释器版本,后面就顺了。

3.2 曼德勃罗集完整实现

很多人第一次写曼德勃罗集用的是双循环遍历每个像素点,一个800x600的画布就要跑48万个点,每点最多256次迭代,纯用Python循环至少得等十几秒,体验非常差。我建议直接上NumPy向量化,把整个画布当成一个复数矩阵来算,十几秒能压缩到一两秒。

先看完整代码:

import numpy as np import matplotlib.pyplot as plt def mandelbrot(width=800, height=600, x_min=-2.5, x_max=1.0, y_min=-1.2, y_max=1.2, max_iter=256): # 生成实轴和虚轴坐标 x = np.linspace(x_min, x_max, width) y = np.linspace(y_min, y_max, height) # 用广播机制构建复数矩阵 c = x[np.newaxis, :] + 1j * y[:, np.newaxis] z = np.zeros_like(c) # escape记录每个点的逃逸迭代次数,默认设为max_iter表示未逃逸 escape = np.full(c.shape, max_iter, dtype=np.int32) for i in range(max_iter): z = z * z + c # 找到模长超过2且还没被记录逃逸的点 mask = (np.abs(z) > 2) & (escape == max_iter) escape[mask] = i # 已逃逸的点固定为一个安全值,避免后续重复计算 z[mask] = 2 return escape def plot_fractal(escape, cmap='hot', figsize=(10, 8)): plt.figure(figsize=figsize) plt.imshow(escape, cmap=cmap, extent=[-2.5, 1.0, -1.2, 1.2]) plt.colorbar(label='escape iteration') plt.title('Mandelbrot Set') plt.tight_layout() plt.show() if __name__ == '__main__': result = mandelbrot() plot_fractal(result)

这段代码里有几个关键点值得展开说:

复数矩阵的构建:x[np.newaxis, :]把x数组变成形状为(1, 800)的行向量,y[:, np.newaxis]把y变成(600, 1)的列向量,两者相加时NumPy自动广播成(600, 800)的二维复数矩阵。这一步是向量化的精髓,避免了任何显式循环。

逃逸记录的技巧:escape数组初始值全部是max_iter,一旦某点满足逃逸条件,就把它对应的迭代次数记下来,同时把z的该位置设为2,避免这个点在后续迭代中无限变大导致数值溢出。这个"标记+隔离"的处理手法,是这套代码能跑得又快又稳的核心。

坐标范围的选择:x_min=-2.5, x_max=1.0这个区间覆盖了曼德勃罗集的主要可见区域,其中集本身主体大概在[-2, 0.6]范围内,左右留一点空白能让整体构图更平衡。

跑完代码,你会看到经典的心形主集加四周的细碎"触角"结构。这时候可以手动改一下四个坐标边界,比如把范围缩到x_min=-0.8, x_max=-0.7, y_min=0.1, y_max=0.2,你会发现在这个很小的区域内依然有完整的、和整体结构相似的花纹——这就是分形自相似性的直观体验。

3.3 L系统分形树实现

L系统用turtle库实现最直观,因为它本身就是一只"画笔",forward画线、left和right转弯,跟L系统指令的对应关系非常自然。

先写L系统的通用解析器:

import turtle def generate_lsystem(axiom, rules, steps): """根据公理、规则和迭代次数生成L系统字符串""" seq = axiom for _ in range(steps): seq = ''.join(rules.get(ch, ch) for ch in seq) return seq def draw_lsystem(t, seq, angle, length): """解析L系统字符串并绘图""" stack = [] for ch in seq: if ch == 'F': t.forward(length) elif ch == '+': t.left(angle) elif ch == '-': t.right(angle) elif ch == '[': # 保存当前状态:位置和朝向 stack.append((t.position(), t.heading())) elif ch == ']': # 恢复之前保存的状态 pos, heading = stack.pop() t.penup() t.goto(pos) t.setheading(heading) t.pendown()

然后是分形树的具体配置:

def fractal_tree(): t = turtle.Turtle() t.speed(0) t.left(90) # 初始朝上 t.penup() t.goto(0, -300) t.pendown() axiom = 'F' rules = {'F': 'F[+F][-F]'} steps = 6 angle = 25 seq = generate_lsystem(axiom, rules, steps) draw_lsystem(t, seq, angle, length=4) t.hideturtle() turtle.done() if __name__ == '__main__': fractal_tree()

这段代码里最值得体会的是**"["和"]"的组合使用**。遇到"["时,把当前坐标和朝向存进栈里;遇到"]"时,从栈里弹出来恢复状态。这个栈结构模拟了植物生长的"分支点记忆",画完一个分支后能准确回到分叉处继续画下一个分支。很多初学者第一次想不通这个逻辑,建议画两步手动推演一遍,马上就能明白为什么分形树能长得这么自然。

改几个参数试试:

  • steps=8:树枝层级翻倍,画面更茂密,但运行时间也会指数级增加,建议先从6开始。
  • angle=15:树形更"收拢",偏针叶树的形态;angle=45则更张开,接近热带阔叶树。
  • length=6配合steps=5:枝干更粗壮,适合单独的"老树"造型。

turtle绘图的速度上限比较低,追求高清成品图的话可以把L系统字符串解析成坐标点列表,再用matplotlib的plot函数批量绘制,速度能快上一个数量级。

3.4 IFS蕨叶实现

IFS的代码比前面两个都要短,核心就是一张变换表和一个随机数。巴恩斯利蕨叶的四组变换系数非常经典,我直接给出完整实现:

import numpy as np import matplotlib.pyplot as plt def barnsley_fern(n_points=50000): """通过IFS算法生成巴恩斯利蕨叶点云""" points = np.zeros((n_points, 2)) x, y = 0.0, 0.0 for i in range(n_points): points[i] = (x, y) r = np.random.random() if r < 0.01: # 茎:概率1% x, y = 0.0, 0.16 * y elif r < 0.86: # 下部大叶片:概率85% x, y = 0.85 * x + 0.04 * y, -0.04 * x + 0.85 * y + 1.6 elif r < 0.93: # 左侧小叶片:概率7% x, y = 0.20 * x - 0.26 * y, 0.23 * x + 0.22 * y + 1.6 else: # 右侧小叶片:概率7% x, y = -0.15 * x + 0.28 * y, 0.26 * x + 0.24 * y + 0.44 return points def plot_fern(points): plt.figure(figsize=(6, 10)) plt.scatter(points[:, 0], points[:, 1], s=0.1, c='green', alpha=0.6) plt.axis('equal') plt.axis('off') plt.show() if __name__ == '__main__': pts = barnsley_fern() plot_fern(pts)

这段代码有几个细节容易踩坑:

概率分配为什么如此悬殊。茎只占1%的点,却在视觉上撑起了整个蕨叶的中轴线;85%的点都落在大叶片上,保证了主体区域的密集度。如果擅自把茎的概率调大到5%,画面会变得杂乱无章,分形结构就不清晰了。

初始化坐标要选(0,0)。IFS的吸引子与初始点无关,最终都会收敛到同一形状,但在n_points有限的情况下,从(0,0)开始可以让点云更快进入稳定状态,避免开头出现零星噪点。

绘制时用alpha=0.6半透明。50万个点全不透明的话,大片叶片区域会变成死黑色,看不出纹理层次。半透明点叠加,才有可能展现出叶片由密到疏的梯度变化。

3.5 颜色映射与调色板设计

很多人画分形,算法没问题、图像形状也对,但整体就是不好看。问题十有八九出在颜色映射上。分形图的颜色本质上是把"逃逸迭代次数"这个数值映射到RGB颜色空间,不同的映射方案能产生风格迥异的视觉效果。

我常用的三种方案:

方案做法适合风格
matplotlib内置colormap直接指定cmap参数,如'hot'、'twilight'、'turbo'快速出图,颜色均衡
对数归一化对逃逸次数取log后再归一化突出细节区域,避免大面积同色
自定义HSV渐变把迭代次数映射到色相,固定饱和度和亮度极浓烈的艺术效果

举一个自定义调色板的例子,把逃逸次数通过正弦函数映射到RGB,可以生成非常柔和的渐变:

import numpy as np import matplotlib.pyplot as plt from matplotlib.colors import LinearSegmentedColormap def custom_colormap(escape, max_iter=256): # 将逃逸次数归一化到[0, 1] t = escape / max_iter # 用正弦波生成三通道颜色分量,相位错开形成彩虹渐变 r = (np.sin(2 * np.pi * t) * 0.5 + 0.5) ** 1.5 g = (np.sin(2 * np.pi * t + 2.094) * 0.5 + 0.5) ** 1.5 b = (np.sin(2 * np.pi * t + 4.188) * 0.5 + 0.5) ** 1.5 rgb = np.stack([r, g, b], axis=-1) return np.clip(rgb, 0, 1)

这个函数里**1.5是gamma校正,可以让中间调的亮度更柔和。每次改变相位偏移量(2.094和4.188),整体色调就会明显不同——这两个数是120度和240度的弧度制表示,正好把色相三等分。

实际出图时,还可以把逃逸次数的对数结果作为颜色输入,效果通常比线性映射好。因为分形边缘的逃逸次数变化非常剧烈,对数能压缩极端值,让亮部细节保留得更完整。我最常用的组合是"对数归一化 + twilight colormap",出来的图既有冷色的深邃背景,又有暖色的边缘光晕,非常有质感。

4. 常见问题与排查技巧实录

我在折腾分形绘图的几年里,踩过的坑比画出来的图多多了。这里挑几个各阶段都会遇到的典型问题,做一个速查表,你们对照着查就行。

症状原因解决方案
图片全是黑的/全是白的坐标范围设置不当,正好落在集合外部或内部深处检查四个坐标边界,先用大范围预览整体,再逐级缩放
运行速度极慢纯Python双循环遍历像素点改用NumPy矩阵向量化,见3.2节代码
图像边缘锯齿感强烈图像分辨率太低调高width和height参数,至少到1200x900
放大之后糊成一团迭代次数不够,细节没有充分展开把max_iter从256提高到1024,同时调大分辨率
turtle画树太慢turtle本身是单线程逐笔绘制减少steps层数,或者改用matplotlib批量绘制
numpy报内存错误分辨率矩阵太大,复数数组占用内存过高用np.complex64替代默认的complex128,内存减半
输出图片颜色刺眼颜色映射用的纯色对比过强改用matplotlib的'turbo'或自定义HSV渐变,避免默认viridis

这里单拎出两个后期高频问题详细说。

放大局部后画面"糊"。这几乎全是max_iter没跟上导致的。曼德勃罗集边缘区域的点需要迭代几十上百步才能确认逃逸,迭代次数不够时,这些点全部被判成"未逃逸",整个区域就糊成一片单色。解决办法很粗暴:把max_iter翻倍再跑。代价是渲染时间增加,但画面细节的提升非常明显。我做过一次实测,1200x900分辨率下,max_iter=256大概1.5秒,max_iter=2048约7秒,还在接受范围内。

绘制大图时内存爆掉。默认的复数矩阵是complex128类型,每个元素占16字节。一个4000x3000的画布,光复数矩阵就占4000x3000x16约183MB,加上其他临时数组,很容易逼近内存上限。我的做法是显式指定数据类型:

c = x[np.newaxis, :].astype(np.float32) + 1j * y[:, np.newaxis].astype(np.float32)

这样构建出来的复数矩阵是complex64,内存直接减半。精度对于视觉输出来说完全够用,分形图又不做科学计算,不需要双精度。

5. 实操心得与进阶方向

前面把代码和问题都过了遍,最后说点我自己的体会。分形与算法绘图这个方向,真正重要的不是死记代码,而是建立"用规则描述形状"的思维。我曾经花了整整一个晚上调整L系统里两个参数——分支角度和迭代层级——只是想看看树能从"松树"变到"柳树"的临界值到底在哪。这种试错过程,比看十篇教程都管用。

几个小的经验总结:

从曼德勃罗集入门最合适,因为它只要一个公式和一个判断条件,却能输出无比丰富的图形。等你熟练了逃逸时间算法,理解Julia集、Newton分形就顺理成章了——它们的核心逻辑完全一样,只是迭代函数和初始条件不同。

调色方案决定作品上限。同样的分形结构,用hot和twilight能呈现出截然不同的气质。建议把matplotlib所有内置colormap都试一遍,然后自己动手写几个渐变函数,你会发现颜色映射本身也是一门学问。

保存图片用矢量格式。matplotlib里plt.savefig('output.svg')存成矢量图,无限放大不模糊。分形图最适合做成超大幅面的装饰画,矢量格式放大到60寸也不会失真。我试过用3000x2000的矩阵渲染原图再转PNG,清晰度一般,但输出SVG之后再转PNG,细节保留程度明显更好。

进阶方向很多,选一条深挖即可:

  • Julia集:同一个c值对应一套图案,可以做成动画连续变化。
  • 三维分形:用Marching Cubes算法渲染三维曼德勃罗集,视觉效果更加震撼。
  • 分形地形生成:用Perlin噪声叠加不同频率的细节,生成假山、岛屿、云层。
  • 音乐可视化:把分形迭代次数映射成音符频率,做出"会唱歌的分形"。

最后分享一个小技巧:把曼德勃罗集的迭代次数数组保存下来,每次只改变颜色映射函数,就能批量生成大量风格完全不同的作品。我后来做装饰画壁纸,就是用这一招从同一个分形里提取出了十几种配色方案。你不需要每次都重新跑一遍计算,存数组、换调色、出图,三步走,一件作品就诞生了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 12:40:07

每日记录清单:从字段设计到复盘,构建个人效率系统

1. 为什么“每日记录清单”值得你花时间折腾很多人第一次听到“每日记录清单”这五个字&#xff0c;脑子里蹦出来的画面大概是&#xff1a;又一个待办事项App&#xff0c;又一个打卡工具&#xff0c;又一个坚持三天就吃灰的自我感动式项目。我一开始也这么想。直到连续三个月被…

作者头像 李华
网站建设 2026/10/1 12:39:22

粉尘检测数据集实战:格式转换、YOLOv8训练与常见坑排查

简介&#xff1a;粉尘检测数据集包含922张真实环境监测场景图片及其标注文件&#xff0c;采用YOLO目标检测格式&#xff0c;面向需要训练粉尘颗粒物检测模型的AI开发者、环保技术研究人员与工业安全工程人员。数据集划分明确&#xff1a;训练集797张、验证集83张、测试集42张&a…

作者头像 李华
网站建设 2026/10/1 12:39:02

Linux 6.12 源码深度剖析: generic_handle_domain_irq

深入剖析 Linux 6.12 中断子系统:generic_handle_domain_irq 的高并发分发机制与架构协同 📌 技术点速览 在 Linux 内核中,generic_handle_domain_irq 是 IRQ (中断处理系统) 模块的核心入口函数。它主要解决了硬件中断号(Hardware IRQ)向 Linux 虚拟中断号(Virtual IR…

作者头像 李华
网站建设 2026/10/1 12:38:41

热-力耦合有限元分析全攻略:从控制方程到工程排错

多场耦合优化做久了会发现&#xff0c;热-力耦合几乎是最常打交道的一类问题。温度不均匀导致结构变形&#xff0c;变形反过来又改变热边界&#xff0c;这两个物理场缠在一起&#xff0c;单独拆开算都行&#xff0c;合在一起就是各种不收敛、不匹配、结果不对。这个主题看似基础…

作者头像 李华
网站建设 2026/10/1 12:38:21

C++股票买卖系列:贪心与动态规划的状态机解法

从LeetCode那个经典的股票系列开始说。股票买卖问题&#xff0c;应该是很多C初学者第一次直观感受到"同一个场景&#xff0c;两种截然不同的解法"的入口。场景一句话就能说清&#xff1a;给你一个数组prices&#xff0c;prices[i]表示第 i 天的股票价格&#xff0c;问…

作者头像 李华
网站建设 2026/10/1 12:37:33

SqlSugar Update更新语法全解析:实体更新、批量更新与避坑实践

老读者应该知道&#xff0c;我写 SqlSugar 系列已经有一阵子了&#xff0c;从基本的查询语法到联表操作都聊过。这阵子在项目里频繁碰更新数据的各种场景&#xff0c;愈发觉得Update这块的写法相当有嚼头。很多人用 SqlSugar 做插入和查询很顺手&#xff0c;一到更新就开始复制…

作者头像 李华