Is there any Python equivalent to partial classes?

前端 未结 9 1484
無奈伤痛
無奈伤痛 2020-11-27 15:31

Using \"new\" style classes (I\'m in python 3.2) is there a way to split a class over multiple files? I\'ve got a large class (which really should be a single class from an

相关标签:
9条回答
  • 2020-11-27 16:11

    If your problem really is just working with a large class in an editor, the first solution I'd actually look for is a better way to break down the problem. The second solution would be a better editor, preferably one with code folding.

    That said, there are a couple of ways you might break up a class into multiple files. Python lets you use a folder as a module by putting an __init__.py in it, which can then import things from other files. We'll use this capability in each solution. Make a folder called, say, bigclass first.

    1. In the folder put the various .py files that will eventually comprise your class. Each should contain functions and variable definitions for the eventual class, not classes. In __init__.py in the same folder write the following to join them all together.

      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
      

      This has the advantage that you end up with a single class derived directly from object, which will look nice in your inheritance graphs.

    2. You could use multiple inheritance to combine the various parts of your class. In your individual modules you would write a class definition for Bigclass with parts of the class. Then in your __init__.py write:

      import classdef1, classdef2, classdef3
      
      class Bigclass(classdef1.Bigclass, classdef2.Bigclass, classdef3.Bigclass):
          num = 42   # add more members if desired
      
    3. If the multiple inheritance becomes an issue, you can use single inheritance: just have each class inherit from another one in chain fashion. Assuming you don't define anything in more than one class, the order doesn't matter. For example, classdef2.py would be like:

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

      classdef3 would import Bigclass from classdef2 and add to it, and so on. Your __init__.py would just import the last one:

      from classdef42 import Bigclass
      

    I'd generally prefer #1 because it's more explicit about what members you're importing from which files but any of these solutions could work for you.

    To use the class in any of these scenarios you can just import it, using the folder name as the module name: from bigclass import Bigclass

    0 讨论(0)
  • 2020-11-27 16:13

    I would like to add that the pythonic way of doing this is through multiple inheritance, not necessarily using mixins. Instance attributes can be added using super().__init__(*args, **kwargs) in __init__ calls to pass arguments to baseclasses (see ‘super considered super’ presentation by Raymond Hettinger 1). This also enables dependency injection and kind of forces you to think about organization of base classes (it works best if only one baseclass sets an attribute in __init__ and all classes using the attribute inherit from it).

    This does usually require you having control over the base classes (or they being written for this pattern).

    Another option is using descriptors returning functions through __get__ to add functionality to classes in a decoupled way.

    You could also look at __init_subclass__ to add e.g. methods to classes during class generation (i think added in python 3.6, but check)

    0 讨论(0)
  • 2020-11-27 16:15

    I've not used it, but this package called partial claims to add support for partial classes.

    It seems like there's a few other ways you could implement this yourself as well.

    You could implement separate parts of the class as mixins in seperate files, then import them all somewhere and subclass them.

    Alternatively, you could implement each of the methods of your class somewhere then in a central file import them and assign them as attributes on a class, to create the whole object. Like so:

    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
    

    You could even go so far as to automate this process if you really wanted - loop through all the functions provided by modules a and b and then add them as attributes on C. Though that might be total overkill.

    There might be other (possibly better) ways to go about it, but those are the 2 that popped into mind.

    0 讨论(0)
  • 2020-11-27 16:17

    Class definitions containing hundreds of lines do occur "in the wild" (I have seen some in popular open-source Python-based frameworks), but I believe that if you ponder what the methods are doing, it will be possible to reduce the length of most classes to a manageable point. Some examples:

    • Look for places where mostly the same code occurs more than once. Break that code out into its own method and call it from each place with arguments.
    • "Private" methods that do not use any of the object state can be brought out of the class as stand-alone functions.
    • Methods that should be called only under certain conditions may indicate a need to place those methods in a subclass.

    To directly address your question, it is possible to split up the definition of a class. One way is to "monkey-patch" the class by defining it and then adding outside functions to it as methods. Another is to use the built-in type function to create the class "by hand", supplying its name, any base classes, and its methods and attributes in a dictionary. But I do not recommend doing this just because the definition would be long otherwise. That sort of cure is worse than the disease in my opinion.

    0 讨论(0)
  • 2020-11-27 16:21

    You can do this with decorators like so:

    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
    
    0 讨论(0)
  • 2020-11-27 16:21

    First off, I don't see how splitting the class into multiple files makes editing any easier. A decent IDE should be able to find any method easily whether in one file or multiple; if you're not using a decent IDE, splitting the class means the maintainer has to guess which file a given method is in, which sounds harder rather than easier.

    More fundamentally, this class - so large that you want a special language feature just to support its weight - sounds fundamentally broken. How many lines of code are we talking about? Almost certainly, it would be a better idea to do one of:

    • Refactor duplicated code into fewer, more general primitives
    • Define a base class and extend it with subclasses as Karoly Horvath suggests in comments (this is the closest thing to the 'partial classes' that you're asking for that I would endorse)
    • Define a few separate classes to encapsulate different parts of this class's functionality, and compose this class of instances of those smaller ones.
    0 讨论(0)
提交回复
热议问题