Python—;反射初始化类
我正在用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 |- ...
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
中创建一个所有命令的列表,然后遍历所有命令并构造它们。整个问题是如何构造它们。我已经能够弄清楚如何列出一个模块的所有子模块,但是如何检查它们的特定类,以及如何实际初始化该类