как получить доступ к свойствам внешнего класса внутри внутренних классов?

#python #inner-classes

Вопрос:

 class Remote:
aa=7
def __init__(self):
    self.name="Lenovo"
    self.b=self.Battery()
    print("this is outer",self.b.t)
class Battery:
    def __init__(self):
        self.name="Hp"
        self.t="df"
        self.c=self.Cover()
    class Cover:
        def __init__(self):
            self.name="Arplastic"
        
c1=Remote()
 

Сегодня я знал о внутреннем классе, но я не знаю, как получить доступ к свойствам и методам внешнего класса во внутренний класс, пожалуйста, дайте мне знать кому-нибудь.

Комментарии:

1. Очень редко бывает полезно вложить такие классы, как этот. Для данного экземпляра внутренних классов нет никакой гарантии, что внешний класс вообще был создан.

2. «Внутреннему» классу не предоставляется никакой специальной области действия для заключающего класса. Cover Экземпляр должен взаимодействовать с Battery экземпляром так же, как и любой другой объект.

Ответ №1:

Измените конструктор(ы) внутреннего(ых) класса (ов), чтобы принять parent аргумент, и пусть создающий экземпляр передаст ему себя:

 class Remote:
    aa=7
    def __init__(self):
        self.name="Lenovo"
        self.b=self.Battery(self)
        print("this is outer",self.b.t)
    class Battery:
        def __init__(self,parent):
            self.name="Hp"
            self.t="df"
            self.c=self.Cover(self)
            self.parent=parent
        class Cover:
            def __init__(self,parent):
                self.name="Arplastic"
                self.parent=parent

c1=Remote()
print(c1.b.c.parent.parent.name) # prints 'Lenovo'
 

Комментарии:

1. спасибо, сэр, это полезно

Ответ №2:

Один из подходов заключается в создании метакласса, который автоматически создает self.parent атрибуты для вложенных классов. Обратите внимание, что здесь есть компромисс между удобочитаемостью и шаблоном-многие программисты предпочли бы, чтобы вы просто вручную передавали родителей в качестве аргументов и добавляли их в __init__ методы. Это, однако, веселее, и есть что сказать в пользу того, что код менее загроможден.

Вот код:

 import inspect


def inner_class(cls):
    cls.__is_inner_class__ = True
    return cls


class NestedClass(type):
    def __new__(metacls, name, bases, attrs, parent=None):
        attrs = dict(attrs.items())
        super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
        inner_class_cache = {}

        def __getattribute__(self, attr):
            val = super_getattribute(self, attr)
            if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
                if (self, val) not in inner_class_cache:
                    inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)

                return inner_class_cache[self, val]
            else:
                return val

        attrs['__getattribute__'] = __getattribute__
        attrs['parent'] = parent

        return type(name, bases, attrs)


class Remote(metaclass=NestedClass):
    aa = 7

    def __init__(self):
        self.name = "Lenovo"
        self.b = self.Battery()
        print("this is outer", self.b.t)

    @inner_class
    class Battery:
        def __init__(self):
            self.name = "Hp"
            self.t = "df"
            self.c = self.Cover()

        @inner_class
        class Cover:
            def __init__(self):
                self.name = "Arplastic"
                print(f'{self.parent=}, {self.parent.parent=}')


c1 = Remote()
print(f'{c1.b.c.parent.parent is c1=}')
print(f'{isinstance(c1.b, c1.Battery)=}')
 

Выход:

 self.parent=<__main__.Battery object at 0x7f11e74936a0>, self.parent.parent=<__main__.Remote object at 0x7f11e7493730>
this is outer df
c1.b.c.parent.parent is c1=True
isinstance(c1.b, c1.Battery)=True
 

Способ, которым это работает, заключается в сохранении атрибута parent как класса ( None по умолчанию) и замене __getattribute__ метода таким образом, чтобы все внутренние классы были заменены на NestedClass es с parent правильно заполненным атрибутом.

inner_class Декоратор используется для пометки класса как внутреннего класса путем установки __is_inner_class__ атрибута.

 def inner_class(cls):
    cls.__is_inner_class__ = True
    return cls

 

Это не является строго необходимым, если все атрибуты, являющиеся классами, должны рассматриваться как внутренние классы, но рекомендуется сделать что-то подобное Bar.foo , чтобы в этом примере их не рассматривали как внутренний класс:

 
class Foo:
    pass

class Bar(metaclass=NestedClass):
    foo = Foo
 

Все NestedClass , что делает метакласс, — это берет описание класса и изменяет его, добавляя parent атрибут:

 class NestedClass(type):
    def __new__(metacls, name, bases, attrs, parent=None):
        attrs = dict(attrs.items())
        
        ...

        attrs['parent'] = parent

        return type(name, bases, attrs)
 

…и изменение __getattribute__ метода. __getattribute__ Метод-это специальный метод, который вызывается каждый раз при обращении к атрибуту. Например:

 class Foo:
    def __init__(self):
        self.bar = "baz"
    def __getattribute__(self, item):
        return 1

foo = Foo()
# these assert statements pass because even though `foo.bar` is set to "baz" and `foo.remote` doesn't exist, accessing either of them is the same as calling `Foo.__getattribute(foo, ...)`
assert foo.bar == 1
assert foo.remote == 1
 

Таким образом, изменив __getattribute__ метод, вы можете заставить self.Battery access возвращать класс , у которого есть parent атрибут, равный self , а также превратить его во вложенный класс:

 class NestedClass(type):
    def __new__(metacls, name, bases, attrs, parent=None):
        attrs = dict(attrs.items())
        # get the previous __getattribute__ in case it was not the default one
        super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
        inner_class_cache = {}

        def __getattribute__(self, attr):
            # get the attribute
            val = super_getattribute(self, attr)
            if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
                # if it is an inner class, then make a new version of it using the NestedClass metaclass, setting the parent attribute
                if (self, val) not in inner_class_cache:
                    inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)

                return inner_class_cache[self, val]
            else:
                return val

        attrs['__getattribute__'] = __getattribute__
        attrs['parent'] = parent

        return type(name, bases, attrs)
 

Обратите внимание, что кэш используется для обеспечения того self.Battery , чтобы всегда возвращать один и тот же объект каждый раз, а не создавать класс заново при каждом его вызове. Это гарантирует, что проверки типа isinstance(c1.b, c1.Battery) работают правильно , так как в противном c1.Battery случае будет возвращен другой объект , чем тот, который использовался для создания c1.b , в результате чего он вернется False , когда он должен вернуться True .

И это все! Теперь вы можете наслаждаться вложенными классами без шаблонных шаблонов!