gpt4 book ai didi

python - 元类混合或链接?

转载 作者:IT老高 更新时间:2023-10-28 22:12:39 25 4
gpt4 key购买 nike

是否可以链接元类?

我有类 Model 使用 __metaclass__=ModelBase 来处理其命名空间字典。我将从它继承并“绑定(bind)”另一个元类,这样它就不会遮蔽原来的元类。

第一种方法是继承class MyModelBase(ModelBase):

MyModel(Model):
__metaclass__ = MyModelBase # inherits from `ModelBase`

但是是否可以像 mixins 一样将它们链接起来,而无需显式子类化?像

class MyModel(Model):
__metaclass__ = (MyMixin, super(Model).__metaclass__)

... 甚至更好:从使用它的类的直接父级创建一个将使用 __metaclass__ 的 MixIn:

class MyModel(Model):
__metaclass__ = MyMetaMixin, # Automagically uses `Model.__metaclass__`

原因:为了更灵活地扩展现有应用程序,我想创建一个全局机制来 Hook 到 ModelForm、... Django,因此可以在运行时进行更改。

一个通用的机制比使用回调混合实现多个元类要好得多。


在您的帮助下,我终于想出了一个解决方案:元类 MetaProxy

想法是:创建一个元类,该元类调用回调来修改正在创建的类的命名空间,然后在 __new__ 的帮助下,变异为其中一个父类的元类

#!/usr/bin/env python
#-*- coding: utf-8 -*-

# Magical metaclass
class MetaProxy(type):
""" Decorate the class being created & preserve __metaclass__ of the parent

It executes two callbacks: before & after creation of a class,
that allows you to decorate them.

Between two callbacks, it tries to locate any `__metaclass__`
in the parents (sorted in MRO).
If found — with the help of `__new__` method it
mutates to the found base metaclass.
If not found — it just instantiates the given class.
"""

@classmethod
def pre_new(cls, name, bases, attrs):
""" Decorate a class before creation """
return (name, bases, attrs)

@classmethod
def post_new(cls, newclass):
""" Decorate a class after creation """
return newclass

@classmethod
def _mrobases(cls, bases):
""" Expand tuple of base-classes ``bases`` in MRO """
mrobases = []
for base in bases:
if base is not None: # We don't like `None` :)
mrobases.extend(base.mro())
return mrobases

@classmethod
def _find_parent_metaclass(cls, mrobases):
""" Find any __metaclass__ callable in ``mrobases`` """
for base in mrobases:
if hasattr(base, '__metaclass__'):
metacls = base.__metaclass__
if metacls and not issubclass(metacls, cls): # don't call self again
return metacls#(name, bases, attrs)
# Not found: use `type`
return lambda name,bases,attrs: type.__new__(type, name, bases, attrs)

def __new__(cls, name, bases, attrs):
mrobases = cls._mrobases(bases)
name, bases, attrs = cls.pre_new(name, bases, attrs) # Decorate, pre-creation
newclass = cls._find_parent_metaclass(mrobases)(name, bases, attrs)
return cls.post_new(newclass) # Decorate, post-creation



# Testing
if __name__ == '__main__':
# Original classes. We won't touch them
class ModelMeta(type):
def __new__(cls, name, bases, attrs):
attrs['parentmeta'] = name
return super(ModelMeta, cls).__new__(cls, name, bases, attrs)

class Model(object):
__metaclass__ = ModelMeta
# Try to subclass me but don't forget about `ModelMeta`

# Decorator metaclass
class MyMeta(MetaProxy):
""" Decorate a class

Being a subclass of `MetaProxyDecorator`,
it will call base metaclasses after decorating
"""
@classmethod
def pre_new(cls, name, bases, attrs):
""" Set `washere` to classname """
attrs['washere'] = name
return super(MyMeta, cls).pre_new(name, bases, attrs)

@classmethod
def post_new(cls, newclass):
""" Append '!' to `.washere` """
newclass.washere += '!'
return super(MyMeta, cls).post_new(newclass)

# Here goes the inheritance...
class MyModel(Model):
__metaclass__ = MyMeta
a=1
class MyNewModel(MyModel):
__metaclass__ = MyMeta # Still have to declare it: __metaclass__ do not inherit
a=2
class MyNewNewModel(MyNewModel):
# Will use the original ModelMeta
a=3

class A(object):
__metaclass__ = MyMeta # No __metaclass__ in parents: just instantiate
a=4
class B(A):
pass # MyMeta is not called until specified explicitly



# Make sure we did everything right
assert MyModel.a == 1
assert MyNewModel.a == 2
assert MyNewNewModel.a == 3
assert A.a == 4

# Make sure callback() worked
assert hasattr(MyModel, 'washere')
assert hasattr(MyNewModel, 'washere')
assert hasattr(MyNewNewModel, 'washere') # inherited
assert hasattr(A, 'washere')

assert MyModel.washere == 'MyModel!'
assert MyNewModel.washere == 'MyNewModel!'
assert MyNewNewModel.washere == 'MyNewModel!' # inherited, so unchanged
assert A.washere == 'A!'

最佳答案

一个类型只能有一个元类,因为一个元类只是说明类语句的作用——拥有多个是没有意义的。出于同样的原因,“链接”没有意义:第一个元类创建类型,那么第二个应该做什么?

您必须合并这两个元类(就像任何其他类一样)。但这可能会很棘手,尤其是当您并不真正了解他们的工作时。

class MyModelBase(type):
def __new__(cls, name, bases, attr):
attr['MyModelBase'] = 'was here'
return type.__new__(cls,name, bases, attr)

class MyMixin(type):
def __new__(cls, name, bases, attr):
attr['MyMixin'] = 'was here'
return type.__new__(cls, name, bases, attr)

class ChainedMeta(MyModelBase, MyMixin):
def __init__(cls, name, bases, attr):
# call both parents
MyModelBase.__init__(cls,name, bases, attr)
MyMixin.__init__(cls,name, bases, attr)

def __new__(cls, name, bases, attr):
# so, how is the new type supposed to look?
# maybe create the first
t1 = MyModelBase.__new__(cls, name, bases, attr)
# and pass it's data on to the next?
name = t1.__name__
bases = tuple(t1.mro())
attr = t1.__dict__.copy()
t2 = MyMixin.__new__(cls, name, bases, attr)
return t2

class Model(object):
__metaclass__ = MyModelBase # inherits from `ModelBase`

class MyModel(Model):
__metaclass__ = ChainedMeta

print MyModel.MyModelBase
print MyModel.MyMixin

正如您所看到的,这已经涉及一些猜测,因为您并不真正知道其他元类的作用。如果两个元类都非常简单,这可能可以工作,但我对这样的解决方案没有太大信心。

为合并多个基的元类编写一个元类作为练习留给读者;-P

关于python - 元类混合或链接?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4651729/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com