Reverse mapping class attributes to classes in Python

半世苍凉 提交于 2020-01-07 06:40:34

问题


I have some code in Python where I'll have a bunch of classes, each of which will have an attribute _internal_attribute. I would like to be able to generate a mapping of those attributes to the original class. Essentially I would like to be able to do this:

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']()

What I'm missing here is how to generate magic_reverse_mapping dict. I have a gut feeling that having a metaclass generate A and B is the correct way to go about this; does that seem right?


回答1:


You can use a meta class to automatically register your classes in 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']()

Alternatively you can use a decorator on your classes to register them. I think this is more readable and easier to understand:

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']()

Or you could even do it by hand. It's not that much more work without using any magic:

reverse_mapping = {}

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

reverse_mapping[A._internal_attribute] = A

Looking at the different variants I think the decorator version would be the most pleasant to use.




回答2:


You need some data structure to store the list of applicable classes in the first place, but you don't have to generate it in the first place. You can read classes from globals instead. This naturally assumes that your classes extend object, as they do in your first post.

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'>]

Note that this returns a list of classes with that attribute value, because there may be more than one. If you wanted to instantiate the first one:

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

Unlike in sth's answer, you don't have to add a decorator to your classes (neat solution, though). However, there's no way to exclude any classes that are in the global namespace.



来源:https://stackoverflow.com/questions/1263479/reverse-mapping-class-attributes-to-classes-in-python

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!