17、编程学习:从函数到面向对象编程

从函数到面向对象编程入门

编程学习:从函数到面向对象编程

函数的定义与应用

在编程中,定义自己的函数是一项重要技能。函数能够减少代码的重复,帮助我们构建更模块化的程序。一个命名良好的函数,能让代码更易读、更易理解,无论是对自己还是其他处理代码的人来说都是如此。

我们可以为函数添加参数,使函数更加通用。在调用函数时,通过传递与参数对应的不同参数值,来控制函数的行为。函数调用可以使用位置参数和/或关键字参数。对于可选参数,我们可以为参数定义默认值,这样在调用函数时,如果没有提供该参数的值,Python 会使用默认值。

同时,我们还可以定义返回值的函数。这意味着我们可以使用函数来处理数据,并将结果返回给调用者。如果函数返回一个值,我们可以将其赋值给一个变量。此外,我们还可以将一个函数包装在一个参数周围,以处理并返回另一个函数的值。

例如,我们可以尝试不同的 lissajousPoint() 函数参数,或者添加新的曲线和直线,甚至尝试在三条曲线之间连接三条线,以实现变形三角形的效果。通过不断地实验,我们可以发现更多有趣的结果。

三角函数在编程中也有重要的应用。像 sin() cos() 这样的内置三角函数,可用于绘制圆形、螺旋线、椭圆、正弦波和利萨如曲线等。通过运用三角函数,我们能够生成一些令人惊叹的图案和动画,就像在一些屏幕保护程序中看到的那样。

面向对象编程基础

面向对象编程(OOP)处理的是被称为对象的数据结构。我们可以从类创建新对象,类就像是对象的模板,由一组相关的函数和变量组成。对于我们想要处理的每一类对象,都可以定义一个类,每个新对象都会自动采用该类中定义的特征。

面向对象编程结合了我们之前所学的所有知识,包括变量、条件语句、列表、字典和函数。它通过对现实世界对象进行建模,为我们提供了一种非常有效的程序组织方式。

我们可以使用类来建模具体的对象,如建筑物、人、猫和汽车;也可以用它们来建模更抽象的事物,如银行账户、个性和物理力。虽然类定义了一类对象的一般特征,但我们可以为每个创建的对象分配独特的属性,以区分它们。

以汽车类为例,一个 Car 类可能默认指定所有汽车都有四个轮子、一个挡风玻璃等。但像油漆颜色这样的特征,在不同的汽车之间可能会有所不同。当我们使用 Car 类创建一个新的汽车对象时,就可以选择颜色。这些特征被称为属性,在 Python 中,属性是属于类的变量。我们可以决定哪些属性有预定义的值(如四个轮子和挡风玻璃),哪些属性是在创建新汽车时分配的(如油漆颜色)。

属性
Car 类 颜色、发动机类型、型号
Car 对象 1 红色、电动、轿车
Car 对象 2 蓝色、汽油、轿车
Car 对象 3 橙色、柴油、皮卡

除了属性,类还可以包含方法。方法是属于类的函数,定义了类可以执行的操作或活动。例如,驾驶员通过转向、加速和制动来控制车辆,所以 Car 类可以包含执行这些操作的定义。

定义和使用 Amoeba 类

现在,让我们定义一个 Amoeba 类,它包含一组属性和方法,用于控制变形虫对象的外观和行为。我们将使用这个类来创建多个变形虫。

定义新类

在 Python 中,我们使用 class 关键字来定义一个类。类名可以随意命名,但与变量和函数名一样,只能使用字母数字和下划线字符。推荐的类命名约定是 UpperCamelCase ,即每个单词的首字母大写,从第一个单词开始。

以下是一个简单的 Amoeba 类定义:

class Amoeba(object):
    def __init__(self):
        print('amoeba initialized')

这里, __init__() 方法是一个特殊的方法,它在创建新的变形虫对象时会自动调用。这个方法用于设置属性并在对象创建时执行代码。

创建类的实例

要实例化一个变形虫,我们通过类名调用 Amoeba 类,并将其赋值给一个变量,就像调用一个返回值的函数一样。实例化就是创建一个新实例的意思,实例与对象是同义词。

class Amoeba(object):
    def __init__(self):
        print('amoeba initialized')
a1 = Amoeba()

当运行这段代码时,Python 会创建一个新的 Amoeba() 实例,并自动调用 __init__() 方法。此时,控制台会显示一条 amoeba initialized 消息。

为类添加属性

属性可以看作是属于对象的变量,它可以包含任何类型的数据,如数字、字符串、列表、字典,甚至其他对象。在 Amoeba 类中,我们可以添加三个属性来保存变形虫的 x 坐标、y 坐标和直径。

class Amoeba(object):
    def __init__(self, x, y, diameter):
        print('amoeba initialized')
        self.x = x
        self.y = y
        self.d = diameter
a1 = Amoeba(400, 200, 100)

__init__() 方法中, self 参数是必需的,并且总是第一个参数。它提供了对实例特定值的访问。例如,对于变形虫 a1 self.x 的值为 400。

访问属性

我们可以使用点符号来访问属性。对于 a1 实例,我们可以分别通过 a1.x a1.y a1.d 来访问 x、y 和 d 属性。

以下代码展示了如何使用这些属性来绘制一个代表变形虫 a1 的圆:

def setup():
    size(800, 400)
    frameRate(120)

def draw():
    background('#004477')
    # cell membrane
    fill(0x880099FF)
    stroke('#FFFFFF')
    strokeWeight(3)
    circle(a1.x, a1.y, a1.d)

在这个代码中,显示窗口的宽度为 800 像素,高度为 400 像素。较高的帧率 120 有助于使后续添加的变形虫摆动动画更加平滑。

添加具有默认值的属性

就像每辆新车出厂时油箱都是空的一样,我们可以为 Amoeba 类添加一个具有默认值的属性。例如,每个变形虫都有一个默认填充为红色的细胞核。

class Amoeba(object):
    def __init__(self, x, y, diameter):
        print('amoeba initialized')
        self.x = x
        self.y = y
        self.d = diameter
        self.nucleus = '#FF0000'

draw() 函数中,我们可以添加代码来绘制细胞核:

def draw():
    background('#004477')
    # nucleus
    fill(a1.nucleus)
    noStroke()
    circle(a1.x, a1.y, a1.d/2.5)
    # cell membrane
    fill(0x880099FF)
    stroke('#FFFFFF')
    strokeWeight(3)
    circle(a1.x, a1.y, a1.d)
修改属性值

很多属性的值会随着程序的运行而改变。我们可以直接通过实例使用点符号来修改属性的值。

# nucleus
a1.nucleus = '#00FF00'
fill(a1.nucleus)

这段代码将变形虫 a1 的细胞核颜色修改为绿色。

使用字典作为属性

属性可以包含任何类型的数据,我们可以使用字典属性来组合细胞核的属性。

class Amoeba(object):
    def __init__(self, x, y, diameter):
        print('amoeba initialized')
        self.x = x
        self.y = y
        self.d = diameter
        self.nucleus = {
          'fill': ['#FF0000', '#FF9900', '#FFFF00',
                   '#00FF00', '#0099FF'][int(random(5))],
          'x': self.d * random(-0.15, 0.15),
          'y': self.d * random(-0.15, 0.15),
          'd': self.d / random(2.5, 4)
        }

在这个字典中, fill 键对应一个随机选择的十六进制颜色值, x y 键对应随机生成的坐标值, d 键对应随机生成的直径值。这样,每个变形虫的细胞核外观都会有所不同。

graph TD;
    A[开始] --> B[定义 Amoeba 类];
    B --> C[创建 Amoeba 实例 a1];
    C --> D[为 a1 添加属性];
    D --> E[访问 a1 属性并绘制变形虫];
    E --> F[添加默认属性 nucleus];
    F --> G[修改 nucleus 属性值];
    G --> H[使用字典作为 nucleus 属性];
    H --> I[结束];

通过以上步骤,我们逐步学习了如何定义类、创建对象、添加和修改属性,以及使用字典来组织属性。这些知识为我们进一步实现变形虫模拟程序奠定了基础。在后续的开发中,我们可以继续为 Amoeba 类添加更多的属性和方法,实现变形虫的移动、碰撞检测等功能。

编程学习:从函数到面向对象编程

为 Amoeba 类添加方法

在前面的基础上,我们已经能够创建变形虫对象并设置其属性。接下来,我们将为 Amoeba 类添加方法,以实现变形虫的移动等功能。

方法是属于类的函数,它可以对对象的属性进行操作。例如,我们可以添加一个 move 方法,让变形虫在屏幕上移动。

class Amoeba(object):
    def __init__(self, x, y, diameter):
        print('amoeba initialized')
        self.x = x
        self.y = y
        self.d = diameter
        self.nucleus = {
          'fill': ['#FF0000', '#FF9900', '#FFFF00',
                   '#00FF00', '#0099FF'][int(random(5))],
          'x': self.d * random(-0.15, 0.15),
          'y': self.d * random(-0.15, 0.15),
          'd': self.d / random(2.5, 4)
        }

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

在上述代码中,我们定义了一个 move 方法,它接受两个参数 dx dy ,分别表示在 x 轴和 y 轴上的移动距离。在方法内部,我们通过修改 self.x self.y 的值来实现变形虫的移动。

要调用这个方法,我们可以在 draw 函数中添加以下代码:

def draw():
    background('#004477')
    a1.move(1, 1)  # 每次移动 1 个像素
    # nucleus
    fill(a1.nucleus['fill'])
    noStroke()
    circle(a1.x + a1.nucleus['x'], a1.y + a1.nucleus['y'], a1.nucleus['d'])
    # cell membrane
    fill(0x880099FF)
    stroke('#FFFFFF')
    strokeWeight(3)
    circle(a1.x, a1.y, a1.d)
实现多个变形虫的模拟

为了实现多个变形虫的模拟,我们可以创建多个 Amoeba 类的实例,并将它们存储在一个列表中。

amoebas = []
amoebas.append(Amoeba(400, 200, 100))
amoebas.append(Amoeba(600, 250, 200))

def draw():
    background('#004477')
    for amoeba in amoebas:
        amoeba.move(1, 1)
        # nucleus
        fill(amoeba.nucleus['fill'])
        noStroke()
        circle(amoeba.x + amoeba.nucleus['x'], amoeba.y + amoeba.nucleus['y'], amoeba.nucleus['d'])
        # cell membrane
        fill(0x880099FF)
        stroke('#FFFFFF')
        strokeWeight(3)
        circle(amoeba.x, amoeba.y, amoeba.d)

在上述代码中,我们创建了一个 amoebas 列表,并向其中添加了两个变形虫对象。在 draw 函数中,我们遍历这个列表,对每个变形虫对象调用 move 方法,并绘制它们的细胞核和细胞膜。

碰撞检测

为了防止变形虫相互穿过,我们需要实现碰撞检测功能。碰撞检测的基本思路是计算两个变形虫之间的距离,如果距离小于它们的半径之和,则认为发生了碰撞。

import math

def is_colliding(amoeba1, amoeba2):
    distance = math.sqrt((amoeba1.x - amoeba2.x) ** 2 + (amoeba1.y - amoeba2.y) ** 2)
    return distance < (amoeba1.d / 2 + amoeba2.d / 2)

def draw():
    background('#004477')
    for i in range(len(amoebas)):
        for j in range(i + 1, len(amoebas)):
            if is_colliding(amoebas[i], amoebas[j]):
                # 处理碰撞,例如反向移动
                amoebas[i].move(-1, -1)
                amoebas[j].move(-1, -1)
        amoebas[i].move(1, 1)
        # nucleus
        fill(amoebas[i].nucleus['fill'])
        noStroke()
        circle(amoebas[i].x + amoebas[i].nucleus['x'], amoebas[i].y + amoebas[i].nucleus['y'], amoebas[i].nucleus['d'])
        # cell membrane
        fill(0x880099FF)
        stroke('#FFFFFF')
        strokeWeight(3)
        circle(amoebas[i].x, amoebas[i].y, amoebas[i].d)

在上述代码中,我们定义了一个 is_colliding 函数,用于判断两个变形虫是否发生碰撞。在 draw 函数中,我们遍历所有变形虫对,检查它们是否发生碰撞。如果发生碰撞,我们让它们反向移动。

总结

通过以上步骤,我们完成了一个简单的变形虫模拟程序。在这个过程中,我们学习了如何定义类、创建对象、添加属性和方法,以及实现碰撞检测等功能。

以下是整个程序的流程图:

graph TD;
    A[开始] --> B[定义 Amoeba 类];
    B --> C[创建多个 Amoeba 实例并存储在列表中];
    C --> D[在 draw 函数中遍历列表];
    D --> E[检查变形虫之间的碰撞];
    E --> F{是否碰撞};
    F -- 是 --> G[处理碰撞,反向移动];
    F -- 否 --> H[正常移动变形虫];
    H --> I[绘制变形虫的细胞核和细胞膜];
    I --> D;
    D --> J[结束];

整个学习过程可以总结为以下步骤:
1. 学习函数的定义和使用,包括添加参数、返回值等。
2. 了解面向对象编程的基本概念,如类、对象、属性和方法。
3. 定义 Amoeba 类,创建变形虫对象并设置属性。
4. 为 Amoeba 类添加方法,实现变形虫的移动。
5. 创建多个变形虫对象,实现多个变形虫的模拟。
6. 实现碰撞检测功能,防止变形虫相互穿过。

通过不断实践和探索,我们可以进一步扩展这个程序,例如添加更多的属性和方法,实现更复杂的变形虫行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值