Python设计指南:

5

我是一位经验丰富的开发者-多年来使用Delphi、C#和C++等语言从事了大量的重型工作。我一直非常遵守结构化编程、面向对象编程、松散耦合模块化设计等准则,因为我所用的所有语言都有内置的方式来强制执行这些概念-访问控制、静态类型、接口和抽象类支持等-我依靠这些来组织我的代码。

现在,我已经开始接触Python几个月了。我对其许多美妙的功能印象深刻,但我非常怀念内置约束以使代码模块化和组织化变得更加容易的方式。不幸的是,我见过很多用Python编写的“意大利面代码”,即使来自非常受人尊敬的来源。我不想挑选任何人,但我有几本由主要的Python专家撰写的书籍,其中的例子充满了设计(更好的说法是“反设计”),让我感到恐惧。 我认为,因为Python非常易于使用,所以很容易被滥用

当我编写Python代码时,我确实试图自律,但我发现需要大量额外的工作来实现,并且经常只能根据自己对设计的记忆设置和遵守约束,而没有任何语言的帮助。由于没有“编译时”检查,这使得情况变得更加困难-通常你要运行代码段才能发现设计缺陷。

因此,我正在寻找非常具体的信息:一些良好结构化的Python设计和设计技巧的示例,或更好的是一本书-如何最好地实现封装、间接性、非常松散耦合的设计等。

著名Python书籍作者的糟糕设计示例(带有混淆)

def populateList(self, selecteddisk=None):
selected = None ***#Bundling - coupling:*** 
self.listWidget.clear()
for disk in self.disks.inOrder():
item = QListWidgetItem(QString("%1 of %2/%3 (%L4)") \
.arg(disk.name).arg(disk.owner).arg(disk.country) \
.arg(disk.teu))
self.listWidget.addItem(item)
***#Bundling - coupling:*** 
if selecteddisk is not None and selecteddisk == id(disk):
    selected = item
    if selected is not None:
    selected.setSelected(True)
    self.listWidget.setCurrentItem(selected)

8
请提供一些你认为是“反设计”的例子,这将有助于更好地理解。 - Cat Plus Plus
4
一般来说,我认为访问控制是无意义的,接口/抽象类也是不必要的(当你针对概念/操作编写程序时,代码更加通用,而不是针对具体接口——这在C++中也是正确的,其中模板是一种编译时鸭子类型)。尽管如此,Python中可以实现接口和抽象基类——例如zope.interface或标准库中的abc模块。 - Cat Plus Plus
2
也许广泛使用单元测试可以帮助你——它们在Python中比静态类型语言更重要。 - Sven Marnach
Python是一种灵活适应性强的编程语言。你不会在它身上找到其他语言中常见的限制,这是它的优势之一。 - g.d.d.c
我认为大多数Python用户将设计模式视为从代码中产生的东西,而不是注入其中的东西。 - Wilduck
显示剩余2条评论
2个回答

2

我并不认为“设计模式是语言支持的约束条件”,在我看来,这种想法相当愚蠢。我只是指一些编程语言提供了工具,使得执行设计模式更加容易。 - Vector
@Mikey:“强制使用设计模式”?这是什么意思?你能举个例子吗? - S.Lott
“设计模式”是一个含糊的术语。我只是指界面/实现;抽象/具体;public:data GetData(join(doStuff; doMoreStuff, doStillMoreStuff))/ private:somedata doStuff();someMoreData doMoreStuff();stillMoreData doStillMoreStuff()... - Vector

2

我发现,要实现和编译遵循约束的代码需要额外的工作量,仅依靠自己对设计的记忆而没有语言的帮助在编写代码本身之前。一些IDE提供帮助,但是语言本身并没有提供任何帮助。

而且,由于“编译时”检查似乎从来没有帮助过我找到普通逻辑错误,所以这是双倍困难的 - 通常你直到运行代码段才发现设计缺陷。

Python的设计和设计技术... 如何最好地实现封装,

通过封装。在像Java和C++这样的语言中,“封装”已经演变成“到处使用私有内容”。在Python中,这根本不被支持。

我们都是成年人。

你仍然可以像在其他语言中一样进行封装。但是,没有关键字private

Python提供了属性、装饰器和重写__getattribute__来实现各种封装技术。

间接性,

通过引用其他对象。我不清楚您在这里具体有什么问题,但也许您已经将错误类型的参数传递给了函数。避免这种情况的方法是阅读您为自己编写的文档字符串。
通过进行依赖注入。同样,Python与其他语言一样,可以实现松散耦合的设计。
您应该深入研究并使用文档字符串。
您可能想要使用https://www.sphinx-doc.org来从您的文档字符串生成漂亮的文档。
您还可以使用Python内置的help()函数来阅读您编写代码时编写的文档字符串。

“到处使用私有内容”是不好的。这意味着通过接口公开消费者(包括您自己)需要的内容,并“隐藏”他们不需要的内容。为什么?因为这使代码更加健壮,易于修改和适应。 - Vector
1
一个好的接口不需要改变,即使实现细节会变化。封装意味着你可以将更改局部化,并对实现进行大规模修改,而不会破坏其余代码。当你拥有一个只有你控制的由 3 个文件组成的脚本时,这可能看起来不重要。但是在由团队维护的大型复杂应用程序中,这非常重要。你曾经试过修改五年前的"意面代码(spaghetti code)"而所有原始开发人员已经离开了吗?你曾经交付过一个大型应用程序,然后得知客户想要迁移到新的数据库系统或操作系统吗? - Vector
@Mikey:“一个好的接口永远不需要改变,即使实现细节可能会改变。”这是一个好的、写得好的普遍真理。良好的设计不依赖于“private”,而是依赖于将接口与实现分离的良好设计。你在这个评论中说得很对。Python 对此提供了很少的语言支持。你有属性和描述符。但你仍然必须做这种良好的设计。无论是在 Python、Java 还是 C++ 中。 - S.Lott
“子类化” - 谁在谈论子类化?我几乎不使用传统的继承。接口 - Vector
我转向了Go语言。远比其他语言优秀。 - Vector
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接