Change python mro at runtime

后端 未结 8 2408
我寻月下人不归
我寻月下人不归 2020-12-15 09:02

I\'ve found myself in an unusual situation where I need to change the MRO of a class at runtime.

The code:

class A(object):
    def __init__(self):
          


        
8条回答
  •  执笔经年
    2020-12-15 09:33

    I don't know if it's relevant to the specific problem, but it seems to me that changing the MRO on the fly like that could be risky in a concurrent program, and could definitely have issues if any of these objects turn out to be created recursively.

    A non-MRO-based solution occurs to me, depending on the nature of the errors this code would have encountered. (Keeping in mind that this is belated. Perhaps somebody else will want a different answer.)

    Basically, each hello() method on B would be wrapped in a decorator. Something along the lines of

    class deferring(object):
    
        def __init__(self, function):
            self.function = function
    
        def __get__(self, instance, owner):
            # Return an unbound method, or whatever, when called from B.
            if instance is None:
                return self.function.__get__(None, owner)
            else:
                # Indicate that an instance is ready via a flag.
                # May need to tweak this based on the exact problem.
                if hasattr(instance, '_set_up'):
                    return self.function.__get__(instance, owner)
                else:
                    # Walk the mro manually.
                    for cls in owner.__mro__:
                        # Crazy inefficient. Possible to mitigate, but risky.
                        for name, attr in vars(cls).items():
                            if attr is self:
                                break
                        else:
                            continue
                        return getattr(super(cls, instance), name)
                    else:
                        raise TypeError
    

    If you don't want to go the descriptor route, it's also possible to do something like

    def deferring(function):
        def wrapped(self, *args, **kwargs):
            if hasattr(self, '_set_up'):
                return function(self, *args, **kwargs)
            else:
                for cls in type(self).__mro__:
                    for name, attr in vars(cls).items():
                        if attr is function:
                            break
                    else:
                        continue
                    return getattr(super(cls, self), name)(*args, **kwargs)
                else:
                    raise TypeError
        return wrapped
    

提交回复
热议问题