Python中的类和函数的区别

 下述是举了一个遍历二叉树的例子 

from typing import List


class TreeNode:
    #定义树节点
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right


class Solution:
    #定义前序遍历函数
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        # self:这是Python中类方法的第一个参数,它指向当前类的实例。当您在类内部定义一个方法时,
        # 每个方法都至少有一个参数,通常是self,它代表类的实例本身。
        # root: TreeNode:这是第二个参数,root是参数名,而TreeNode是参数的类型注解。这意味着root应该是一个TreeNode类的实例。
        # 类型注解不会改变参数的类型,它只是告诉其他开发者或使用静态类型检查工具(如mypy)这个参数期望的类型。
        res = []

        # dfs(node):这是一个递归函数,它接受一个node参数,该参数代表当前正在访问的树节点。
        # if node is None: :这是一个基本情况检查。如果当前节点为None(即没有子节点或已经到达叶节点的子节点),则函数返回,结束当前的递归调用。
        # res.append(node.val):如果当前节点不为None,则将当前节点的值添加到结果列表res中。
        # dfs(node.left):递归调用dfs函数,以当前节点的左子节点作为参数,继续遍历左子树。
        # dfs(node.right):递归调用dfs函数,以当前节点的右子节点作为参数,继续遍历右子树。
        def dfs(node):
            if node is None:
                return
            res.append(node.val)
            dfs(node.left)
            dfs(node.right)

        # dfs(root):这是对dfs函数的第一次调用,它启动了整个递归过程。参数root是二叉树的根节点,它是遍历的起始点。
        # 之所以要调用dfs(root),是因为递归过程需要一个起始点,即从树的根节点开始遍历。
        # 根节点是整个树的入口点,通过递归调用dfs函数,可以遍历整棵树的所有节点。
        dfs(root)
        return res

    #定义中序遍历函数
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        res = []

        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            res.append(node.val)
            dfs(node.right)

        dfs(root)
        return res

    #定义后序遍历函数
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        def dfs(node):
            if node is None:
                return
            dfs(node.left)
            dfs(node.right)
            res.append(node.val)
        dfs(root)
        return res


#测试代码
if __name__ == "__main__":
    # 构建一个二叉树
    #       1
    #     /   \
    #    2     3
    #   / \   / \
    #  4   5 6   7
    root = TreeNode(1)
    root.left = TreeNode(2)
    root.right = TreeNode(3)
    root.left.left = TreeNode(4)
    root.left.right = TreeNode(5)
    root.right.left = TreeNode(6)
    root.right.right = TreeNode(7)

    #创建一个Solution实例(对象)
    sol = Solution()
    result = sol.preorderTraversal(root)
    result1 = sol.inorderTraversal(root)
    result2 = sol.postorderTraversal(root)
    print(result2)
from typing import List

# 定义二叉树节点类
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# 定义Solution类
class Solution:
    def preorderTraversal(self, root: TreeNode) -> List[int]:
        res = []
        
        def dfs(node):
            if node is None:
                return
            
            res.append(node.val)
            dfs(node.left)
            dfs(node.right)
        
        dfs(root)
        return res

# 测试代码
if __name__ == "__main__":
    # 构建一个二叉树
    #      1
    #     / \
    #    2   3
    #   / \
    #  4   5
    root = TreeNode(1)
    root.left = TreeNode(2)
    root.right = TreeNode(3)
    root.left.left = TreeNode(4)
    root.left.right = TreeNode(5)

    # 创建Solution类的实例
    sol = Solution()

    # 调用preorderTraversal方法并打印结果
    print(sol.preorderTraversal(root))  # 应该输出 [1, 2, 4, 5, 3]
from typing import List

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def preorder_traversal(root: TreeNode) -> List[int]:
    res = []
    
    def dfs(node):
        if node is None:
            return
        res.append(node.val)
        dfs(node.left)
        dfs(node.right)
    
    dfs(root)
    return res

# 测试代码
if __name__ == "__main__":
    root = TreeNode(1)
    root.left = TreeNode(2)
    root.right = TreeNode(3)
    root.left.left = TreeNode(4)
    root.left.right = TreeNode(5)
    print(preorder_traversal(root))  # 输出: [1, 2, 4, 5, 3]

 为什么要创建一个类来实现遍历?为什么又要创建一个类的实例?只用函数实现调用和用创建实例类来调用有什么区别?

为什么创建一个类来实现遍历?

  1. 封装:类允许我们将数据(属性)和行为(方法)封装在一起。在二叉树遍历的情况下,Solution类将遍历算法封装起来,使其更加模块化和可重用。

  2. 可维护性:通过类,我们可以更容易地管理和维护代码。如果将来需要修改遍历算法,我们只需要修改Solution类中的preorderTraversal方法,而不需要触及使用该方法的代码。

  3. 可扩展性:如果我们想要为二叉树添加更多的操作(例如中序遍历、后序遍历),可以在Solution类中添加更多的方法,而不需要定义多个独立的函数。

  4. 命名空间:类为方法提供了一个命名空间,有助于避免全局命名空间的污染,并且可以清晰地表达方法与数据之间的关系。

为什么创建类的实例?

  1. 实例化:要使用类定义的方法,我们需要创建类的实例(对象)。实例化过程分配内存来存储对象的属性,并使方法可供调用。

  2. 状态:类的实例可以保存状态。虽然在这个特定的例子中,Solution类没有使用实例属性来保存状态,但如果需要,可以在实例中保存与遍历相关的状态。

函数调用与类实例调用的区别:

  • 函数调用

    • 通常用于执行单一的操作,不与特定的数据结构或状态相关联。
    • 直接通过函数名调用,不需要实例化。
    • 不保存状态,所有数据都是通过参数传递。
  • 类实例调用

    • 适用于需要封装多个操作和数据的情况。
    • 需要先创建类的实例,然后通过实例调用方法。
    • 可以保存状态,即使方法调用结束后,实例属性仍然存在。

为什么说要使用类定义的方法,我们需要创建类的实例(对象) ? 

在面向对象编程(OOP)中,类(Class)定义了一个数据类型的蓝图,它包含了数据成员(属性)和方法。而对象(Object)是类的实例,是实际存在的实体。以下是为什么需要创建类的实例来使用类定义的方法的几个原因:

1. 实例化过程:

  • 分配内存:当我们创建一个类的实例时,Python 会为这个实例分配内存,用于存储其属性和方法的引用。
  • 初始化:实例化过程中会自动调用类的构造函数(__init__方法),允许我们初始化实例的属性。

2. 封装:

  • 数据和行为:类将数据和操作数据的方法封装在一起。创建实例是为了将这些数据和操作应用于具体的对象。
  • 状态:每个实例都有自己的状态(即属性值),即使是同一个类的不同实例,它们的状态也可能是不同的。

3. 多态性:

  • 方法重写:子类可以重写(覆盖)父类的方法。通过创建实例,我们可以调用特定子类的实现,而不是仅仅调用父类的方法。

4. 方法的作用域:

  • 实例方法:类中定义的方法默认是实例方法,它们操作的是类的实例(即对象)。这些方法必须通过实例来调用,因为它们需要访问实例的数据。
  • 类方法:虽然有些方法可以使用类方法(通过装饰器@classmethod定义),它们操作的是类本身而不是实例,但大多数方法都是实例方法。

5. 访问控制:

  • 私有属性和方法:类中可以定义私有属性和方法,它们不能从类外部直接访问,只能通过类的实例方法来访问。创建实例是为了能够在类的内部逻辑控制下访问这些私有成员。

6. 代码组织:

  • 模块化:通过类,我们可以将代码组织成模块化的单元,每个类负责一部分功能。创建实例是为了使用这些功能单元。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值