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
You can't (in any portable, reliable or efficient way.) Nested classes have no relation to the nesting class. They have no added value, either. Instead of using nested classes, use containment of instances -- define Outer
and Inner
separately, and create an instance of Inner
in Outer.__init__
, possibly passing Outer
or the Outer
instance if you need it. You can make Inner
a class attribute of Outer
if you insist, but it will have no special access to Outer
.
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(<note0>):
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 <Outer object>
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
)
You can use a metaclass to implement a __get__ descriptor that binds the inner class to the outer one. And since you seem to be interested in only binding to a class, consider modifying the inner class in-place, unlike a function which is wrapped into a method.
>>> class Outer(object):
class Inner(object):
class __metaclass__(type):
def __get__(self, instance, owner):
self.owner = owner
return self
>>> Outer.Inner is Outer().Inner
True
>>> Outer.Inner.owner is Outer
True
If you'd rather wrap the inner class via a subclass then replace the __get__ body with:
return type(self.__name__, (self,), {'owner': owner})
See Access outer class from inner class in python (https://stackoverflow.com/a/35118717/3787376)
for a simple reliable answer I made that only uses variables, attributes, functions and classes - no special code.
In Python 2.6, a class decorator that's also a custom descriptor matches the specs you give:
class InnerClassDescriptor(object):
def __init__(self, cls):
self.cls = cls
def __get__(self, instance, outerclass):
class Wrapper(self.cls):
outer = instance
Wrapper.__name__ = self.cls.__name__
return Wrapper
class Outer(object):
@InnerClassDescriptor
class Inner(object):
def __init__(self):
print self.outer
o = Outer()
i = o.Inner()
print 'Outer is a', type(Outer)
print 'Inner is a', type(o.Inner)
This emits:
<__main__.Outer object at 0x82f90>
Outer is a <type 'type'>
Inner is a <type 'type'>
just to confirm that
o.Inner [[is]] a class object, not something weird like a closure
as per your peculiar specs. Of course it needs to be a different class each time for reentrancy -- even in a single-threaded world, the following:
o1 = Outer()
o2 = Outer()
i1 = o1.Inner
i2 = o2.Inner
print i1(), i2(), i1(), i2()
should work cleanly, and stashing o1 vs o2 anywhere else than in the classes returned by o1.Inner
vs o2.Inner
(e.g., in TLS) would mean horrible results for this use.
But then you didn't specify "o.Inner
has to be exactly the same class object for every possible o
that's an instance of Outer
", so this code fully meets the specs you did give;-).
You should redesign your code not to use inner classes and to explicitly pass the instance of Outer to Inner when you make it, or not to require it.