#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
.
И это все! Теперь вы можете наслаждаться вложенными классами без шаблонных шаблонов!