是更好的做法是在类中预初始化属性,还是在过程中添加它们?


11

很抱歉,这是一个绝对如此愚蠢的问题,但是我很好奇目前存在的最佳做法,而且我似乎在Google上找不到很好的答案。

在Python中,我通常使用一个空类作为超级类数据结构容器(有点像JSON文件),并在此过程中添加属性:

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

这给了我极大的灵活性,因为容器对象实际上可以存储任何东西。因此,如果出现了新的需求,我将其添加为DataObj对象的另一个属性(我在代码中传递了该属性)。

但是,最近(FP程序员)给我留下了深刻的印象,那就是这是一种糟糕的做法,因为这使得很难阅读代码。必须仔细阅读所有代码,以找出DataObj实际具有的属性。

问题:如何在不牺牲灵活性的情况下重写此代码,以实现更大的可维护性?

我可以采纳函数式编程中的任何想法吗?

我正在寻找最佳实践。

注意:一种想法是使用一个期望遇到的所有属性来预先初始化该类,例如

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

这实际上是一个好主意吗?如果我不知道我的先验属性怎么办?


您的数据结构易变,以至于您似乎担心它们的可维护性。在您的大量空闲时间中,尝试阅读有关不可变数据模型的文章。它可能会完全改变您对数据进行推理的方式。
9000

@ 9000这样的文章重新说服了已经说服的人。在我看来,它更像是一种方法而不是原因(除非您觉得自己有这些特定需求,否则为什么列表并不能令人信服)。对我来说,这并不能说服某人在VB中更新发票,而必须不断制作其发票对象的新副本才有意义(添加付款,新发票对象;添加零件,新发票对象)。
保罗

Answers:


10

如何在不牺牲灵活性的情况下重写此代码,以提高可维护性?

你不知道 灵活性正是导致问题的原因。如果任何地方的任何代码都可能更改对象的属性,则可维护性已经成片。理想情况下,每个类都具有一组固定的属性,__init__每个实例都相同。并非总是可能或明智的,但是只要您没有充分的理由避免它,都应该这样做。

一种想法是使用一个期望遇到的所有属性来预初始化该类

那不是一个好主意。当然,该属性在那里,但可能具有伪造的值,或者甚至是一个有效的值,它掩盖了未分配该值的代码(或拼写错误的值)。AttributeError是可怕的,但得到错误的结果会更糟。通常,默认值是可以的,但是要选择一个合理的默认值(并确定所需的值),您需要知道对象的用途。

如果我不知道我的先验属性怎么办?

然后,无论如何您都会感到困惑,应该使用字典或列表而不是对属性名称进行硬编码。但是我认为您的意思是“……在编写容器类时”。答案是:“您可以按步编辑文件,du。” 需要一个新属性?将Frigging属性添加到容器类。使用该类的代码更多,并且不需要该属性吗?考虑将内容拆分为两个单独的类(使用mixins保持DRY),因此在有意义的情况下使其可选。

如果您担心编写重复的容器类:明智地应用元编程,或者collections.namedtuple在创建后不需要对成员进行变异的情况下使用(您的FP伙伴会很高兴)。


7

您可以始终使用Alex Martelli的Bunch类。在您的情况下:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

这样,至少对于读者来说清楚的是,数据只是一个愚蠢的数据存储,并且可以立即看到以什么名称存储了哪些值,因为所有这些都发生在一行中。

是的,有时候这样做确实是个好主意。


1

我可能会使用第二种方法,可能None用来指示无效数据。的确,如果以后添加属性,则很难读取/维护。但是,有关此类/对象的目的的更多信息将使您了解为什么第一个主意是错误的设计:您将在哪里拥有一个完全空的类而没有方法或默认数据? 您为什么不知道类具有什么属性?

可能processData可能是更好的方法(process_data按照蟒蛇命名约定),因为它作用于类。给定示例,看起来它可能会更好地作为数据结构(其中a dict可能就足够了)。

给定一个真实的例子,您可以考虑将问题带到CodeReview,在这里他们可以帮助重构代码。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.