首页 > 解决方案 > 如何保持python项目模块化?

问题描述

语境

我最近一直在做一个 python 项目,发现模块化非常重要。例如,您创建了一个具有一些属性的类和一些使用这些属性的代码行,例如

a = A()
print("hi"+a.imA)

如果要将imA类修改A为另一种类型,则必须修改 print 语句。就我而言,我不得不这样做很多次。这很烦人而且很耗时。get/set 方法可以解决这个问题,但我听说 get/set 不是“好 python”。那么,如果不使用 get 和 set 方法,您将如何解决这个问题呢?

标签: python

解决方案


第一点:通过使用字符串格式而不是字符串连接,您会为自己节省很多麻烦,即:

print("hi {}".format(a.imA))

当然,最终结果可能会或不会是您所期望的,具体取决于a.imA类型的实现方式__str__()__repr__()但至少这不会破坏代码。

wrt/ getter 和 setter,它们确实被认为是非 Python 的,因为 Python 对计算属性有很强的支持,并且一个简单的通用实现可用作内置property类型

注意:实际上,当普通的公共属性足够时,系统地使用实现属性和 getter/setter(显式或 - 与计算属性一样 - 隐式)被认为是非pythonic的,这被认为是非pythonic,因为你总是可以打开在不破坏客户端代码的情况下将普通属性转换为计算属性(当然假设您没有更改属性的类型或语义)——这在 Smalltalk、C++ 或 Java 等早期 OOPL 中是不可能的(Smalltalk 有点实际上是特殊情况,但这是另一个话题)。

在您的情况下,如果重点是在不破坏 API 的情况下更改存储值的类型,那么简单明显的规范解决方案是使用property委托给实现属性:

前:

class Foo(object):
    def __init__(self, bar):
        # `bar`  is expected to be the string representation of an int.
        self.bar = bar

    def frobnicate(self, val):
        return (int(self.bar) + val) / 2

后:

class Foo(object):
    def __init__(self, bar):
        # `bar`  is expected to be the string representation of an int.
        self.bar = bar

    # but we want to store it as an int
    @property
    def bar(self):
        return str(self._bar)

    @bar.setter
    def bar(self, value):
        self._bar = int(value)

    def frobnicate(self, val):
        # internally we use the implementation attribute `_bar`
        return (self._bar + val) / 2

而且您现在将值在内部存储为 int,但公共接口(几乎)完全相同 - 唯一的区别是传递无法传递给的int()内容将在预期的位置(当您设置它时)而不是在最意想不到的时候打破(当你打电话时.frobnicate()

现在请注意,更改公共属性的类型就像更改 getter 的返回类型(或 setter 参数的类型)一样——在这两种情况下你违反了合同——所以如果你真正想要的是改变type of A.imA,getter 和 properties 都不会解决您的问题 - getter 和 setter(或在 Python 计算属性中)只能保护您免受实现更改的影响。

编辑:哦,是的:这与模块化无关(这是关于编写更易于阅读、测试、维护和最终重用的解耦、独立的代码),但与封装(其目的是使公共接口具有弹性)无关实施变更)。


推荐阅读