涉及元类的动态类创建python

发布于 2025-01-11 08:38:32 字数 2520 浏览 6 评论 0原文

我正在尝试使用参数创建类的实例,问题是我的元类需要一些位置参数来决定类创建的基础。

这是一个更清晰的例子:


Here are the childs which will be methods of the class (they will be chosen be the parent class object name)
import inspect
import sys

class ParentA:
    pass


class Child1(ParentA):
    def ch1_cmd(self):
        print('This is RAM Telnet IOS Cisco ')


class Child2(ParentA):
    def ch2_cmd(self):
        print('This is Time Telnet IOS Cisco ')

这是我的元类,它接受一个附加参数CONNECTION_TYPE,它是一个字典,所以元类将根据这个字典加载适当的子级,

class MainMeta(type):
    def __new__(cls, clsname, bases, clsdict, CONNECTION_TYPE):
        if CONNECTION_TYPE != None:
            CONNECTION_TYPE['OBJ'] = getattr(
                sys.modules[__name__], CONNECTION_TYPE['OBJ'])
            if CONNECTION_TYPE['TYPE'] == 'CONDITION1':
                if CONNECTION_TYPE['CONNECTION_TYPE'] == 'CONDITION2':
                    bases = []
                    for name, obj in inspect.getmembers(sys.modules[__name__]):
                        if inspect.isclass(obj):
                            if CONNECTION_TYPE['OBJ'].__subclasscheck__(obj):
                                if CONNECTION_TYPE['OBJ'].__name__ != obj.__name__:
                                    bases.append(obj)
        bases = tuple(bases)
        print(clsname, bases, clsdict) # here Shows that bases are loaded CORRECTLY
        return super().__new__(cls, clsname, bases, clsdict)

这是将提供给元类的字典。

clsdic = {'TYPE': 'CONDITION1',
          'CONNECTION_TYPE': 'CONDITION2', 'OBJ': "ParentA", }

问题在于,我使用 exec 使用所需的 CONNECTION_TYPE 字典动态创建我的类,代码运行时没有错误,但创建的类中缺少一些内容,这些是 __module__子类

def dev_creator(*args, **kwargs):
    print()
    myclass = f"""class MyMain(metaclass=MainMeta, CONNECTION_TYPE={kwargs}):\n    pass\n"""
    return exec(myclass, globals())
dev = dev_creator(**clsdic)
print(dir(dev))

这是输出:|

MyMain (<class '__main__.Child1'>, <class '__main__.Child2'>) {'__module__': '__main__', '__qualname__': 'MyMain'}
['__bool__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

i am trying to create an instance of a class using arguments, and the problem is my meta class needs some positional args to decide about the bases of class creation.

here is an example to clear it more:


Here are the childs which will be methods of the class (they will be chosen be the parent class object name)

import inspect
import sys

class ParentA:
    pass


class Child1(ParentA):
    def ch1_cmd(self):
        print('This is RAM Telnet IOS Cisco ')


class Child2(ParentA):
    def ch2_cmd(self):
        print('This is Time Telnet IOS Cisco ')

Here is my metaclass which accepts an additional argument CONNECTION_TYPE which is a dictionary, so metaclass will load proper childs according to this Dictionary

class MainMeta(type):
    def __new__(cls, clsname, bases, clsdict, CONNECTION_TYPE):
        if CONNECTION_TYPE != None:
            CONNECTION_TYPE['OBJ'] = getattr(
                sys.modules[__name__], CONNECTION_TYPE['OBJ'])
            if CONNECTION_TYPE['TYPE'] == 'CONDITION1':
                if CONNECTION_TYPE['CONNECTION_TYPE'] == 'CONDITION2':
                    bases = []
                    for name, obj in inspect.getmembers(sys.modules[__name__]):
                        if inspect.isclass(obj):
                            if CONNECTION_TYPE['OBJ'].__subclasscheck__(obj):
                                if CONNECTION_TYPE['OBJ'].__name__ != obj.__name__:
                                    bases.append(obj)
        bases = tuple(bases)
        print(clsname, bases, clsdict) # here Shows that bases are loaded CORRECTLY
        return super().__new__(cls, clsname, bases, clsdict)

this is the Dictionary which will be served to the meta class.

clsdic = {'TYPE': 'CONDITION1',
          'CONNECTION_TYPE': 'CONDITION2', 'OBJ': "ParentA", }

HERE IS The PROBLEM, I use exec to create my class dynamically with my desired CONNECTION_TYPE dictionary, code runs without Err but there are something missing in the created class, and those are __module__ and child classes.

def dev_creator(*args, **kwargs):
    print()
    myclass = f"""class MyMain(metaclass=MainMeta, CONNECTION_TYPE={kwargs}):\n    pass\n"""
    return exec(myclass, globals())
dev = dev_creator(**clsdic)
print(dir(dev))

This is the output :|

MyMain (<class '__main__.Child1'>, <class '__main__.Child2'>) {'__module__': '__main__', '__qualname__': 'MyMain'}
['__bool__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

自由范儿 2025-01-18 08:38:32

嗯,解决了。
这是全局变量定义问题。

这是我们应该如何调用 exec 并从中获取返回值。

dev = None
def dev_creator(*args, **kwargs):
    # print(kwargs)
    myclass = f"""class MyMain(metaclass=MainMeta, CONNECTION_TYPE={kwargs}):\n    pass\ndev = MyMain"""
    exec(myclass, globals(), dev)
    return dev

HUM, Solved.
it was the global variable definition problem.

here is how we should call an exec and get a return from it.

dev = None
def dev_creator(*args, **kwargs):
    # print(kwargs)
    myclass = f"""class MyMain(metaclass=MainMeta, CONNECTION_TYPE={kwargs}):\n    pass\ndev = MyMain"""
    exec(myclass, globals(), dev)
    return dev
情绪少女 2025-01-18 08:38:32

您根本不需要也不应该调用 exec

只需调用您的元类并传递所需的参数(在这种情况下,名称、基础和命名空间就足够了:

 myclass = metaclass("myclass", (), {}, CONNECTION_TYPE=kwargs)

将为您创建一个新类。(元类 __new__ 末尾的 super() 调用即可执行此操作)。

You don't needand should not call exec at all.

Simply calling your metaclass passign the needed parameters (name, bases and namespace is enough in this case:

 myclass = metaclass("myclass", (), {}, CONNECTION_TYPE=kwargs)

will create you a new class. (the super() call atthe end of the metaclass __new__ does that).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文