在Python中将类属性反向映射到类

0 投票
2 回答
649 浏览
提问于 2025-04-15 13:32

我有一些Python代码,里面有很多类,每个类都有一个属性叫做_internal_attribute。我想生成一个把这些属性和原始类对应起来的映射。简单来说,我想做到这一点:

class A(object):
    _internal_attribute = 'A attribute'
class B(object):
    _internal_attribute = 'B attribute'

a_instance = magic_reverse_mapping['A attribute']()
b_instance = magic_reverse_mapping['B attribute']()

我现在缺少的是如何生成magic_reverse_mapping这个字典。我感觉使用元类来生成A和B可能是正确的做法;你觉得这样对吗?

2 个回答

3

你需要一个数据结构来存储适用的类列表,但一开始并不需要生成这个列表。你可以直接从全局变量中读取类。这自然假设你的类是从 object 继承的,就像你在第一条帖子中提到的那样。

def magic_reverse_mapping(attribute_name, attribute_value):
    classobjects = [val for val in globals().values() if isinstance(val, object)]
    attrobjects = [cls for cls in classobjects if hasattr(cls, attribute_name)]
    resultobjects = [cls for cls in attrobjects if object.__getattribute__(cls, attribute_name) == attribute_value]
    return resultobjects

magic_reverse_mapping('_internal_attribute', 'A attribute')
#output:   [<class '__main__.A'>]

请注意,这个方法返回的是一个包含该属性值的类的 列表,因为可能会有多个类。如果你想实例化第一个类,可以这样做:

magic_reverse_mapping('_internal_attribute', 'A attribute')[0]()
#output:   <__main__.A object at 0xb7ce486c>

和某些人的回答不同,你不需要给你的类添加装饰器(虽然这是个不错的解决方案)。不过,无法排除任何在全局命名空间中的类。

5

你可以使用元类来自动注册你的类到 magic_reverse_mapping 中:

magic_reverse_mapping = {}

class MagicRegister(type):
   def __new__(meta, name, bases, dict):
      cls = type.__new__(meta, name, bases, dict)
      magic_reverse_mapping[dict['_internal_attribute']] = cls
      return cls

class A(object):
    __metaclass__ = MagicRegister
    _internal_attribute = 'A attribute'

afoo = magic_reverse_mapping['A attribute']()

另外,你也可以在你的类上使用装饰器来注册它们。我觉得这样更容易读懂,也更好理解:

magic_reverse_mapping = {}

def magic_register(cls):
   magic_reverse_mapping[cls._internal_attribute] = cls
   return cls

@magic_register
class A(object):
    _internal_attribute = 'A attribute'

afoo = magic_reverse_mapping['A attribute']()

或者你也可以手动去做。其实不使用任何魔法的话,工作量也没多大:

reverse_mapping = {}

class A(object):
    _internal_attribute = 'A attribute'

reverse_mapping[A._internal_attribute] = A

看了不同的方式,我觉得使用装饰器的方式会是最舒服的选择。

撰写回答