Python:如何将类的功能分成多个文件?

14 python file object decorator

我知道这已被问过几次,但我不能完全理解以前的答案和/或我认为解决方案并不能代表我正在拍摄的内容.我仍然是Python的新手,所以我很难搞清楚这一点.

我有一个主类,它有一个不同功能的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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

obj_1 = MainClass()

我希望能够做到:

obj_1.long_func_1(a, b)

代替:

separate.long_func_1(obj_1, a, b)

我知道这似乎有点挑剔,但我想要开始的所有代码,obj_1.所以没有混乱.

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

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

kab*_*nus 22

我真的很惊讶这不是重复.我看到了一些类似的问题,我认为没有一个简洁的答案,所以这就是我如何做到的:

  1. 类(或组)实际上是一个完整的模块(你不必这样做,但如果你在多个文件上拆分一个类,我认为这是'最干净'(意见).
  2. 定义在__init__.py,方法被分成具有有意义分组的文件.
  3. 方法文件只是一个带有函数的常规python文件,除了你不能忘记'self'作为第一个参数.
  4. 方法是直接导入类定义的文件.

假设我的班级有些适合gui(这实际上是我第一次这样做的).所以我的文件层次结构可能看起来像

mymodule/
     __init__.py
     _plotstuff.py
     _fitstuff.py
     _datastuff.py
Run Code Online (Sandbox Code Playgroud)

因此,绘图方法将有绘图方法,拟合东西拟合方法,数据填充和数据处理 - 你明白了.按照惯例,我用a标记文件,self表明这些文件实际上并不是要直接导入模块外的任何地方.所以_例如可能看起来像:

def plot(self,x,y):
     #body
def clear(self):
     #body
Run Code Online (Sandbox Code Playgroud)

等等现在重要的是_plotsuff.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

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

     #I think static methods have to be here
     @staticmethod
     def something(argumentIsNotSelf):
         print('yay')
Run Code Online (Sandbox Code Playgroud)

请注意,__init__.py对于您不希望通过类的对象访问的方法隐藏一些"帮助"函数特别有用.我通常也会将类的自定义异常放在不同的文件中,但是直接导入它们以便可以将它们作为访问__init__.

如果此模块在您的路径中,那么您可以访问您的课程

from mymodule import Fitter
f = Fitter()
f.load('somefile') #Imported method
f.plot()           #Imported method
Run Code Online (Sandbox Code Playgroud)

不完全直观,但也不困难.针对您的特定问题的简短版本是您的关闭 - 只需将导入移动到类中,然后使用

from separate import long_func_1
Run Code Online (Sandbox Code Playgroud)

别忘了你from ... import ...!

  • 作为一般性评论,我想补充一点,有时将类拆分为子类是没有意义的,即使在 Python 中也可能会遇到重构长类代码的问题。 (2认同)
  • @Tian Yup,与任何方法相同。创建一个文件,然后从那里导入它(`from .init1 import __init__`,其中 init1.py 包含 `def __init__(self...):...`)等。下次我 syggest 自己尝试这些东西时(可能更快)。 (2认同)
  • 此外,如果工具(linter、IDE 等)抱怨或无法处理我们的明智选择,则问题出在工具上。软件架构应该由工具来服务,而不是相反。顺便说一句,Python 内置库本身有很多更好的选择示例,包括名称约定,比 PEP8“推荐”的库(它旨在用于语言开发,而不是作为其他所有内容的神圣书籍)。 (2认同)

Jef*_*ton 10

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

所以文件夹结构类似于:

_DataStore/
    __init__.py
    DataStore.py
    _DataStore.py

Run Code Online (Sandbox Code Playgroud)

所以你的基类将是:

# 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

Run Code Online (Sandbox Code Playgroud)

然后你的 Mixin 类:

# _DataStore.py

class Mixin:

    def big_method(self):
        return self._b

    def huge_method(self):
        return self._c
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我认为这是一个有效的方法。您还可以在 Mixin 类的 __init__ 中引发异常,以阻止用户实例化 Mixin 类。 (3认同)
  • 我用过mixin,可能太多了。这是一种减小文件大小的简单方法,但可以让类大小为任意大小。问题是您没有像继承那样重构,而只是创建一个大型类。无论是使用 mixin 还是猴子补丁,缺点是你的 linter 无法处理它,所以你必须以艰难的方式发现错误。由于这个原因,子类化是首选,更不用说更好的封装了,等等。 (2认同)

cow*_*ert 9

以下是Martijn Pieters 使用子类的注释的实现:

文件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
Run Code Online (Sandbox Code Playgroud)

文件分离.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.
Run Code Online (Sandbox Code Playgroud)

from main import MainClass
m = MainClass()
m.func_1(1, 2)
....
Run Code Online (Sandbox Code Playgroud)