Получить имя внешнего класса для вложенного класса (Python)

#python #class #python-3.x #nested

#python #класс #python-3.x #вложенный

Вопрос:

Предыстория

(Может быть актуально, потому что может быть более простой способ достичь того, чего я хочу.)

Я хочу создать декларативный способ определения «аспектов», которые могут быть проанализированы с помощью инструмента статического анализа кода. Вся концепция записана здесь. Каждый аспект (скажем Redundancy ) может рекурсивно иметь подаспекты (скажем Redundancy.Clone ), и каждый аспект должен иметь документацию и произвольные другие свойства. Пользователь должен иметь возможность выбирать, какие аспекты анализировать, и я должен программно выяснить для внутреннего представления аспекта, является ли оно тем, которое выбрано пользователем (т. Е. Для данного класса Redundancy.Clone я хочу подтвердить, что он принадлежит данной строке, redundancy.clone но не redundancy.unused_import ).

Я решил использовать классы, подобные этому:

 class Redundancy(Aspect):
    """
    This meta aspect describes any kind of redundancy in your source code.
    """

    # Can't inherit from Redundancy here because of the recursion
    class Clone(Aspect):
        """
        This redundancy describes a code clone. Code clones are different pieces of
        code in your codebase that are very similar.
        """

        # Stuff...
  

Проблема

Для данного Aspect класса я хочу получить описывающую строку ( Redundancy.Clone -> redundancy.clone ). Для этого я должен получить имя окружающего модуля / класса / что бы это ни было, проверить, является ли это классом (тривиальным) и построить из него строку.

Возможные решения и причины их сбоя

Я попытался просмотреть dir свои классы, чтобы посмотреть, есть ли что-нибудь полезное в методах dunder, которые я мог бы использовать, но ничего не нашел, кроме того, repr который в приведенном выше случае является <class 'coalib.bearlib.aspects.Redundancy.Clone'> при работе в aspects модуле. Это показывает, что это должно быть возможно, но я понятия не имею, как repr получить эту информацию, и я хотел бы избежать использования repr и удаления ненужных материалов, поскольку это своего рода взлом.

Я не могу наследовать вложенный класс от внешнего, поскольку он еще не полностью определен. Я хочу, чтобы они были вложены для удобства использования, возможность from ... import Redundancy записи в моем исходном коде Redundancy.Clone является огромным плюсом.

Буду признателен за любые предложения, включая изменение моего подхода.

Комментарии:

1. Вложенные классы очень редко полезны в Python. Делает ли избыточность что-нибудь сама по себе? Если нет, то, вероятно, это должен быть просто модуль.

2. Наличие у них класса, наследуемого от Aspect, приятно, потому что мы можем предоставить ему возможности, привязанные к его «бытию», например, это позволяет определять настройки, с которыми мы можем выполнять операции.

3. (Упс, введите отправку уже …) Это все равно должно работать рекурсивно, например Redundancy.Clone.FullClone или еще много чего. Требования таковы: — Сами аспекты должны быть очень простыми в написании — Должны быть интуитивно понятными в использовании (например Redundancy.Clone ) — Мне нужно иметь возможность получить строковое представление, как описано (это может быть сложным, но это не делается пользователем)

Ответ №1:

Вы могли бы использовать __qualname__ (PEP 3155)

 >>> class C:
...   def f(): pass
...   class D:
...     def g(): pass
...
>>> C.__qualname__
'C'
>>> C.f.__qualname__
'C.f'
>>> C.D.__qualname__
'C.D'
>>> C.D.g.__qualname__
'C.D.g'
  

Комментарии:

1. Каждый день ты узнаешь что-то новое — большое спасибо! Кажется, это именно то, что нам нужно.

Ответ №2:

Вы можете создать класс после того, как class Redundancy инструкция завершит свое выполнение с помощью класса type. Например:

 class Aspect:
    pass

class Redundancy(Aspect):    

    @classmethod
    def init(cls):
       cls.make_Clone()

    @classmethod
    def make_Clone(cls):
        def __init__(self):
            print('inside Redundancy.Clone')

        methods = {} 
        methods["__init__"] =  __init__

        cls.Clone = type("{0}.Clone".format(cls.__name__), (Redundancy,), methods )

Redundancy.init()

print(Redundancy.Clone)
print(Redundancy.Clone())
# output:
#   <class '__main__.Redundancy.Clone'>
#   inside Redundancy.Clone
#   <__main__.Redundancy.Clone object at 0x01DCA130>
  

Комментарии:

1. Действительно, спасибо за ответ! Однако это усложняет наш API, поскольку пользователи должны иметь возможность легко определять эти вещи декларативным способом. Вложенные классы позволяют нам — и благодаря __qualname__ мы можем сделать это вот так. (Плюс мы не поддерживаем python 3.3 и ниже.)