Inner Classes: How can I get the outer-class object at construction time?

前端 未结 7 856
死守一世寂寞
死守一世寂寞 2020-12-08 23:15

Consider the following Python (runs in 2.x or 3.x):

class Outer(object):
  pass

  class Inner(object):
    def __init__(self):
      print(\"Inner.self\", s         


        
7条回答
  •  借酒劲吻你
    2020-12-09 00:03

    This is my solution for python 3 (I'm using 3.7, don't know for earlier versions or 2.x)

    from functools import wraps
    from inspect import signature as _signature
    
    class _Bound:
        '''type of bound methodsand classes'''
        def __init__(self, callable):
            self._callable = callable
        def __get__(self, instance, cls):
            'called when instance.callable or Class.callable'
            if instance is None: # called on class, not instance
                return self._callable
    
            @wraps(self._callable)
            def retf(*a, **k):
                try: 
                    return self._callable(instance, *a, **k)
                except: # information if failed
                    # user did `self.callable(*args, **kwargs)` and failed
                    print(f'failed call of {self._callable}')
                    print(f'self:   {instance}')
                    print(f'args:   {a}')
                    print(f'kwargs: {k}')
                    raise 
            # additional wrap to expell first argument
            sig = _signature(retf)
            retf.__signature__ = sig.replace(parameters=tuple(sig.parameters.values())[1:])
    
            return retf
        def __call__(self, *a, **k):
            'Class.__dict__['BoundMethod'](instance, *a, **k) (c(), 55, 8, key=9)'
            return self._callable(*a, **k)
        pass
    
    class UnBound:
        "similar to static, only doesn't change attribute at all (except for builtin-like method making)"
        def __init__(self, callable):
            self.callable = callable
        pass
    class class_(classmethod):
        pass
    class static(staticmethod):
        pass
    
    class NestedType(type):
        '''meta to support nested classes (and other callables)
        meta to outer class'''
        @staticmethod
        def bind(dict):
            for name, attr in dict.items():
                if callable(attr) and not isinstance(attr, (class_, static)): #functions, classes, ...
                    dict[name] = _Bound(attr)
                elif isinstance(attr, UnBound):
                    dict[name] = attr.callable
        def __new__(meta, object_or_name, bases, dict):
            NestedType.bind(dict)
            return type.__new__(meta, object_or_name, bases, dict)
    
    class NestedBase(metaclass=NestedType):
        pass
    

    example

    class Outer():
        class Inner:
            def __init__(self, self_): # I prefer self_ above outer
    

    note0: here can come NestedBase or metaclass=NestedType (and beware of meta mismatch error)

    need to know
    Outer.Inner return class module.Outer.Inner
    Outer().Inner returns bound method
    Outer.__dict__['Inner'] returns bound object _Bound...

    note1
    imports are not that important, they only change pop-up when using IDE (VScode python extension doesn't recognize it, so it displays it wrongly, but IDLE does)

    classes explained
    _Bound should not be used

    used as a decorator:
    UnBound will make old-style binding
    class_ same as classmethod
    static same as staticmethod

    NestedType metaclass to the outer class
    NestedBase base to the outer class
    only one needed per outer class

    note2
    inner class that has no decorator UnBound needs to have init that receives as 2nd argument
    otherwise will write (see _Bound.__get__.retf.except) and throw TypeError: Inner(?) takes ? arguments

    meta mismatch
    to see meta mismatch write class M(type): pass, class A(NestedBase): pass, class B(metaclass=M): pass, class C(A, B): pass
    solution: create submeta of both metas and, if , call NestedType.bind(dict) (as in NestedType.__new__)
    note3: overriding __new__ or NestedMeta not 1st in subclassing order (class NewMeta(NotNestedMeta, NestedMeta) ( here NestedMeta is non-strict submeta of NestedType)

提交回复
热议问题