#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 и ниже.)