在这个 class 中我可以做得更好吗? Python class 重构

What I can do better in this class? Python class refactoring

我有一些 classes 和继承树的代码。我可以在此代码中做得更好吗?如果我们往设计方向看classes.

CAR_TYPES = {
    'Car': 'Car',
    'Truck': 'Truck',
    'SpecMachine': 'SpecMachine'
}


class CarBase:
    def __init__(self, brand, photo_file_name, carrying):
        self.car_type = None
        self.photo_file_name = photo_file_name
        self.brand = brand
        self.carrying = carrying

    def get_photo_file_ext(self):
        return self.photo_file_name.split(".")[-1]

    def __str__(self):
        return f"Car type: {self.car_type} | Brand: {self.brand} | Carrying: {self.carrying}"


class Truck(CarBase):
    def __init__(self, photo_file_name, brand, carrying, body_lwh):
        super().__init__(photo_file_name, brand, carrying)
        self.car_type = CAR_TYPES['Truck']
        self.body_lwh = body_lwh
        self.body_length = 0
        self.body_width = 0
        self.body_height = 0
        self.body_volume = 0

        if body_lwh:
            self._set_lwh()
            self._set_body_volume()

    def __str__(self):
        return f"{super().__str__()} | Length: {self.body_length} | Width: {self.body_width}, " \
           f"| Height {self.body_height}, | Volume: {self.body_volume}"

    def _set_lwh(self):
        try:
            self.body_length, self.body_width, self.body_height = map(float, self.body_lwh.split('x'))
        except ValueError:
            self.body_length, self.body_width, self.body_height = 0.0, 0.0, 0.0
            print("Value Error. Check your values and try again!")

    def _get_body_volume(self):
        return self.body_length * self.body_width * self.body_height

    def _set_body_volume(self):
        self.body_volume = self._get_body_volume()


class Car(CarBase):
    def __init__(self, photo_file_name, brand, carrying, passenger_seats_count):
        super().__init__(photo_file_name, brand, carrying)
        self.car_type = CAR_TYPES['Car']
        self.passenger_seats_count = passenger_seats_count

    def __str__(self):
        return f"{super().__str__()} | Passenger seats count: {self.passenger_seats_count}"


class SpecMachine(CarBase):
    def __init__(self, photo_file_name, brand, carrying, extra):
        super().__init__(photo_file_name, brand, carrying)
        self.car_type = CAR_TYPES['SpecMachine']
        self.extra = extra

    def __str__(self):
        return f"{super().__str__()} | Extra: {self.extra}"

我想让这段代码更具可读性和可扩展性,但我在这方面没有任何经验,我想学习它。

例如,我可以用 car_type 变量做什么?我试图将 car_type 放入 CarBase class,但我不知道,我以后如何分配它并从设计方面使其正确

很多事情取决于您的设计目标,因此我们需要知道您的汽车 classes 需要做什么,以便他们的客户对他们的设计发表更多评论,但我可以评论您的使用类型。

首先,class 名称 CarBase 表明它并不意味着要实例化,它只是您派生的 classes 的基础。如果这是您的设计意图,我建议您查看 python 的 abc.abstractclass,以便您可以将其抽象为 class.

其次,回答你关于 car_type 变量的问题:我会声称你根本不需要,甚至你不应该拥有它。通常,继承的目的是让您的客户不需要关心您的类型是什么,他们只需要知道您是某种汽车,并且您会处理这对您的特定类型的重要性。这就是所谓的多态性。

大多数情况下,通过抽象来提高可读性实际上会适得其反。

首先,你在这里做得很好。你 functions/methods 都很小——只有几行。这意味着更容易吸收该方法的作用,然后再返回其余代码。另外,您的名字非常具有描述性——他们不在名称中使用变量类型(好事!!)而且他们不使用首字母缩略词。您使用的唯一缩写是 lwh,这是......非常容易掌握。

看起来您正在使用 CAR_TYPE 的术语词典,如果这是一个完全独立的系统,那就太好了。如果这是其他人也将使用的代码,您可以改用 Litterals:

class CarType():
    MAZARATI = "Mazarati"
    FERRARI = "Ferrari"
    SPORTS_CAR = "Performance"

等等等等。如果您在 class 中像这样在 __init__ 构造函数之外定义 Litterals,则可以直接访问它们而无需实例化 class。这些成为模块常量,您可以像这样引用它们:

import CarType

if car_type == CarType.SPORTS_CAR:
    print("Zooooomy!")

这看起来更专业,而且 Intellisense 会选择这些并以任何体面的方式提供它们 IDE,而字典不会显示其可用的键。

总体而言 - 我认为您无需为重构担心太多。重构的主要思想是让你的代码 DRY - Don't Repeat Y我们自己——你几乎没有重复自己。重构的另一面是使代码更具可读性——作为一般规则,任何与您具有相同或更多经验(并且在某种程度上,更少!)的给定开发人员应该能够像阅读一本书一样简单地阅读您的代码(所以上面夸你的方法小,你的方法名和变量名其实是可读的,不要让我费解)

如果您的大部分内容只有 1 或 2 行,请不要太担心重构。正如我在这里的第一行所说,有一点重构太多实际上会使情况变得更糟。如果我必须跳到 10 种不同的方法来弄清楚发生了什么,那比其他任何事情都更有害(这也是为什么有些人认为你在另一个内部调用的方法应该总是在之后和下面定义的原因,所以您正在阅读 向下 页面,就像您阅读一本书一样。但这只是一种观点 - 有些人不同意并认为它应该相反 - 有时有点取决于语言)

总体而言 - 如果这作为合并请求出现在我的日程表上,与我的一位初级开发人员一起审查,我将非常高兴!

您可能会在未来的实施中尝试的一些东西:

  1. 任何默认设置为 None 的属性,您也可以使用 kwarg 默认设置。

    def init(self,brand,photo_file_name,携带,car_type=None): self.car_type = car_type

这样,当有人实例化汽车时,他们可以在实例化期间继续设置 car_type,而不必使用第二行。

  1. 如果你要传入超过 4 或 5 个属性,你可以考虑使用字典(如果这是内部的)或将它们中的一部分或全部转换为 kwargs,以便它们必须在实例化中被标记(car_base = CarBase(brand=A, photo_file_name=B.jpg, ...) 当您拥有多个属性时,这可以帮助解决问题。

干得好!坚持下去!