一种方法是创建一个元类,自动为嵌套类创建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。
就是这样!您现在可以享受没有样板的嵌套类!