How dangerous is setting self.__class__ to something else?

前端 未结 8 739
难免孤独
难免孤独 2020-11-27 18:10

Say I have a class, which has a number of subclasses.

I can instantiate the class. I can then set its __class__ attribute to one of the subclasses. I hav

8条回答
  •  我在风中等你
    2020-11-27 19:01

    In the comments I proposed modeling cellular automata as a possible use case for dynamic __class__s. Let's try to flesh out the idea a bit:


    Using dynamic __class__:

    class Stage(object):
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
    class Stage1(Stage):
        def step(self):
            if ...:
                self.__class__ = Stage2
    
    class Stage2(Stage):
        def step(self):
            if ...:
                self.__class__ = Stage3
    
    cells = [Stage1(x,y) for x in range(rows) for y in range(cols)]
    
    def step(cells):
        for cell in cells:
            cell.step()
        yield cells
    

    For lack of a better term, I'm going to call this

    The traditional way: (mainly abarnert's code)

    class Stage1(object):
        def step(self, cell):
            ...
            if ...:
                cell.goToStage2()
    
    class Stage2(object):
        def step(self, cell):
            ...
            if ...:        
                cell.goToStage3()
    
    class Cell(object):
        def __init__(self, x, y):
            self.x = x
            self.y = y
            self.current_stage = Stage1()
        def goToStage2(self):
            self.current_stage = Stage2()
        def __getattr__(self, attr):
            return getattr(self.current_stage, attr)
    
    cells = [Cell(x,y) for x in range(rows) for y in range(cols)]
    
    def step(cells):
        for cell in cells:
            cell.step(cell)
        yield cells
    

    Comparison:

    • The traditional way creates a list of Cell instances each with a current stage attribute.

      The dynamic __class__ way creates a list of instances which are subclasses of Stage. There is no need for a current stage attribute since __class__ already serves this purpose.

    • The traditional way uses goToStage2, goToStage3, ... methods to switch stages.

      The dynamic __class__ way requires no such methods. You just reassign __class__.

    • The traditional way uses the special method __getattr__ to delegate some method calls to the appropriate stage instance held in the self.current_stage attribute.

      The dynamic __class__ way does not require any such delegation. The instances in cells are already the objects you want.

    • The traditional way needs to pass the cell as an argument to Stage.step. This is so cell.goToStageN can be called.

      The dynamic __class__ way does not need to pass anything. The object we are dealing with has everything we need.


    Conclusion:

    Both ways can be made to work. To the extent that I can envision how these two implementations would pan-out, it seems to me the dynamic __class__ implementation will be

    • simpler (no Cell class),

    • more elegant (no ugly goToStage2 methods, no brain-teasers like why you need to write cell.step(cell) instead of cell.step()),

    • and easier to understand (no __getattr__, no additional level of indirection)

提交回复
热议问题