【问题标题】:How can I separate the functions of a class into multiple files?如何将一个类的功能分成多个文件?
【发布时间】:2018-05-13 16:43:12
【问题描述】:

我有一个主类,其中有一个 ton 的不同功能。越来越难管理了。我希望能够将这些函数分离到一个单独的文件中,但我发现很难想出一个好的方法。

这是我到目前为止所做的:

文件 main.py

import separate

class MainClass(object):
    self.global_var_1 = ...
    self.global_var_2 = ...

    def func_1(self, x, y):
        ...
    def func_2(self, z):
        ...
    # tons of similar functions, and then the ones I moved out:

    def long_func_1(self, a, b):
        return separate.long_func_1(self, a, b)

文件 separate.py

def long_func_1(obj, a, b):
    if obj.global_var_1:
        ...
    obj.func_2(z)
    ...
    return ...
# Lots of other similar functions that use info from MainClass

我这样做是因为如果我这样做:

obj_1 = MainClass()

我希望能够做到:

obj_1.long_func_1(a, b)

代替:

separate.long_func_1(obj_1, a, b)

我知道这似乎有点挑剔,但我希望几乎所有代码都以 obj_1. 开头,所以不会混淆。

有没有比我目前正在做的更好的解决方案?我当前设置的唯一问题是:

  1. 我必须更改函数的两个实例的参数
  2. 这似乎是不必要的重复

我知道这个问题已经被问过好几次了,但我不太明白之前的答案和/或我认为该解决方案不能完全代表我的目标。我对 Python 还是很陌生,所以我很难弄清楚这一点。

【问题讨论】:

  • 如果您是 Python 新手,只需遵守约定,并将类的所有方法保存在同一个文件中。
  • 如果您必须将您的方法分组到单独的模块中,请使用继承;在一个模块中创建一个基类,将其导入并在另一个模块中对其进行子类化。
  • @MartijnPieters 我知道我可以做到这一点,但是类中的所有函数都没有最终确定,所以我发现自己滚动了很多以找到合适的函数,这比我想要的要花费更多的时间只是因为有这么多。
  • 那不是改代码就能解决的问题;这是一个可以通过使用an IDE 来解决的问题,它允许您跳转到函数的位置。 (或使用文本编辑器的“查找”功能。)
  • 如果一个文件不足以满足所有方法,那么您的设计可能有问题。该类太heavy 并且可能将其分成两个或三个类(和文件)是解决方案。

标签: python file object decorator


【解决方案1】:

这是我的做法:

  1. 类(或组)实际上是一个完整的模块。您不必这样做,但如果您将一个类拆分为多个文件,我认为这是“最干净的”(意见)。

  2. 定义在__init__.py,方法通过有意义的分组被分成文件。

  3. 方法文件只是一个带有函数的普通 Python 文件,除了你不能忘记'self' 作为第一个参数。这里可以有辅助方法,既取self,也取不取。

  4. 方法直接导入到类定义中。

假设我的课程是一些合适的 GUI(这实际上是我第一次这样做)。所以我的文件层次结构可能看起来像

mymodule/
     __init__.py
     _plotstuff.py
     _fitstuff.py
     _datastuff.py

所以 plot 东西会有绘图方法, fit 东西包含拟合方法,而 data 东西包含加载和处理数据的方法 - 你明白了。按照惯例,我用_ 标记这些文件,以表明这些文件并不意味着直接在模块外的任何地方导入。所以 _plotsuff.py 例如可能看起来像:

def plot(self,x,y):
     #body
def clear(self):
     #body

等等。现在重要的是文件__init__.py

class Fitter(object):
     def __init__(self,whatever):
         self.field1 = 0
         self.field2 = whatever

     # Imported methods
     from ._plotstuff import plot, clear
     from ._fitstuff  import fit
     from ._datastuff import load

     # static methods need to be set
     from ._static_example import something
     something = staticmethod(something)

     # Some more small functions
     def printHi(self):
         print("Hello world")

Tom Sawyer 提到 PEP-8 建议将所有导入放在顶部,因此您可能希望将它们放在 __init__ 之前,但我更喜欢这种方式。我不得不说,我的Flake8 检查器没有抱怨,所以这很可能是符合 PEP-8 的。

注意from ... import ... 对于隐藏一些你不想通过类的对象访问的方法的“帮助”函数特别有用。我通常也会将类的自定义异常放在不同的文件中,但直接导入它们,以便它们可以作为Fitter.myexception 访问。

如果这个模块在你的路径中,那么你可以访问你的类

from mymodule import Fitter
f = Fitter()
f.load('somefile') # Imported method
f.plot()           # Imported method

这不是完全直观,但也不是太难。您的具体问题的简短版本是您很接近 - 只需将导入移动到类中,然后使用

from separate import long_func_1

别忘了你的self

【讨论】:

  • @cowbert 在外面,见标签。我将添加一些示例代码以使其清楚。你只需要它与类一起编译一次,而不是每个对象。
  • 我想添加一个一般性评论,有时将一个类拆分为子类是没有意义的,即使在 Python 中也可能会遇到重构长类代码的问题。
  • 将“类”拆分为多个文件也是常见的 ECMAScript 模式(使用 var PseudoClass = PseudoClass || {} 成语),因此如果您正在使用 Python 中间件进行全栈开发,拆分 Python 可能是有意义的由于各种原因上课。
  • 在文件中间使用导入是不好的做法
  • @Tian Yup,和任何方法一样。创建一个文件,然后从那里导入它(from .init1 import __init__,其中 init1.py 包含def __init__(self...):...)等。下次我会自己尝试这些东西(可能更快)。
【解决方案2】:

这是Martijn Pieters's comment to use subclasses的一个实现:

文件 main.py

from separate import BaseClass

class MainClass(BaseClass):
    def long_func_1(self, a, b):
        if self.global_var_1:
            ...
        self.func_2(z)
        ...
        return ...
    # Lots of other similar functions that use info from BaseClass

文件 separate.py

class BaseClass(object):

    # You almost always want to initialize instance variables in the `__init__` method.
    def __init__(self):
        self.global_var_1 = ...
        self.global_var_2 = ...

    def func_1(self, x, y):
        ...
    def func_2(self, z):
        ...
    # tons of similar functions, and then the ones I moved out:
    #
    # Why are there "tons" of _similar_ functions?
    # Remember that functions can be defined to take a
    # variable number of/optional arguments, lists/tuples
    # as arguments, dicts as arguments, etc.

from main import MainClass
m = MainClass()
m.func_1(1, 2)
....

【讨论】:

    【解决方案3】:

    我使用我找到的方法here。它显示了许多不同的方法,但是如果您向下滚动到最后,首选方法基本上是与@Martin Pieter 的建议相反的方向,即拥有一个基类,该基类使用您在这些类中的方法继承其他类。

    所以文件夹结构是这样的:

    _DataStore/
        __init__.py
        DataStore.py
        _DataStore.py
    
    

    所以你的基类是:

    文件 DataStore.py

    import _DataStore
    
    class DataStore(_DataStore.Mixin): # Could inherit many more mixins
    
        def __init__(self):
            self._a = 1
            self._b = 2
            self._c = 3
    
        def small_method(self):
            return self._a
    

    然后是你的 Mixin 类:

    文件 _DataStore.py

    class Mixin:
    
        def big_method(self):
            return self._b
    
        def huge_method(self):
            return self._c
    

    您的单独方法将位于其他适当命名的文件中,在此示例中它只是 _DataStore。

    我很想听听其他人对这种方法的看法。我把它展示给工作中的人,他们被它吓到了,但这似乎是一种将一个类分成多个文件的干净而简单的方法。

    【讨论】:

    • 我认为这是一种有效的方法。您还可以在 Mixin 类的 __init__ 中引发异常,以阻止用户实例化 Mixin 类。
    • 我用过mixins,可能太多了。这是一种减小文件大小的简单方法,但可以让类大小为任意大小。问题是您并没有重构继承的方式,而只是创建了一个大型类。无论是使用 mixins 还是猴子补丁,缺点是你的 linter 无法处理它,所以你必须以艰难的方式找到错误。出于这一原因,子类化是首选,更不用说更好的封装等。
    猜你喜欢
    • 2021-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-16
    相关资源
    最近更新 更多