实践 - 使用Python画一棵递归分形树

本文介绍了如何使用Python在Tkinter上实现递归生成并显示分形树的方法,涵盖数据结构-树的概念、递归表示、坐标系、树的生成与显示,以及Tkinter的基本画图技巧。读者将学习面向对象编程、生成器、递归函数和Tkinter图形界面的知识。
摘要由CSDN通过智能技术生成

本实践中,作者要介绍用Python在Tkinter上画一棵树的方法。通过本实践,读者可以:练习面向对象的程序设计方法;了解生成器的使用方法;运用递归函数;了解Tkinter画图的基本方法;以及学习“树”这种重要的数据结构。

版权声明

本文可以在互联网上自由转载,但必须:注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。

本文不可以以纸质出版为目的进行改编、摘抄。

本文节选自作者的《Python编程基础及应用》视频教程。

本实践中,作者要介绍用Python在Tkinter上画一棵树的方法。通过本实践,读者可以:练习面向对象的程序设计方法;了解生成器的使用方法;运用递归函数;了解Tkinter画图的基本方法;以及学习“树”这种重要的数据结构。

​ 在本书配套的网站上,你可以下载到本实践的全部代码。本章要画的图“大概”长成下面这样。为什么说是大概呢? 因为树的结构是在一定约束条件下随机生成的。
在这里插入图片描述

1. 数据结构 - 树

​ 要完成本实践,读者首先要了解一点数据结构 - data structure的知识。数据结构大概是指计算机内部表达和组织数据的方式。

1.1 树

在这里插入图片描述

​ 树-Tree是一种数据结构,它用于模拟真实世界中的树形结构,通常描绘成上图的样子。为了说明方便,作者把每个节点用字母作了标识。

​ T是一棵树,树上的每个圆圈称之为一个节点-node,其中,a是树T的根节点 - root node。节点可以有后代 - descendents,其中,直接的后代称为儿子节点 - child。上图中,b,c,d是节点a的儿子,h,i是节点d的儿子。有儿子的节点称为内节点,没有儿子的节点称为叶子-leaf。上图中,e,f,k等节点没有儿子,作者用粗线圆作了标识,它们是叶子。

​ T’是树T的一棵子树-sub tree,其根节点为d,我们称树T’是树T中以d节点为根的子树。理论上,任何一个节点其及全部后代都可视为一棵树。

​ 上树中右侧的数字0,1,2,3表示节点在树中的深度-depth。a节点的深度为0,g,h节点的深度为1,j,k,l节点的深度为3。

1.2 树的递归表示

class TreeNode:
    def __init__(self,label):
        self.sLabel = label
        self.children = []

a = TreeNode('a')
b = TreeNode('b')
c = TreeNode('c')
d = TreeNode('d')
a.children.extend([b,c,d])
b.children.extend([TreeNode('e'),TreeNode('f')])
...

​ 上边的代码展示了一个表示树的Python递归结构。TreeNode类有一个sLabel属性,存储节点的标签。此外,该类还有一个类型为列表的数据成员children,这里面存储该结点的全部儿子节点。而儿子节点的类型也为TreeNode,其也有children列表用于存储该儿子节点的儿子。当一个TreeNode对象的children列表为空时,表明该节点为叶子。

​ 上述代码的后几行建立了前小节中树结构的一部分。其中a为根节点,a有b,c,d三个儿子,而b又有两个儿子e和f…

2. Top-Left坐标系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6MOpO1zu-1578801407595)(/images/1542197988935.png)]

​ 在图形程序设计中,一般采用Top-Left坐标系。如上图,以屏幕或者窗口的左上角为原点,横向为x轴,纵向为y轴。位置越靠右,x越大;位置越靠下,y越大。窗口内的任意一个点,可用一对(x,y)构成,这一对(x,y)值,称为该点的坐标。

3. 实例中的树表达

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class TreeNode:
    maxDepth = 8
    nBranches = 3
    def __init__(self, bottom, top, depth=0):
        self.bottom = bottom
        self.top = top
        self.drawingTop = top
        self.depth = depth
        self.children = []
        self.__generateDescendents()

​ 如代码所示,Point类有成员x和y,表明一个坐标点。TreeNode类表示一个树节点。类成员maxDepth表示树所允许的最大深度。类成员nBranches表示“建议”的树分叉数,即一根树支将分出多少根下层树支。此处之所以用了“建议”一词,是因为实际树的生成过程中分支数会引入随机成分。

​ TreeNode对象的children属性是其儿子列表,depth表示该节点在树中的深度。TreeNode对象的另外几个属性需要结合下图来理解,这是作者在树上取下的一个小树支。下图中,a是一个TreeNode节点,其bottom表示以该节点为根的子树的“下”顶点;其top表示以该节点为根的子树的“上”顶点。注意,这里的上下打了引号,那是因为实践中,树支因地球引力,可能事实上倒垂向下。

drawingTop定义为该节点自身(不含其子孙)的描绘用“上”顶点。a节点在树中的实际表现为一段树支,树支的描绘范围即为bottom至drawingTop点。从drawingTop一直到top则是a的子孙们的描绘区域。下图中,b,c,d为a的儿子,b,c,d也是TreeNode类型,其bottom应等于a的drawingTop。
在这里插入图片描述

​ 此外,TreeNode类构造函数的最后一行执行了一个“不公开”成员函数__generateDescendents(),这个函数将递归生成以该节点的根的子树,该子树中节点的深度不超过TreeNode.maxDepth。该部分内容见后节。

变量、函数、类的判定准则
- 新手们写出来的程序通常可读性和健壮性都有问题。作者建议,当读者试图定义一个变量、函数、类时,都必须满足如下规则:如果其作用和取值可以用简洁明了没有歧义的讲清楚,那么其存在可能是合理的。如果说不清道不明,那么读者需要另寻它法。本实践中的,TreeNode之bottom,top,drawingTop,受限于其复杂性,很难用一句话描述,但至少借助于图示,是可以讲清楚的。

- Python之禅里有“If the implementation is easy to explain, it may be a good idea"。这里,请读者品味其含义。

4. 树的递归生成

4.1 分叉树支生成

class TreeNode:
    ...
    def __generateDescendents(self):
        "Recursively build sub-tree of the current node."
        n = random.randint(TreeNode.nBranches//2,round(TreeNode.nBranches*1.5))
        n = n if n >=1 else 1

        r = 0.20 + random.random() * 0.2
        x = self
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值