面向对象练习题

随机数生成类

要求:

  • 可指定生成数字的个数、生成的数值范围;
  • 运行时还可以调整每批生成数字的个数。

方法1

import random

class NumberGenerator:
    def __init__(self, num_count=1, range_min=0, range_max=100):
        self.num_count = num_count
        self.range_min = range_min
        self.range_max = range_max

    def generate_numbers(self):
        return [random.randint(self.range_min, self.range_max) for _ in range(self.num_count)]

    def set_num_count(self, num_count):
        self.num_count = num_count

    def set_range(self, range_min, range_max):
        self.range_min = range_min
        self.range_max = range_max

## 示例用法
## 创建一个生成器对象
generator = NumberGenerator()

## 生成默认数量的数字
print("Generated numbers:", generator.generate_numbers())

## 设置新的数字个数
generator.set_num_count(3)

## 生成新的数字
print("Generated numbers:", generator.generate_numbers())

## 设置新的数字范围
generator.set_range(10, 50)

## 生成新的数字
print("Generated numbers:", generator.generate_numbers())

这个类名为 NumberGenerator,你可以根据需要初始化时指定生成的数字个数和范围,也可以随时调整生成的数字个数。使用 generate_numbers 方法可以生成指定数量的随机整数列表,使用 set_num_countset_range 方法可以在运行时修改生成的数字个数和范围。

方法2(推荐)

普通类实现,这种实现适合用每一个实例来保存各自的数值范围,互不干扰。

import random
class RandomGen:
    def __init__(self, start=1, end=100, count=10):
        self.start = start
        self.end = end
        self.count = count
    def generate(self):
        return [random.randint(self.start, self.end) for _ in range(self.count)]

rg1 = RandomGen()
print(rg1.generate())

rg2 = RandomGen(1, 10000, 3)
print(rg2.generate())

方法3(推荐)

作为工具类来实现,提供类方法。不需要记录用过的值,只需要调用一次给结果就好。

import random
class RandomGen:
    @classmethod
    def generate(cls, start=1, end=10, count=10):
        return [random.randint(start, end) for _ in range(count)]

print(RandomGen.generate())
print(RandomGen.generate(1, 10000, 3))

用类的实例调用(则无需在类方法前使用classmethod装饰器):

import random
class RandomGen:
    def generate(cls, start=1, end=10, count=10):
        return [random.randint(start, end) for _ in range(count)]

print(RandomGen().generate())
print(RandomGen().generate(1, 10000, 3))

方法4

使用生成器表达式

不立刻返回结果,而是返回生成器对象。

import random
class RandomGen:
    def __init__(self, start=1, end=100, count=10):
        self.start = start
        self.end = end
        self.count = count
    def generate(self):
        # 生成器表达式,返回一个生成器对象,该生成器对象可以逐个生成指定数量的随机整数。
        return (random.randint(self.start, self.end) for _ in range(self.count))

r = RandomGen()
print(r.generate()) # <generator object RandomGen.generate.<locals>.<genexpr> at 0x000002A15DBBFA00>,生成器对象的内存地址
print(*r.generate()) # 46 46 37 93 81 90 92 72 70 13,通过解构运算符* 迭代打印生成器中的每个值
print(list(r.generate())) # [13, 37, 15, 66, 77, 33, 56, 5, 97, 96],将生成器转换为列表并打印

方法5

使用 yield 将普通函数转换为生成器函数

import random
class RandomGen:
    def __init__(self, start=1, end=100, count=10):
        self.start = start
        self.end = end
        self.count = count
    def _generate(self): # 生成器函数
        while True: # 一次生成一个数据
            yield random.randint(self.start, self.end)
    def generate(self):
        return [next(self._generate()) for _ in range(self.count)]

r = RandomGen()
print(r.generate()) # [51, 97, 90, 68, 41, 64, 78, 37, 73, 72]

改进版

import random
class RandomGen:
    def __init__(self, start=1, end=100, count=10):
        self.start = start
        self.end = end
        self.count = count
        self._gen = self._generate()
    def _generate(self): # 生成器函数
        while True: # 一次生成一个数据
            yield random.randint(self.start, self.end)
    def generate(self):
        return [next(self._gen) for _ in range(self.count)]

r = RandomGen()
print(r.generate()) # [92, 71, 8, 23, 37, 3, 98, 88, 19, 17]

PS

这两个版本的主要区别在于如何生成随机数序列。

  • 在第一个版本中,每次调用 generate() 方法时都会创建一个新的生成器对象;
  • 而在第二个版本中,生成器对象 _gen 在初始化时就被创建,并在后续调用 generate() 方法时重复使用。

推荐使用第二种方式,因为它更加高效。在第二个版本中,生成器对象 _gen 只创建了一次,然后被重复利用,避免了每次调用 generate() 方法时都重新创建生成器对象的开销。这样可以节省内存和计算资源,并且提高代码的性能。

方法6

较为复杂的实现方式

import random

class RandomGen:
    def __init__(self, start=1, stop=100, count=10):
        self.start = start
        self.stop = stop
        self._count = count
        self._gen = self._generate()

    def _generate(self):
        while True:
            yield [random.randint(self.start, self.stop) for _ in range(self._count)]

    def generate(self):
        return next(self._gen)

    @property
    def count(self):
        return self._count

    @count.setter
    def count(self, value):
        self._count = value

r = RandomGen()

print(r.generate())
print(r.generate())

print(r.count)
r.count = 30
print(r.generate())

**while True:**的意义

在这段代码中,while True: 的意义在于创建了一个无限循环,这是为了确保生成器 (_generate 方法) 能够持续地产生随机数列表。生成器的主要目的是生成随机数列表,而无限循环保证了生成器可以在需要时一直生成列表,而不会停止。

这里的 while True: 是必要的,如果没有 while True:,生成器将只生成一个随机数列表,然后在 generate 方法被调用后停止。而通过使用 while True:,生成器可以持续生成随机数列表,直到调用方明确要求停止,而不会在生成一个列表后就停止。

import random

class RandomGen:
    def __init__(self, start=1, stop=100, count=10):
        self.start = start
        self.stop = stop
        self._count = count
        self._gen = self._generate()

    def _generate(self):
        #while True:
            yield [random.randint(self.start, self.stop) for _ in range(self._count)]

    def generate(self):
        return next(self._gen)

r = RandomGen()

print(r.generate())
print(r.generate()) # StopIteration,不加 while True 将停止迭代。

打印二维坐标

使用上题中的类,随机生成20个数字,两两配对形成二维坐标系的坐标,把这些坐标组织起来,并打印输出。

方法1

import random

class RandomGen:
    def __init__(self, start=1, stop=100, count=10):
        self.start = start
        self.stop = stop
        self._count = count
        self._gen = self._generate()

    def _generate(self):
        while True:
            yield [random.randint(self.start, self.stop) for _ in range(self._count)]

    def generate(self):
        return next(self._gen)

    @property
    def count(self):
        return self._count

    @count.setter
    def count(self, value):
        self._count = value

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

r = RandomGen()

points = [Point(x, y) for x, y in zip(r.generate(), r.generate())]

for p in points:
    print("{:2} {:2}".format(p.x, p.y))

points = [Point(x, y) for x, y in zip(r.generate(), r.generate())] 含义:

这段代码创建了一个名为 points 的列表,其中包含了由 RandomGen 类生成的随机坐标点。让我逐步解释:

  1. RandomGen 类定义了一个名为 generate 的方法,用于生成指定范围内的随机整数列表。这个方法有三个参数:start(起始值,默认为1),end(结束值,默认为10),以及 count(生成的随机数的数量,默认为10)。它返回一个包含随机整数的列表。

  2. Point 类定义了一个点的对象,它有两个属性 xy 分别代表横纵坐标。

  3. 在实例化 RandomGen 类之后,使用其 generate 方法两次来生成两个长度相同的随机整数列表,分别用来表示 x 和 y 坐标。

  4. zip(r.generate(), r.generate()) 这一步将两个随机数列表按顺序一一配对,形成一个包含元组的迭代器。每个元组中的第一个元素来自第一个随机数列表,第二个元素来自第二个随机数列表。

  5. 列表推导式 [Point(x, y) for x, y in zip(r.generate(), r.generate())] 遍历了这个元组迭代器,对于每个元组中的 x 和 y 坐标,创建一个 Point 对象,并将其添加到 points 列表中。

所以,points 列表中的每个元素都是一个 Point 对象,表示一个随机生成的坐标点,其 x 和 y 坐标是随机生成的。

最后使用 for 循环遍历了 points 列表中的每个元素,即 Point 对象。对于每个 Point 对象,它打印出其 xy 属性的值,即打印出该点的横纵坐标。

所以,这段代码的执行结果会逐行打印出每个点的横纵坐标。

方法2

使用 repr 魔术方法

import random
class RandomGen:
    def generate(cls, start=1, end=10, count=10):
        return [random.randint(start, end) for _ in range(count)]

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

    def __repr__(self):
        return f"({self.x}, {self.y})"

r = RandomGen()

points = [Point(x, y) for x, y in zip(r.generate(), r.generate())]
print(points)

方法3

使用 namedtuple

想要简单可以使用这种方式

import random
from collections import namedtuple

class RandomGen:
    def generate(cls, start=1, end=10, count=10):
        return [random.randint(start, end) for _ in range(count)]

r = RandomGen()

Point = namedtuple('Point', ['x', 'y'])
points = [Point(x, y) for x, y in zip(r.generate(), r.generate())]
print(points)

方法4

使用 lambda 表达式

import random
from collections import namedtuple

class RandomGen:
    def generate(cls, start=1, end=10, count=10):
        return [random.randint(start, end) for _ in range(count)]

r = RandomGen()

Point = namedtuple('Point', ['x', 'y'])
points = map(lambda x, y: Point(x, y), r.generate(), r.generate())
print(*points)

车辆信息

记录车的品牌brand、颜色color、价格price、速度speed等特征,并实现车辆管理,能增加车辆、显示车辆的全部信息

方法1

class Car:
    def __init__(self, brand, color, price, speed):
        self.brand = brand
        self.color = color
        self.price = price
        self.speed = speed

class CarManager:
    def __init__(self):
        self.cars = []

    def add_car(self, car):
        self.cars.append(car)

    def display_all_cars(self):
        for idx, car in enumerate(self.cars, start=1):
            print(f"Car {idx}:")
            print(f"Brand: {car.brand}")
            print(f"Color: {car.color}")
            print(f"Price: {car.price}")
            print(f"Speed: {car.speed}")
            print()

## 示例用法
car_manager = CarManager()

## 添加车辆
car1 = Car("Toyota", "Red", 25000, 180)
car_manager.add_car(car1)

car2 = Car("Honda", "Blue", 30000, 200)
car_manager.add_car(car2)

## 显示所有车辆信息
car_manager.display_all_cars()

{car.brand}是怎么拿到的?

在这段代码中,carCar类的一个实例对象,而不是列表。当你调用add_car方法将Car对象添加到CarManagercars列表中时,实际上你是将Car对象的引用添加到了列表中,而不是直接将对象本身放入列表。

所以,当你在display_all_cars方法中遍历self.cars列表时,每次迭代得到的car都是Car类的一个实例对象。因此,{car.brand}就是访问该实例对象的brand属性,以显示该汽车的品牌信息。

方法2

class Car:
    def __init__(self, brand, color, price, speed):
        self.brand = brand
        self.color = color
        self.price = price
        self.speed = speed

    def __repr__(self): # 不加这个返回的将是默认的、难以理解的实例对象信息
        # return str(sorted(self.__dict__.items()))
        # return "<Car brand={}, color={}, price={}, speed={}>".format(
        #     self.brand, self.color, self.price, self.speed
        # )
        return str(self.__dict__)

class CarInfoMgr:
    def __init__(self):
        self.__car_list = []

    def add_car(self, car):
        self.__car_list.append(car) # 添加的是Car类的一个实例对象

    def get_all(self):
        return self.__car_list

mgr = CarInfoMgr() # 实例化后将会生成一个列表(self.__car_list = []),可以容纳其它车辆

car1 = Car("Audi", "red", 20, 200)
car2 = Car("BMW", "blue", 30, 300)
mgr.add_car(car1)
mgr.add_car(car2)

print(*mgr.get_all(), sep='\n')

温度单位转换

摄氏度、华氏度、开氏度之间互相转换。

方法1

通过工具类实现(工具类不需要实例化)

class TemperatureConvert:
    @classmethod
    def c2f(cls, c):
        return c * 9 / 5 + 32

    @classmethod
    def f2c(cls, f):
        return (f - 32) * 5 / 9

    @classmethod
    def c2k(cls, c):
        return c + 273.15

    @classmethod
    def k2c(cls, k):
        return k - 273.15

    @classmethod
    def f2k(cls, f):
        return cls.c2k(cls.f2c(f))

    @classmethod
    def k2f(cls, k):
        return cls.c2f(cls.k2c(k))

print(TemperatureConvert.c2f(30)) # 86.0
print(TemperatureConvert.f2c(86)) # 30.0

print(TemperatureConvert.c2k(30)) # 303.15
print(TemperatureConvert.k2c(303.15)) # 30.0

print(TemperatureConvert.f2k(86)) # 303.15
print(TemperatureConvert.k2f(303.15)) # 86.0

新需求

一个温度和单位,先存着,以后不一定什么时候需要将该温度转换成其它单位的温度。

方案1

下面的实现方式,既可以当工具类,又可以当实例类~

class TemperatureConvert:
    def __init__(self, t, unit='c'):
        if unit == 'c':
            self._c = t
            self._f = self.c2f(t)
            self._k = self.c2k(t)
        elif unit == 'f':
            self._f = t
            self._c = self.f2c(t)
            self._k = self.f2k(t)
        elif unit == 'k':
            self._k = t
            self._c = self.k2c(t)
            self._f = self.k2f(t)

    @property
    def c(self):
        return self._c

    @property
    def f(self):
        return self._f

    @property
    def k(self):
        return self._k

    @classmethod
    def c2f(cls, c):
        return c * 9 / 5 + 32

    @classmethod
    def f2c(cls, f):
        return (f - 32) * 5 / 9

    @classmethod
    def c2k(cls, c):
        return c + 273.15

    @classmethod
    def k2c(cls, k):
        return k - 273.15

    @classmethod
    def f2k(cls, f):
        return cls.c2k(cls.f2c(f)) # 核心,最常用的温度体系是摄氏度

    @classmethod
    def k2f(cls, k):
        return cls.c2f(cls.k2c(k))

t = TemperatureConvert(30, 'c')
print(t.c)
print(t.f)
print(t.k)
print('-' * 30)
t = TemperatureConvert(86, 'f')
print(t.c)
print(t.f)
print(t.k)
print('-' * 30)
t = TemperatureConvert(303.15, 'k')
print(t.c)
print(t.f)
print(t.k)

方案2

class TemperatureConvert:
    def __init__(self, t, unit='c'):
        self._c = None
        self._t = None
        self._k = None
        if unit == 'c':
            self._c = t
        elif unit == 'f':
            self._f = t
            self._c = self.f2c(t)
        elif unit == 'k':
            self._k = t
            self._c = self.k2c(t)

    @property
    def c(self):
        return self._c

    @property
    def f(self):
        if self._f is None:
            self._f = self.c2f(self._c)
        return self._f

    @property
    def k(self):
        if self._k is None:
            self._k = self.c2k(self._c)
        return self._k

    @classmethod
    def c2f(cls, c):
        return c * 9 / 5 + 32

    @classmethod
    def f2c(cls, f):
        return (f - 32) * 5 / 9

    @classmethod
    def c2k(cls, c):
        return c + 273.15

    @classmethod
    def k2c(cls, k):
        return k - 273.15

    @classmethod
    def f2k(cls, f):
        return cls.c2k(cls.f2c(f))

    @classmethod
    def k2f(cls, k):
        return cls.c2f(cls.k2c(k))

两个方案对比

两种方案都能实现温度单位之间的转换,但它们的实现方式略有不同。

方案1:

  • 使用了属性(property)来延迟计算,只有在需要时才计算对应的温度值。
  • 使用了单个属性来表示每种温度单位的值,当一个单位的值被设置时,其他单位的值会被自动计算。
  • 在初始化时,只需要提供一个温度值和其对应的单位即可,其他单位的值会在需要时自动计算。

方案2:

  • 在初始化时,根据提供的单位,直接计算并设置所有单位的值。
  • 不使用属性延迟计算,直接计算并存储所有单位的值。

哪种方案更好取决于具体的使用场景和偏好:

  • 如果需要频繁地访问不同单位的温度值,且性能要求不高,可以选择方案1,因为它会在需要时计算,并且能够节省内存空间。
  • 如果性能要求较高,或者温度转换是在初始化后不再改变的情况下,可以选择方案2,因为它会在初始化时一次性计算并存储所有单位的值,避免了在访问时的计算开销。

总的来说,方案1更灵活和节省内存,适用于需要频繁访问不同单位温度值的场景;而方案2更简洁直接,适用于性能要求较高或者温度转换不频繁的场景。

模拟购物车购物

商品有很多种类,商品的属性多种多样,怎么解决?

购物车可以加入很多不同的商品,如何实现?

方法1

class Color:
    RED = 0
    BLUE = 1
    BLACK = 2

class Item: # 不应该设计所有的商品类,应该像这样统一设计。
    def __init__(self, name, price, **kwargs):
        self.name = name
        self.price = price
        self.__spec = kwargs

    def __repr__(self):
        return f'{self.name} - {self.price} - {self.__spec}'

class Cart:
    def __init__(self):
        self.items = []

    def add(self, item: Item):
        self.items.append(item)

    def getall(self):
        return self.items

my_cart = Cart()
my_item = Item('p30', 3000, color=Color.RED, memory='8g')
my_cart.add(my_item)
print(my_cart.getall())

方法2

要解决商品种类繁多、属性多样的问题,可以采用面向对象的方法,定义商品类(Product class),每个商品对象有各自的属性(如名称、价格、库存等),然后将不同种类的商品实例化为不同的对象。

至于购物车的实现,可以创建一个购物车类(ShoppingCart class),该类可以包含一些方法来添加商品、移除商品、计算总价等。

以下是一个简单的 Python 实现:

class Product:
    def __init__(self, name, price, quantity=1):
        self.name = name
        self.price = price
        self.quantity = quantity

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add_item(self, product):
        self.items.append(product)

    def remove_item(self, product):
        self.items.remove(product)

    def calculate_total(self):
        total = 0
        for item in self.items:
            total += item.price * item.quantity
        return total

## 测试代码
## 创建一些商品
product1 = Product("手机", 2000)
product2 = Product("电视", 3000)
product3 = Product("电脑", 4000)

## 创建购物车
cart = ShoppingCart()

## 添加商品到购物车
cart.add_item(product1)
cart.add_item(product2)
cart.add_item(product3)

## 计算购物车总价
total_price = cart.calculate_total()
print("总价:", total_price)

这个例子中,我们首先定义了一个 Product 类来表示商品,每个商品有名称、价格等属性。然后定义了 ShoppingCart 类来表示购物车,它包含一个列表用来存储购物车中的商品对象。购物车类中有添加商品、移除商品和计算总价等方法。

你可以根据自己的需求对这个实现进行扩展和修改,比如加入库存管理、优惠券功能等。

如果商品还有其它属性怎么办?

可以在 Product 类的 __init__() 方法中使用 **kwargs 参数来接收任意数量的关键字参数,并将它们存储在一个字典中。以下是一个示例:

class Product:
    def __init__(self, name, price, quantity=1, **kwargs):
        self.name = name
        self.price = price
        self.quantity = quantity
        self.attributes = kwargs  # 存储附加属性的字典

## 测试代码
product1 = Product("T恤", 20, color="红色", size="M")
product2 = Product("牛仔裤", 50, color="蓝色", size="L")
product3 = Product("运动鞋", 100, color="黑色", size="42")

## 打印商品的属性
print(product1.name, product1.price, product1.attributes)
print(product2.name, product2.price, product2.attributes)
print(product3.name, product3.price, product3.attributes)

在这个示例中,Product 类的 __init__() 方法使用 **kwargs 参数来接收任意数量的关键字参数,并将它们存储在 attributes 字典中。这样,你就可以给每个商品传递不同数量和类型的附加属性,而不需要事先定义它们。

计算图形面积

要求:Shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆。

方法1

  • 使用@property装饰器后,调用类方法时,就像掉类属性一样,更加方便;

  • 此方式仅适用于中途不修改属性值

import math

class Shape:
    def __init__(self):
        self._area = None

    @property
    def area(self):
        raise NotImplementedError('基类未实现')


class Triangle(Shape):
    def __init__(self, a, b, c):
        super().__init__() # 继承父类的 self._area
        self.a = a
        self.b = b
        self.c = c

    @property
    def area(self):
        # 不立刻生成面积的计算结果,以减少开销
        if self._area is None:
            p = (self.a + self.b + self.c) / 2
            self._area = math.sqrt(p * (p - self.a) * (p - self.b) * (p - self.c))
            print('*' * 30) # 测试是否二次计算
        return self._area


class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__()
        self.width = width
        self.height = height

    @property
    def area(self):
        if self._area is None:
            self._area = self.width * self.height
        return self._area


class Circle(Shape):
    def __init__(self, radius):
        super().__init__()
        self.r = radius

    @property
    def area(self):
        if self._area is None:
            self._area = 3.14 * self.r * self.r
        return self._area

shapes = [Triangle(3,4, 5),Rectangle(4,5),Circle(4)]

for shape in shapes:
    print(shape.area)
    print(shape.area)
t = Triangle(3, 4, 5)
print('-' * 100)
print(t.area)
print(t.area)

方法2

  • 此方式适用于中途修改属性值(修改属性值后,将 __area 设置为 None,以重新计算)
  • 下面仅使用 Triangle 类演示:
import math

class Shape:
    def __init__(self):
        self._area = None

    @property
    def area(self):
        raise NotImplementedError('基类未实现')


class Triangle(Shape):
    def __init__(self, a, b, c):
        super().__init__() # 继承父类的 self._area
        self.a = a
        self.b = b
        self.c = c

    # 私有属性只读
    @property
    def a(self):
        return self.__a

    @a.setter
    def a(self, value):
        self.__a = value
        self.__area = None

    @property
    def b(self):
        return self.__b

    @b.setter
    def b(self, value):
        self.__b = value
        self.__area = None

    @property
    def c(self):
        return self.__c

    @c.setter
    def c(self, value):
        self.__c = value
        self.__area = None

    @property
    def area(self):
        # 不立刻生成面积的计算结果,以减少开销
        if self._area is None:
            p = (self.a + self.b + self.c) / 2
            self._area = math.sqrt(p * (p - self.a) * (p - self.b) * (p - self.c))
            print('*' * 30) # 测试是否二次计算
        return self._area


class Rectangle(Shape):
    def __init__(self, width, height):
        super().__init__()
        self.width = width
        self.height = height

    @property
    def area(self):
        if self._area is None:
            self._area = self.width * self.height
        return self._area


class Circle(Shape):
    def __init__(self, radius):
        super().__init__()
        self.r = radius

    @property
    def area(self):
        if self._area is None:
            self._area = 3.14 * self.r * self.r
        return self._area


shapes = [Triangle(3,4, 5),Rectangle(4,5),Circle(4)]

for shape in shapes:
    print(shape.area)
    print(shape.area)
print('-' * 100)
t = Triangle(3, 4, 5)
print(t.area)
print(t.area)
print('=' * 30)
t.a = 2
print(t.area)
print(t.area)

实现圆类的数据可序列化

import math
import json
import msgpack

class Shape:
    def __init__(self):
        self._area = None

    @property
    def area(self):
        raise NotImplementedError('基类未实现')


class Circle(Shape):
    def __init__(self, radius):
        super().__init__()
        self.r = radius

    @property
    def area(self):
        if self._area is None:
            self._area = 3.14 * self.r * self.r
        return self._area


class SerializableMixin:
    def dumps(self, typ='json'):
        if typ == 'json':
            return json.dumps(self.__dict__)
        elif typ == 'msgpack':
            return msgpack.dumps(self.__dict__)
        else:
            raise TypeError


class SerializableCircleMixin(SerializableMixin, Circle): pass


sc = SerializableCircleMixin(3)
print(sc.dumps())
print(sc.dumps('msgpack'))
print()
print(sc.area)
print(sc.dumps())

这段代码展示了 Python 中 Mixin 的用法,Mixin 是一种将类的功能“混入”到其他类中的方式,以增加功能或重用代码而不引入继承链的复杂性。下面是对代码的详细解释:

  1. Shape 类是一个基类,定义了一个抽象的属性 area,但没有实现具体的计算方法。这意味着任何继承自 Shape 的子类都需要实现自己的 area 方法。

  2. Circle 类继承自 Shape 类。它具体实现了 area 方法,用于计算圆的面积。此外,它使用了一个 property 装饰器,将 area 方法转换为属性,使得可以通过 sc.area 的形式访问面积,而不需要加上括号。

  3. SerializableMixin 类是一个 Mixin 类,用于提供序列化的功能。它定义了一个 dumps 方法,根据指定的类型(‘json’ 或 ‘msgpack’),将对象转换为对应格式的字符串。

  4. SerializableCircleMixin 类继承自 SerializableMixinCircle。这意味着它继承了 SerializableMixin 提供的序列化功能,同时也继承了 Circle 类的计算面积的功能。

  5. 当创建 SerializableCircleMixin 的实例 sc 时,需要传入一个半径。然后可以使用 sc.dumps() 方法将对象序列化为 JSON 格式的字符串,或者使用 sc.dumps('msgpack') 方法将对象序列化为 MessagePack 格式的字符串。

  6. Circle 类中,计算出的面积会被缓存到 _area 属性中,避免重复计算。因此,当调用 sc.area 时,如果 _area 已经有值,就直接返回缓存的结果。

  7. 在最后一行代码中,再次调用了 sc.dumps() 方法,由于对象的属性并没有发生改变,所以序列化的结果与之前是一样的。

总的来说,Mixin 提供了一种方便的方式,可以将不同类中的功能组合在一起,而不需要使用传统的多重继承,从而避免了继承链过长和复杂性增加的问题。

实现单双向链表

要求

用面向对象的方式实现链表 LinkedList

  • 单向链表实现 appenditernodes 方法
  • 双向链表实现 appendpopinsertremoveiternodes 方法

PS:

单向链表:

  • 单向链表是一种基本的数据结构,它由一系列节点组成,每个节点包含两部分:数据和指向下一个节点的指针。每个节点只能访问其后继节点,而不能直接访问前驱节点或者其他节点。链表的第一个节点称为头节点,最后一个节点的指针指向一个特殊的值(通常是空指针或者NIL),表示链表的结束。
  • 单向链表的优点之一是在插入和删除操作时具有较高的效率,因为只需调整节点的指针,而不需要像数组那样移动大量元素。然而,缺点是访问元素时需要从头节点开始逐个遍历,因此访问效率较低,尤其是在需要随机访问元素时。

双向链表:

  • 双向链表是一种链表数据结构,它与单向链表相似,但每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点。这使得在双向链表中,每个节点都可以从前向后或从后向前遍历。

  • 与单向链表相比,双向链表在某些操作上具有更高的灵活性和效率。例如,在双向链表中,可以更容易地从后向前遍历,而不需要重新遍历整个链表。另外,插入和删除操作可能更高效,因为不需要在访问之前找到前一个节点,可以直接通过前向指针访问。但是,双向链表占用的空间更多,因为每个节点需要存储额外的指针。

实现