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