【问题标题】:how to access outer class properties inside the inner classes?如何访问内部类中的外部类属性?
【发布时间】:2021-11-27 15:51:50
【问题描述】:
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()

我今天知道了内部类,但我不知道如何将外部类的属性和方法访问到内部类中,请告诉我任何人。

【问题讨论】:

  • 像这样嵌套类很少有用。对于给定的内部类实例,不能保证外部类已经被实例化了。
  • “内部”类没有授予封闭类任何特殊作用域。 Cover 实例必须以与任何其他对象相同的方式与 Battery 实例交互。

标签: python inner-classes


【解决方案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'

【讨论】:

  • 谢谢先生,这很有帮助
【解决方案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__ 方法,以便将所有内部类替换为NestedClasses 和@ 987654329@属性填写正确。

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返回一个其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

就是这样!您现在可以享受没有样板的嵌套类!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 2014-08-10
    • 1970-01-01
    • 2011-01-02
    • 1970-01-01
    • 2011-02-13
    相关资源
    最近更新 更多