메타 쇠를 연결할 수 있습니까?Metaclass Mixin 또는 Chaining?
나는 그 네임 스페이스 딕셔너리를 처리하는 __metaclass__=ModelBase
를 사용하는 클래스 Model
있습니다. 나는 그걸 상속하여 다른 메타 클래스를 "묶어"원래의 그늘을 그늘지게하지 않을 것입니다.
첫 번째 방법은 class MyModelBase(ModelBase)
를 서브 클래 싱하는 것입니다
MyModel(Model):
__metaclass__ = MyModelBase # inherits from `ModelBase`
를하지만 명시 적으로 서브 클래 싱하지 않고, 단지 유지 mixin 같은 체인들을 수 있습니까? 더 나은
class MyModel(Model):
__metaclass__ = (MyMixin, super(Model).__metaclass__)
... 나 같은 뭔가 :
class MyModel(Model):
__metaclass__ = MyMetaMixin, # Automagically uses `Model.__metaclass__`
이유 : 더 많은 유연성을 위해 그것을 사용하는 클래스의 바로 부모로부터 __metaclass__
을 사용하는 믹스 인을 만들 기존 앱을 확장에서, 나는 Model
, Form
의 과정에 접선을위한 글로벌 메커니즘을 만들려면 ... 장고의 정의는 그래서 런타임에 변경 될 수 있습니다.
일반적인 메커니즘은 콜백이나 mixin 여러 메타 클래스를 구현하는 것보다 훨씬 더 좋을 것이다. 메타 클래스 MetaProxy
: 당신의 도움으로
나는 마침내 해결책을 마련 할 수 있었다.
아이디어입니다 : 부모 중 하나
#!/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!'
파이썬 3.4에서는 올바르게 어설 션 된 것처럼 보이지 않습니다. 113 행에 실패 ('MyModel'에는'washere' 속성이 없습니다.) – Joost