因此,我正在编写一个接口框架,使人们可以编写命令集合。
我在这里想要做的是,对于Base
的每个子类,找到其所有具有描述的方法,然后收集所有这些函数并从中生成一个集体描述(以及其他内容) ,但与此问题无关)
这是MCVE
import types
class Meta(type):
def __init__(cls, *args, **kwargs):
cls._interfaces = {}
super().__init__(*args, **kwargs)
def __call__(cls, id, *args, **kwargs):
if id in cls._interfaces:
return cls._interfaces[id]
obj = cls.__new__(cls, *args, **kwargs)
obj.__init__(*args, **kwargs)
cls._interfaces[id] = obj
return obj
class Base(metaclass=Meta):
@property
def descriptions(self): # this doesn't work. It gives RecursionError
res=''
for name in dir(self):
attr=getattr(self,name)
if isinstance(attr, types.FunctionType) and hasattr(attr, 'description'):
res += f'{name}: {attr.description}\n'
return res
@property
def descriptions(self):
res=''
for cls in self.__class__.__mro__:
for name,attr in cls.__dict__.items():
if isinstance(attr, types.FunctionType) and hasattr(attr, 'description'):
res += f'{name}: {attr.description}\n'
return res
class A(Base):
def a(self): pass
a.description='A.a'
def b(self): pass
b.description='A.b'
class B(A):
def c(self): pass
c.description='B.c'
def d(self): pass
d.description='B.d'
print(B(0).descriptions)
现在我当前方法的问题是它不检测方法重写。假设我将此代码段添加到类B
def a(self): pass
a.description='B.a'
现在生成的描述将完全错误。我想要的是,当某个方法被覆盖时,它所收集的描述也被覆盖。因此,说明应始终指向人们通常采用的方法(例如B.a.description
)Python通常会解析的方法
因此预期输出将变为
a: B.a
b: A.b
c: B.c
d: B.d
我想我可以用单词descriptions
做一个特例并修复第一个description
方法。
但是,有没有更优雅和Pythonic的方式来实现这一目标?老实说,self.__class__.__mro__
看起来非常可怕。
答案 0 :(得分:2)
当您遍历self.__class__.__mro__
时,您正在遍历解决顺序,在您的示例中为<class '__main__.B'> <class '__main__.A'> <class '__main__.Base'> <class 'object'>
。
因此,您随后将查看每个这些类的description
属性。在类a.description = 'B.a'
中声明B
时,实际上并没有在父类A
中覆盖它,而只是在子类B
中覆盖了它。
因此,您可以执行以下操作:
import inspect
class Base(metaclass=Meta):
def descriptions(self):
res = ''
for mtd in inspect.getmembers(self, predicate=inspect.ismethod):
if hasattr(mtd[1], 'description'):
res += f'{mtd[0]}: {mtd[1].description}\n'
return res
那样,您只是检查实际的孩子,而不是其父母。但是,在这种情况下,Base.descriptions
不能是属性,因为属性是在声明时进行评估的,这会使inspect.getmembers
保持检查自身无限长的时间。
答案 1 :(得分:1)
我认为您可以解决descriptions
的第一个实现,这可能比搞MRO容易。得到RecursionError
的原因是"descriptions"
将显示在属性名称列表中。当您对它执行getattr(self, name)
时,由于它是一个属性,它会递归调用自身。您可以通过仅查找名称而不是"descriptions"
来避免这种情况。还存在一个问题,当您在实例上按名称查找方法时,将获得绑定的方法对象,而不是函数。也许您应该在getattr
而不是type(self)
上致电self
?看起来像这样:
@property
def descriptions(self):
res=''
for name in dir(self):
if name != "descriptions": # avoid recursion!
attr = getattr(type(self), name, None) # lookup name on class object
if isinstance(attr, types.FunctionType) and hasattr(attr, 'description'):
res += f'{name}: {attr.description}\n'
return res
避免此问题的另一种方法是摆脱property
装饰器,而将descriptions
保留为常规方法。这样查找它不会导致任何递归,只会得到一个绑定方法。
答案 2 :(得分:-1)
我们不太清楚您希望发生什么行为,但是inspect
模块几乎可以确定您需要什么。例如
inspect.getmro(type(self))
inspect.getclasstree(inspect.getmro(type(self)))