Python—;反射初始化类

Python—;反射初始化类,python,python-3.x,reflection,chatbot,python-class,Python,Python 3.x,Reflection,Chatbot,Python Class,我正在用Python创建一个命令系统。我有一个模块vkcommands,它有一个处理来自chat的命令的类(这是一个聊天机器人),在它里面,我还有一个类vkcommands,它的属性有name,usage,min_-rank,等等。然后我有一个模块vkcmds,它的子模块实现这些命令: ... vkcommands.py vkcmds |- __init__.py # empty |- add_group.py |- another_cmd.py |- ...

我正在用Python创建一个命令系统。我有一个模块
vkcommands
,它有一个处理来自chat的命令的类(这是一个聊天机器人),在它里面,我还有一个类
vkcommands
,它的属性有
name
usage
min_-rank
,等等。然后我有一个模块
vkcmds
,它的子模块实现这些命令:

...
vkcommands.py
vkcmds
    |- __init__.py  # empty
    |- add_group.py
    |- another_cmd.py
    |- ...
命令的实现(例如,
add_group
)如下所示:

导入等级
导入vkcommands
从VKCommand导入VKCommand
类AddGroup(VKCommand):
定义初始(自我,克里斯蒂):
VKCommand.\uuuuuu init\uuuuuuuuuuuu(克丽丝蒂,赛尔夫),
label='create',
#…(其他属性)
min_rank=ranks.rank.USER)
def execute(self、chat、peer、sender、args=None、attachments=None):
#实现(从vkcommands.py调用)
当用户在聊天中发送消息时,命令管理器会对其进行分析,并查看已注册的
命令列表,以查看这是普通消息还是bot命令。目前,我手动注册
命令列表中的所有命令,如下所示:

VKCommandsManager类:
定义初始(自我,克里斯蒂):
从vkcmds导入(
添加_组,
下节课
)
self.kristy=kristy
self.commands=(
添加组。添加组(kristy),
下一节课。下一节课(克里斯蒂)
)
现在,我希望所有命令都使用反射来自动注册。在Java中,我会迭代命令包中的所有类,反射地
getConstructor
,调用它来检索
VKCommand
对象,并将其添加到命令列表中

如何在Python中执行此操作?同样,我需要的是:

  • 迭代模块(文件夹)
    vkcmds/
    中的所有子模块
  • 对于每个子模块,检查是否有一些类
    X
    扩展了
    VKCommand
  • 如果(2)为
    true
    ,则使用一个参数调用该类的构造函数(保证所有命令的构造函数只有一个已知类型的参数(我的机器人的主类))
  • 将(3)中构造的对象(
    ?extends VKCommand
    )添加到
    commands
    列表中,我可以稍后迭代该列表

  • 我相信您可以将文件夹中的所有命令组成一个数组,然后遍历它们并实例化对象

    \uuuu init\uuuuu.py中

    all_命令=[AddGroup,AnotherCmd,…]
    
    将其实例化如下:

    objects=[Cmd(arg1,arg2,…)用于所有_命令中的Cmd]
    
    编辑:
    您还可以使用您所说的获取文件夹中所有类名的方法来检索类名。

    我相信您可以将文件夹中的所有命令组成一个数组,然后检查它们并实例化对象

    \uuuu init\uuuuu.py中

    all_命令=[AddGroup,AnotherCmd,…]
    
    将其实例化如下:

    objects=[Cmd(arg1,arg2,…)用于所有_命令中的Cmd]
    
    编辑:
    您还可以使用您所说的获取文件夹中所有类名的方法来检索类名。

    使用此文件结构:

    - Project
       ├─ commands
       |   ├─ base.py
       |   ├─ baz.py
       |   └─ foo_bar.py
       |
       └─ main.py
    
    以及
    命令
    目录文件中的以下内容:

    • base.py

      class VKCommand:
          """ We will inherit from this class if we want to include the class in commands.  """
      
    • 巴兹比

      from commands.base import VKCommand
      
      class Baz(VKCommand):
          pass
      
      
      def baz():
          """ Random function we do not want to retrieve.  
      
    • 福乌酒吧

      from .base import VKCommand
      
      
      class Foo(VKCommand):
          """ We only want to retrieve this command.  """
          pass
      
      
      class Bar:
          """ We want to ignore this class.  """
          pass
      
      
      def fizz():
          """  Random function we do not want to retrieve. """
      
    我们可以使用以下代码直接检索类实例和名称:

    • main.py

      """
        Dynamically load all commands located in submodules.
        This file is assumed to be at most 1 level higher than the
        specified folder.
      """
      
      import pyclbr
      import glob
      import os
      
      def filter_class(classes):
          inherit_from = 'VKCommand'
          classes = {name: info for name, info in classes.items() if inherit_from in info.super}
          return classes
      
      # Locate all submodules and classes that it contains without importing it.
      folder = 'commands'  # `vkcmds`.
      submodules = dict()
      absolute_search_path = os.path.join(os.path.dirname(__file__), folder, '*.py')
      for path in glob.glob(absolute_search_path):
          submodule_name = os.path.basename(path)[:-3]
          all_classes = pyclbr.readmodule(f"commands.{submodule_name}")
          command_classes = filter_class(all_classes)
          if command_classes:
              submodules[submodule_name] = command_classes
      
      # import the class and store an instance of the class into the command list
      class_instances = dict()
      for submodule_name, class_names in submodules.items():
          module = __import__(f"{folder}.{submodule_name}")
          submodule = getattr(module, submodule_name)
          for class_name in class_names:
              class_instance = getattr(submodule, class_name)
              class_instances[class_name] = class_instance
      
      print(class_instances)
      
    解释 解决方案有两个方面。它首先定位具有继承自
    VKCommand
    并位于“commands”文件夹中的类的所有子模块。这将导致以下输出,其中包含必须分别导入和实例化的模块和类:

    {'baz': {'Baz': <pyclbr.Class object at 0x000002BF886357F0>}, 'foo_bar': {'Foo': <pyclbr.Class object at 0x000002BF88660668>}}
    
    重要提示:
  • 该代码仅在导入深度为1的模块时有效。如果要递归使用它,则必须找到并使用正确(完整)的相对导入路径更新
    pyclbr.readmodule
    \uuuuuuuuuuuuuu

  • 只有包含从
    VKCommand
    继承的类的模块才会被加载。所有其他模块均未导入,必须手动导入


  • 使用此文件结构:

    - Project
       ├─ commands
       |   ├─ base.py
       |   ├─ baz.py
       |   └─ foo_bar.py
       |
       └─ main.py
    
    以及
    命令
    目录文件中的以下内容:

    • base.py

      class VKCommand:
          """ We will inherit from this class if we want to include the class in commands.  """
      
    • 巴兹比

      from commands.base import VKCommand
      
      class Baz(VKCommand):
          pass
      
      
      def baz():
          """ Random function we do not want to retrieve.  
      
    • 福乌酒吧

      from .base import VKCommand
      
      
      class Foo(VKCommand):
          """ We only want to retrieve this command.  """
          pass
      
      
      class Bar:
          """ We want to ignore this class.  """
          pass
      
      
      def fizz():
          """  Random function we do not want to retrieve. """
      
    我们可以使用以下代码直接检索类实例和名称:

    • main.py

      """
        Dynamically load all commands located in submodules.
        This file is assumed to be at most 1 level higher than the
        specified folder.
      """
      
      import pyclbr
      import glob
      import os
      
      def filter_class(classes):
          inherit_from = 'VKCommand'
          classes = {name: info for name, info in classes.items() if inherit_from in info.super}
          return classes
      
      # Locate all submodules and classes that it contains without importing it.
      folder = 'commands'  # `vkcmds`.
      submodules = dict()
      absolute_search_path = os.path.join(os.path.dirname(__file__), folder, '*.py')
      for path in glob.glob(absolute_search_path):
          submodule_name = os.path.basename(path)[:-3]
          all_classes = pyclbr.readmodule(f"commands.{submodule_name}")
          command_classes = filter_class(all_classes)
          if command_classes:
              submodules[submodule_name] = command_classes
      
      # import the class and store an instance of the class into the command list
      class_instances = dict()
      for submodule_name, class_names in submodules.items():
          module = __import__(f"{folder}.{submodule_name}")
          submodule = getattr(module, submodule_name)
          for class_name in class_names:
              class_instance = getattr(submodule, class_name)
              class_instances[class_name] = class_instance
      
      print(class_instances)
      
    解释 解决方案有两个方面。它首先定位具有继承自
    VKCommand
    并位于“commands”文件夹中的类的所有子模块。这将导致以下输出,其中包含必须分别导入和实例化的模块和类:

    {'baz': {'Baz': <pyclbr.Class object at 0x000002BF886357F0>}, 'foo_bar': {'Foo': <pyclbr.Class object at 0x000002BF88660668>}}
    
    重要提示:
  • 该代码仅在导入深度为1的模块时有效。如果要递归使用它,则必须找到并使用正确(完整)的相对导入路径更新
    pyclbr.readmodule
    \uuuuuuuuuuuuuu

  • 只有包含从
    VKCommand
    继承的类的模块才会被加载。所有其他模块均未导入,必须手动导入


  • 我想你可以在
    \uuu init\uuuu.py
    中创建一个所有命令的列表,然后遍历所有命令并构造它们。整个问题是如何构造它们。我已经能够弄清楚如何列出一个模块的所有子模块,但是如何检查它们的特定类,以及如何实际初始化该类