是否有任何Python等效于部分类?

时间:2012-03-09 17:30:43

标签: python partial-classes

使用“新”样式类(我在python 3.2中)有没有办法将类拆分为多个文件?我有一个大类(从面向对象的设计角度来看,它应该是一个单独的类,考虑耦合等等,但是为了便于编辑类,分割几个文件会很好。

9 个答案:

答案 0 :(得分:38)

如果您的问题确实只是在编辑器中使用大型类,那么我实际寻找的第一个解决方案是解决问题的更好方法。第二种解决方案是更好的编辑器,最好是具有代码折叠的编辑器。

也就是说,有几种方法可以将一个类分解为多个文件。 Python允许您通过在其中放置__init__.py将文件夹用作模块,然后可以从其他文件导入内容。我们将在每个解决方案中使用此功能。首先创建一个名为bigclass的文件夹。

  1. 在文件夹中放入最终构成您班级的各种.py文件。每个应包含最终类的函数和变量定义, not 类。在同一文件夹的__init__.py中,写下以下内容将它们连接在一起。

    class Bigclass(object):
    
        from classdef1 import foo, bar, baz, quux
        from classdef2 import thing1, thing2
        from classdef3 import magic, moremagic
        # unfortunately, "from classdefn import *" is an error or warning
    
        num = 42   # add more members here if you like
    

    这样做的好处是你最终会得到一个直接来自object的单个类,它在你的继承图中会很好看。

  2. 您可以使用多重继承来组合类的各个部分。在单个模块中,您将为类Bigclass编写类定义。然后在你的__init__.py写:

    import classdef1, classdef2, classdef3
    
    class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
        num = 42   # add more members if desired
    
  3. 如果多重继承成为问题,可以使用单一继承:只需让每个类以链方式从另一个继承。假设您没有在多个类中定义任何内容,则顺序无关紧要。例如,classdef2.py就像:

    import classdef1
    class Bigclass(classdef1.Bigclass):
         # more member defs here
    

    classdef3会从Bigclass导入classdef2并添加到其中,依此类推。您的__init__.py只会导入最后一个:

    from classdef42 import Bigclass
    
  4. 我通常更喜欢#1,因为它更明确地指出您从哪些文件导入的成员,但这些解决方案中的任何一个都可以为您服务。

    要在任何这些场景中使用该类,您只需使用文件夹名称作为模块名称导入它:from bigclass import Bigclass

答案 1 :(得分:7)

包含数百行的类定义确实“在野外”发生(我在流行的基于Python的开源框架中看到了一些),但我相信如果你思考这些方法正在做什么,它将有可能将大多数类的长度减少到可管理的点。一些例子:

  • 查找多次出现相同代码的地方。将该代码分解为自己的方法,并使用参数从每个地方调用它。
  • 不使用任何对象状态的“私有”方法可以作为独立函数从类中取出。
  • 仅在某些条件下应调用的方法可能表示需要将这些方法放在子类中。

要直接解决您的问题,可以拆分类的定义。一种方法是通过定义类,然后将外部函数作为方法添加到类中来“修补”类。另一种方法是使用内置的type函数“手动”创建类,在字典中提供其名称,任何基类及其方法和属性。但我不建议这样做只是因为定义会很长。在我看来,这种治疗比疾病更糟糕。

答案 2 :(得分:6)

我之前玩弄过类似的东西。我的用例是抽象语法树中节点的类层次结构,然后我想把所有节点都放在prettyprinting函数在一个单独的prettyprint.py文件中,但仍然将它们作为类中的方法。

我尝试过的一件事是使用装饰器将装饰函数作为属性放在指定的类上。就我而言,这意味着prettyprint.py包含许多def prettyprint(self),所有@inclass(...)都装饰有不同的def inclass(kls): """ Decorator that adds the decorated function as a method in specified class """ def _(func): setattr(kls,func.__name__, func) return func return _ ## exampe usage class C: def __init__(self, d): self.d = d # this would be in a separate file. @inclass(C) def meth(self, a): """Some method""" print "attribute: %s - argument: %s" % (self.d, a) i = C(10) print i.meth.__doc__ i.meth(20)

这个问题是必须确保总是导入子文件,并且它们依赖于主类,这会产生循环依赖,这可能很麻烦。

{{1}}

答案 3 :(得分:4)

您可以使用像这样的装饰器来执行此操作:

class Car(object):

    def start(self):
    print 'Car has started'


def extends(klass):
    def decorator(func):
      setattr(klass, func.__name__, func)
      return func
    return decorator

#this can go in a different module/file
@extends(Car)
def do_start(self):
    self.start()


#so can this
car = Car()
car.do_start()

#=> Car has started

答案 4 :(得分:2)

我没有使用它,但this package called partial声称要添加对部分类的支持。

似乎还有其他几种方法可以自己实现。

你可以在单独的文件中将mixin的不同部分实现为mixin,然后将它们全部导入并继承它们。

或者,您可以在某个地方实现类的每个方法,然后在中心文件中导入它们并将它们作为属性分配给类,以创建整个对象。像这样:

a.py:

def AFunc( self, something ):
    # Do something
    pass

b.py:

def BFunc( self, something ):
    # Do something else
    pass

c.py:

import a, b

class C:
    AFunc = a.AFunc
    BFunc = b.BFunc

如果你真的想要,你甚至可以自动化这个过程 - 循环遍历模块ab提供的所有功能,然后将它们作为属性添加到C 。虽然这可能完全矫枉过正。

可能还有其他(可能更好的)方法可以解决这个问题,但是这些方法可能会出现在脑海中。

答案 5 :(得分:1)

我遇到了同样的情况 - 我想把我的班级翻到2个档案。 原因是 - 我希望第1部分用于GUI布局,只需布局 和另一个文件保持所有功能。 比如c#的Partial class。一个用于XAML,另一个用于功能。

答案 6 :(得分:0)

首先,我没有看到如何将类拆分成多个文件使编辑变得更容易。一个体面的IDE应该能够轻松找到任何方法,无论是在一个文件还是多个文件中;如果您没有使用体面的IDE,拆分类意味着维护者必须猜测给定方法所处的文件,这听起来更难而不是更容易。

更基本的是,这个类 - 如此之大,以至于你想要一个特殊的语言功能来支持它的重量 - 听起来从根本上被打破了。我们谈论了多少行代码?几乎可以肯定,做一个更好的主意是:

  • 将重复的代码重构为更少,更通用的原语
  • 定义一个基类,并使用子类扩展它,正如Karoly Horvath在评论中所建议的那样(这是你要求的'部分类'最接近我支持的内容)。
  • 定义一些单独的类来封装它的不同部分 类的功能,并组成这类实例 较小的。

答案 7 :(得分:0)

我想补充一点,这样做的pythonic方法是通过多重继承,而不必使用mixins。可以在super().__init__(*args, **kwargs)调用中使用__init__来添加实例属性,以将参数传递给基类(请参见Raymond Hettinger 1的“超级考虑到的超级”演示)。这还使依赖项注入和某种考虑力可以使您考虑基类的组织(如果只有一个基类在__init__中设置一个属性,并且所有使用该属性的类都从基类继承,则效果最好。)

这通常需要您控制基类(或者为该模式编写基类)。

另一种选择是使用描述符通过__get__返回函数,以分离的方式向类添加功能。

您还可以查看__init_subclass__添加例如类生成期间的类的方法(我认为是在python 3.6中添加的,但要检查)

答案 8 :(得分:-1)

首先,我想说一些复杂的事情可能不是一个好主意,只是为了让你在课堂上找到你的位置更容易 - 最好添加评论,突出部分等等。但是,我看到两种方式你可以做到这一点:

  1. 将该类写入多个文件中,然后将其作为文本读取,将它们连接起来并exec生成的字符串。

  2. 在每个文件中创建一个单独的类,然后将它们全部作为mixins继承到主类中。但是,如果你已经继承了另一个类,这可能会导致MRO问题。您可以通过为您的主类创建一个手动解析MRO的元类来解决这个问题,但这可能会变得混乱。

  3. 最简单的是第一个选择。