Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 3.x 从python 3动态更改类父属性_Python 3.x_Class_Metaclass - Fatal编程技术网

Python 3.x 从python 3动态更改类父属性

Python 3.x 从python 3动态更改类父属性,python-3.x,class,metaclass,Python 3.x,Class,Metaclass,我希望新类根据创建实例时给定的属性从不同的父类动态继承。到目前为止,我已经尝试过这样的方法: class Meta(type): chooser = None def __call__(cls, *args, **kwars): if kwargs['thingy'] == 'option': Meta.choose = option return super().__call__(*args, **kwargs)

我希望新类根据创建实例时给定的属性从不同的父类动态继承。到目前为止,我已经尝试过这样的方法:

class Meta(type):
    chooser = None
    def __call__(cls, *args, **kwars):
        if kwargs['thingy'] == 'option':
            Meta.choose = option
        return super().__call__(*args, **kwargs)

    def __new__(cls, name, parents, attrs):
        if Meta.choose == option:
            bases = (parent1)
        return super().__new__(cls, name, parents, attrs)

它不起作用,有没有一种方法可以根据实例的一个参数动态选择类的父级?

首先,让我们修复代码中的一个小错误,然后深入研究“真正的问题”:
base
参数需要是一个元组。当您执行
base=(option)
时,右侧不是元组-它只是一个括号中的表达式,将被解析并作为非元组
选项传递

每当需要为基创建元组时,将其更改为
bases=(选项,)

第二个错误更具概念性,可能是您没有让它跨varios尝试工作的原因:元类的
\uuu调用\uuu
方法不是人们通常会玩弄的东西。为了缩短长期历史,调用
\uuuuuuuu类的
\uuuuuuu调用
方法是为了协调该类实例的
\uuuu新的
\uuuu初始化
方法的调用,这是由Python自动生成的,而
类型
\uuuuuu调用
具有这种机制。当你把它转换到你的元类时,你可能会意识到当你的元类本身的
\uuuuuuu新的
\uuuu初始的
方法即将被调用时,你的元类的
\uuuuuuuuuuu
方法是没有被使用的。换句话说,所使用的
\uuuuuuuuuuuuuuuu调用
是在“meta-meta”类(同样是type)上使用的

您编写的
\uuuuu call\uuuuuu
方法将在创建自定义类时由它们的实例使用(这是您想要的),并且不会对类的创建产生影响,因为它不会调用元类“
\uuuuu new\uuuuuu
——只调用类本身。(这不是你想要的)

因此,您需要的是,从内部
\uuuuu call\uuuuu
不要调用
super()。\uuu call\uuuuu
与您收到的参数相同:这将传递
cls
type
的调用,而
cls
的基础是在运行元类
\uuuuu new\uuuu
时烘焙而成的,这只是在声明类主体本身时发生的

您必须在此
\uuuuu call\uuuu
中动态创建一个新类,或使用一个预先填充的表,并将动态创建的类传递给
类型

但是,归根结底,我们可以看到所有这些都可以通过工厂函数来完成,因此没有必要为此创建这种超复杂的元类机制,而其他Python工具,如Linter和静态分析器(如您或您的同事可能正在使用的IDE中所演示的)可能会更好地使用这种机制

使用工厂功能的解决方案: 如果您不想在每次调用时都创建一个新类,而是想用公共基共享两个预先存在的类,只需创建一个缓存字典,并使用dict的
setdefault
方法:

class_cache = {}

def factory(cls, *args, options=None, **kwargs):
   
   
   if options == 'thingy': 
        cls = class_cache.setdefault((cls.__name__, options),
              type(cls.__name__, (option1, ), cls.__dict__))
   elif options = 'other':
        ...
   return cls(*args, **kwargs)
(如果键(名称、选项)还不存在,setdefault方法将在dict上存储第二个参数)

使用元类: 已更新

早餐后:-)我想到了这个: 使您的元类
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
在创建的类本身上插入一个
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。但与其他示例不同,使用元类将原始参数指定给类创建以创建派生类:

parameter_buffer = {}
derived_classes = {}

class Meta:
    def __new__(metacls, name, bases, namespace):
        cls = super().__new__(metacls, name, bases, namespace)
        parameter_buffer[cls] = (name, bases, namespace) 
        def __new__(cls, *args, option=None, **kwargs):
            if option is None:
                return original_new(cls, *args, **kwargs)
            name, bases, namespace = parameter_buffer[cls]
            if option == 'thingy':
                bases = (option1,)
            elif option== 'thingy2':
                ...
            if not (cls, bases) in derived_classes:
                derived_classes[cls, bases] = type(name, bases, namespace)
            return derived_classes[cls, bases](*args, **kwargs)
            
        cls.__new__ = __new__
        return cls
为了保持示例简短,这只需覆盖使用此元类的类上的任何explict
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。此外,以这种方式创建的派生类本身并不具有相同的功能,因为它们是通过调用
type
创建的,并且元类在这个过程中被丢弃。这两件事都可以通过编写更仔细的代码来解决,但作为一个例子,它会变得复杂

parameter_buffer = {}
derived_classes = {}

class Meta:
    def __new__(metacls, name, bases, namespace):
        cls = super().__new__(metacls, name, bases, namespace)
        parameter_buffer[cls] = (name, bases, namespace) 
        def __new__(cls, *args, option=None, **kwargs):
            if option is None:
                return original_new(cls, *args, **kwargs)
            name, bases, namespace = parameter_buffer[cls]
            if option == 'thingy':
                bases = (option1,)
            elif option== 'thingy2':
                ...
            if not (cls, bases) in derived_classes:
                derived_classes[cls, bases] = type(name, bases, namespace)
            return derived_classes[cls, bases](*args, **kwargs)
            
        cls.__new__ = __new__
        return cls