กรณีการใช้งาน (คอนกรีต) อะไรบ้างสำหรับแว่นตา?


118

ฉันมีเพื่อนคนหนึ่งที่ชอบใช้เมตาแว่นตาและเสนอวิธีแก้ปัญหาให้พวกเขาเป็นประจำ

ฉันคิดว่าคุณแทบไม่จำเป็นต้องใช้เมตาแว่นตาเลย ทำไม? เพราะฉันคิดว่าถ้าคุณทำอะไรแบบนั้นกับชั้นเรียนคุณน่าจะทำกับวัตถุ และการออกแบบ / refactor ขนาดเล็กเป็นไปตามลำดับ

ความสามารถในการใช้ metaclasses ทำให้ผู้คนจำนวนมากในหลาย ๆ แห่งใช้คลาสเป็นวัตถุอัตราสองซึ่งดูเหมือนจะเป็นหายนะสำหรับฉัน การเขียนโปรแกรมจะถูกแทนที่ด้วย meta-programming หรือไม่? การเพิ่มนักตกแต่งชั้นเรียนทำให้เป็นที่ยอมรับมากยิ่งขึ้น

ดังนั้นฉันอยากทราบกรณีการใช้งานที่ถูกต้อง (ที่เป็นรูปธรรม) ของคุณสำหรับ metaclasses ใน Python หรือเพื่อให้รู้แจ้งว่าทำไมคลาสการกลายพันธุ์จึงดีกว่าการกลายพันธุ์วัตถุในบางครั้ง

ฉันจะเริ่ม:

บางครั้งเมื่อใช้ไลบรารีของบุคคลที่สามจะมีประโยชน์ที่จะสามารถเปลี่ยนคลาสด้วยวิธีใดวิธีหนึ่ง

(นี่เป็นกรณีเดียวที่ฉันคิดได้และมันไม่เป็นรูปธรรม)


3
นี่เป็นคำถามที่ดี เมื่อพิจารณาจากคำตอบด้านล่างก็ค่อนข้างชัดเจนว่าไม่มีสิ่งที่เรียกว่าการใช้เมตาแว่นตาอย่างเป็นรูปธรรม
Marcus Ottosson

คำตอบ:


25

ฉันมีคลาสที่จัดการพล็อตแบบไม่โต้ตอบเป็นส่วนหน้าของ Matplotlib อย่างไรก็ตามในบางครั้งเราต้องการวางแผนเชิงโต้ตอบ ด้วยฟังก์ชั่นเพียงไม่กี่ฟังก์ชั่นฉันพบว่าฉันสามารถเพิ่มจำนวนตัวเลขเรียกวาดด้วยตนเอง ฯลฯ แต่ฉันต้องทำสิ่งเหล่านี้ก่อนและหลังการวางพล็อตทุกครั้ง ดังนั้นในการสร้างทั้งกระดาษห่อหุ้มพล็อตเชิงโต้ตอบและกระดาษห่อพล็อตการพล็อตนอกจอฉันพบว่าการทำสิ่งนี้ผ่านเมตาแว่นตามีประสิทธิภาพมากกว่าโดยใช้วิธีการที่เหมาะสมมากกว่าการทำสิ่งต่างๆเช่น:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

วิธีนี้ไม่สอดคล้องกับการเปลี่ยนแปลง API และอื่น ๆ แต่วิธีที่วนซ้ำในแอตทริบิวต์คลาส__init__ก่อนที่จะตั้งค่าแอตทริบิวต์คลาสใหม่จะมีประสิทธิภาพมากกว่าและทำให้สิ่งต่างๆเป็นปัจจุบันอยู่เสมอ:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

แน่นอนว่าอาจมีวิธีที่ดีกว่านี้ แต่ฉันพบว่าวิธีนี้ได้ผล แน่นอนว่าสิ่งนี้สามารถทำได้ใน__new__หรือ__init__แต่นี่เป็นวิธีแก้ปัญหาที่ฉันพบว่าตรงไปตรงมาที่สุด


103

เมื่อเร็ว ๆ นี้ฉันถูกถามคำถามเดียวกันและมีคำตอบหลายคำตอบ ฉันหวังว่าการฟื้นฟูเธรดนี้จะเป็นเรื่องปกติเนื่องจากฉันต้องการอธิบายรายละเอียดเกี่ยวกับกรณีการใช้งานบางส่วนที่กล่าวถึงและเพิ่มใหม่สองสามอย่าง

แว่นตาส่วนใหญ่ที่ฉันเคยเห็นทำหนึ่งในสองสิ่ง:

  1. การลงทะเบียน (การเพิ่มคลาสในโครงสร้างข้อมูล):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    เมื่อใดก็ตามที่คุณซับคลาสคลาสModelของคุณจะถูกลงทะเบียนในmodelsพจนานุกรม:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    สิ่งนี้สามารถทำได้ด้วยนักตกแต่งชั้นเรียน:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    หรือด้วยฟังก์ชันการลงทะเบียนอย่างชัดเจน:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    อันที่จริงสิ่งนี้ก็เหมือนกันมาก: คุณพูดถึงนักตกแต่งในชั้นเรียนอย่างไม่น่าเชื่อ แต่จริงๆแล้วมันไม่มีอะไรมากไปกว่าน้ำตาลวากยสัมพันธ์สำหรับการเรียกใช้ฟังก์ชันในชั้นเรียนดังนั้นจึงไม่มีความมหัศจรรย์ใด ๆ

    อย่างไรก็ตามข้อดีของ metaclasses ในกรณีนี้คือการถ่ายทอดทางพันธุกรรมเนื่องจากใช้กับคลาสย่อยใด ๆ ในขณะที่โซลูชันอื่นใช้ได้เฉพาะกับคลาสย่อยที่ได้รับการตกแต่งหรือลงทะเบียนอย่างชัดเจนเท่านั้น

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. การปรับโครงสร้างใหม่ (การแก้ไขแอตทริบิวต์คลาสหรือเพิ่มใหม่):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    เมื่อใดก็ตามที่คุณซับคลาสModelและกำหนดFieldแอตทริบิวต์บางอย่างพวกเขาจะถูกแทรกด้วยชื่อ (สำหรับข้อความแสดงข้อผิดพลาดที่ให้ข้อมูลเพิ่มเติมเป็นต้น) และจัดกลุ่มลงใน_fieldsพจนานุกรม (เพื่อให้ง่ายต่อการทำซ้ำโดยไม่ต้องดูแอตทริบิวต์คลาสทั้งหมดและคลาสพื้นฐานทั้งหมด ' แอตทริบิวต์ทุกครั้ง):

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    อีกครั้งสิ่งนี้สามารถทำได้ (โดยไม่ต้องสืบทอด) ด้วยมัณฑนากรชั้นเรียน:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    หรืออย่างชัดเจน:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    

    แม้ว่าในทางตรงกันข้ามกับการสนับสนุนของคุณสำหรับการเขียนโปรแกรมที่ไม่ใช่เมตาที่อ่านได้และบำรุงรักษาได้ แต่สิ่งนี้จะยุ่งยากซ้ำซ้อนและเกิดข้อผิดพลาดมากขึ้น:

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

เมื่อพิจารณาถึงกรณีการใช้งานที่พบบ่อยและเป็นรูปธรรมแล้วกรณีเดียวที่คุณต้องใช้เมตาคลาสสิกอย่างแน่นอนคือเมื่อคุณต้องการแก้ไขชื่อคลาสหรือรายการคลาสพื้นฐานเนื่องจากเมื่อกำหนดพารามิเตอร์เหล่านี้จะรวมอยู่ในคลาสและไม่มีมัณฑนากร หรือฟังก์ชันสามารถปลดล็อคได้

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

สิ่งนี้อาจมีประโยชน์ในเฟรมเวิร์กสำหรับการออกคำเตือนเมื่อใดก็ตามที่มีการกำหนดคลาสที่มีชื่อคล้ายกันหรือโครงสร้างการสืบทอดที่ไม่สมบูรณ์ แต่ฉันไม่สามารถนึกถึงเหตุผลที่นอกเหนือจากการหลอกล่อที่จะเปลี่ยนค่าเหล่านี้ได้ บางที David Beazley ก็ทำได้

อย่างไรก็ตามใน Python 3 metaclasses ยังมี__prepare__วิธีการซึ่งช่วยให้คุณสามารถประเมินเนื้อหาของคลาสในการแมปอื่นที่ไม่ใช่ a dictดังนั้นจึงรองรับแอตทริบิวต์ที่สั่งซื้อแอตทริบิวต์ที่โอเวอร์โหลดและสิ่งดีๆที่ชั่วร้ายอื่น ๆ :

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

คุณอาจโต้แย้งแอตทริบิวต์ที่สั่งซื้อได้โดยใช้ตัวนับการสร้างและสามารถจำลองการโอเวอร์โหลดได้ด้วยอาร์กิวเมนต์เริ่มต้น:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

นอกจากจะดูน่าเกลียดกว่ามากแล้วยังมีความยืดหยุ่นน้อยกว่า: ถ้าคุณต้องการสั่งแอตทริบิวต์ตามตัวอักษรเช่นจำนวนเต็มและสตริงล่ะ? เกิดอะไรขึ้นถ้าNoneเป็นค่าที่ถูกต้องสำหรับx?

นี่เป็นวิธีที่สร้างสรรค์ในการแก้ปัญหาแรก:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']

และนี่คือวิธีที่สร้างสรรค์ในการแก้ปัญหาข้อที่สอง:

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

แต่สิ่งนี้มีมากวูดูเอ้อมากกว่าเมตาคลาสสิกธรรมดา ๆ (โดยเฉพาะอย่างยิ่งอันแรกซึ่งทำให้สมองของคุณละลาย) ประเด็นของฉันคือคุณมองว่า metaclasses เป็นสิ่งที่ไม่คุ้นเคยและไม่ใช้งานง่าย แต่คุณยังสามารถมองว่ามันเป็นขั้นตอนต่อไปของวิวัฒนาการในภาษาโปรแกรม: คุณต้องปรับความคิดของคุณ ท้ายที่สุดคุณอาจทำทุกอย่างใน C ได้รวมถึงการกำหนดโครงสร้างด้วยตัวชี้ฟังก์ชันและส่งต่อเป็นอาร์กิวเมนต์แรกไปยังฟังก์ชันของมัน คนที่เห็น C ++ เป็นครั้งแรกอาจพูดว่า "เวทมนตร์นี้คืออะไรเหตุใดคอมไพเลอร์จึงส่งผ่านthisถึงวิธีการ แต่ไม่ใช่ฟังก์ชันปกติและแบบคงที่? เป็นการดีกว่าที่จะพูดอย่างชัดเจนและละเอียดเกี่ยวกับข้อโต้แย้งของคุณ "แต่แล้วการเขียนโปรแกรมเชิงวัตถุจะมีประสิทธิภาพมากขึ้นเมื่อคุณได้รับมันและนี่คือสิ่งนี้เอ่อ ... เข้าใจ metaclasses จริงๆแล้วมันง่ายมากทำไมไม่ใช้เมื่อสะดวกล่ะ?

และในที่สุด metaclasses ก็เป็น rad และการเขียนโปรแกรมควรเป็นเรื่องสนุก การใช้โครงสร้างการเขียนโปรแกรมมาตรฐานและรูปแบบการออกแบบตลอดเวลาเป็นเรื่องน่าเบื่อและไม่น่าสนใจและขัดขวางจินตนาการของคุณ อยู่น้อย! นี่คือ Metametaclass สำหรับคุณโดยเฉพาะ

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan

แก้ไข

นี่เป็นคำถามที่ค่อนข้างเก่า แต่ก็ยังได้รับการโหวตเพิ่มขึ้นดังนั้นฉันจึงคิดว่าฉันจะเพิ่มลิงก์ไปยังคำตอบที่ครอบคลุมมากขึ้น หากคุณต้องการอ่านเพิ่มเติมเกี่ยวกับ metaclasses และการใช้งานของพวกเขาผมเพิ่งเผยแพร่บทความเกี่ยวกับเรื่องที่นี่


5
นั่นเป็นคำตอบที่ยอดเยี่ยมขอบคุณสำหรับเวลาเขียนและให้ตัวอย่างมากมาย
Chen A.

"... ข้อดีของ metaclasses ในกรณีนี้คือการถ่ายทอดทางพันธุกรรมเนื่องจากใช้กับคลาสย่อยใด ๆ " - ไม่ใช่ใน Python 3 ฉันคิดว่า? ฉันคิดว่ามันใช้งานได้ใน Python 2 เท่านั้นเนื่องจากคลาสลูกใด ๆ สืบทอด__metaclass__แอตทริบิวต์ แต่แอตทริบิวต์นี้ไม่พิเศษอีกต่อไปใน Python 3 มีวิธีใดบ้างที่จะทำให้ "คลาสเด็กสร้างขึ้นโดย metaclass ของผู้ปกครอง" ทำงานใน Python 3 ?
ForceBru

2
นี่เป็นความจริงสำหรับ Python 3 เช่นกันเนื่องจากคลาส B ที่สืบทอดมาจาก A ซึ่งมี metaclass คือ M ก็เป็นประเภท M เช่นกันดังนั้นเมื่อ B ได้รับการประเมิน M จะถูกเรียกให้สร้างและสิ่งนี้จะช่วยให้คุณได้อย่างมีประสิทธิภาพ เพื่อ "ทำงานกับคลาสย่อยใด ๆ " (จาก A) ต้องบอกว่า Python 3.6 นำเสนอสิ่งที่ง่ายกว่ามากinit_subclassดังนั้นตอนนี้คุณสามารถจัดการคลาสย่อยในคลาสพื้นฐานได้แล้วและไม่จำเป็นต้องใช้เมตาคลาสอีกต่อไปเพื่อจุดประสงค์นั้น
Dan Gittik

นี่ยอดเยี่ยมมากฉันอ่านบล็อกโพสต์มากมายเกี่ยวกับเมตาคลาสสิกมีเพียงบทความนี้เท่านั้นที่รู้ข้อดีข้อเสียและทางเลือกอื่น ๆ ของเมตาคลาส
ospider

36

จุดประสงค์ของ metaclasses ไม่ได้ต้องการแทนที่ความแตกต่างของคลาส / อ็อบเจ็กต์ด้วย metaclass / class แต่เพื่อเปลี่ยนพฤติกรรมของนิยามคลาส (และอินสแตนซ์ของมัน) ไม่ทางใดก็ทางหนึ่ง มีประสิทธิภาพในการปรับเปลี่ยนพฤติกรรมของคำสั่งคลาสในรูปแบบที่อาจเป็นประโยชน์สำหรับโดเมนเฉพาะของคุณมากกว่าค่าเริ่มต้น สิ่งที่ฉันเคยใช้คือ:

  • การติดตามคลาสย่อยมักจะลงทะเบียนตัวจัดการ สิ่งนี้มีประโยชน์เมื่อใช้การตั้งค่ารูปแบบปลั๊กอินซึ่งคุณต้องการลงทะเบียนตัวจัดการสำหรับสิ่งใดสิ่งหนึ่งเพียงแค่การคลาสย่อยและตั้งค่าแอตทริบิวต์คลาสเล็กน้อย เช่น. สมมติว่าคุณเขียนตัวจัดการสำหรับรูปแบบเพลงต่างๆโดยแต่ละคลาสจะใช้วิธีการที่เหมาะสม (เล่น / รับแท็ก ฯลฯ ) สำหรับประเภทของมัน การเพิ่มตัวจัดการสำหรับประเภทใหม่จะกลายเป็น:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here

    จากนั้น metaclass จะเก็บรักษาพจนานุกรม{'.mp3' : MP3File, ... }ฯลฯ และสร้างอ็อบเจ็กต์ในประเภทที่เหมาะสมเมื่อคุณร้องขอตัวจัดการผ่านฟังก์ชันโรงงาน

  • เปลี่ยนพฤติกรรม. คุณอาจต้องการแนบความหมายพิเศษกับแอตทริบิวต์บางอย่างซึ่งส่งผลให้พฤติกรรมเปลี่ยนแปลงเมื่อมีอยู่ ตัวอย่างเช่นคุณอาจต้องการค้นหาวิธีการที่มีชื่อ_get_fooและ_set_fooแปลงเป็นคุณสมบัติอย่างโปร่งใส เพื่อเป็นตัวอย่างในโลกแห่งความเป็นจริงนี่คือสูตรอาหารที่ฉันเขียนเพื่อให้คำจำกัดความของโครงสร้างแบบ C มากขึ้น metaclass ถูกใช้เพื่อแปลงรายการที่ประกาศเป็นสตริงรูปแบบโครงสร้างการจัดการการสืบทอด ฯลฯ และสร้างคลาสที่สามารถจัดการกับมันได้

    สำหรับตัวอย่างจริงของโลกอื่น ๆ ให้ดูที่ ORMs ต่างๆเช่นsqlalchemy ของออมหรือSQLObject อีกครั้งวัตถุประสงค์คือการตีความนิยาม (ที่นี่คำจำกัดความคอลัมน์ SQL) ด้วยความหมายเฉพาะ


3
ใช่การติดตามคลาสย่อย แต่ทำไมคุณถึงต้องการเช่นนั้น? ตัวอย่างของคุณเป็นเพียงนัยสำหรับ register_music_file (Mp3File, ['.mp3']) และวิธีที่ชัดเจนคืออ่านและบำรุงรักษาได้มากกว่า นี่คือตัวอย่างกรณีเลวร้ายที่ฉันกำลังพูดถึง
Ali Afshar

เกี่ยวกับกรณี ORM คุณกำลังพูดถึงวิธีการกำหนดตารางตามคลาสหรือ metaclasses บนวัตถุที่แมป เนื่องจาก SQLAlchemy สามารถ (อย่างถูกต้อง) แมปกับคลาสใดก็ได้ (และฉันคิดว่ามันไม่ได้ใช้เมตาคลาสสำหรับกิจกรรมนั้น)
Ali Afshar

10
ฉันชอบรูปแบบที่เปิดเผยมากกว่าต้องการวิธีการลงทะเบียนเพิ่มเติมสำหรับทุกคลาสย่อย - จะดีกว่าถ้าทุกอย่างรวมอยู่ในตำแหน่งเดียว
Brian

สำหรับ sqlalchemy ฉันคิดว่าส่วนใหญ่เป็นเลเยอร์ที่เปิดเผยดังนั้น sqlobject อาจเป็นตัวอย่างที่ดีกว่า อย่างไรก็ตาม metaclasses ที่ใช้ภายในยังเป็นตัวอย่างของการตีความคุณลักษณะเฉพาะที่คล้ายคลึงกันเพื่อประกาศความหมาย
Brian

2
ขออภัยการสนทนาของฉันหายไปในสถานการณ์การหมดเวลา SO ฉันพบชั้นเรียนสำหรับการประกาศที่เกือบจะเป็นสิ่งที่น่ารังเกียจ ฉันรู้ว่าผู้คนรักมันและเป็นพฤติกรรมที่ยอมรับได้ แต่ (จากประสบการณ์) ฉันรู้ว่ามันใช้ไม่ได้ในสถานการณ์ที่คุณต้องการประกาศ UN ยกเลิกการลงทะเบียนชั้นเรียนเป็นเรื่องยาก
Ali Afshar

17

เริ่มต้นด้วยคำพูดคลาสสิกของ Tim Peter:

Metaclasses เป็นเวทมนตร์ที่ล้ำลึกกว่า 99% ของผู้ใช้ที่ควรกังวล หากคุณสงสัยว่าคุณต้องการหรือไม่คุณไม่ต้องการ (คนที่ต้องการพวกเขาจริงๆรู้ด้วยความมั่นใจว่าพวกเขาต้องการพวกเขาและไม่ต้องการคำอธิบายว่าทำไม) ทิมปีเตอร์ส (clp post 2002-12-22)

ต้องบอกว่าฉันได้ใช้ metaclasses อย่างแท้จริง (เป็นระยะ ๆ ) สิ่งที่อยู่ในใจคือใน Django ที่โมเดลทั้งหมดของคุณสืบทอดมาจากโมเดล โมเดลในทางกลับกันนางแบบก็ใช้เวทมนตร์ที่ร้ายแรงเพื่อห่อหุ้มโมเดล DB ของคุณด้วยความดีงามของ Django ความมหัศจรรย์นั้นเกิดขึ้นโดยวิธีการของแว่นตา มันสร้างคลาสข้อยกเว้นคลาสผู้จัดการและอื่น ๆ ทั้งหมด

ดู django / db / models / base.py คลาส ModelBase () สำหรับจุดเริ่มต้นของเรื่องราว


ใช่ฉันเห็นประเด็นแล้ว ฉันไม่สงสัยว่า "อย่างไร" หรือ "ทำไม" ถึงใช้เมตาแว่นตาฉันสงสัยว่า "ใคร" และ "อะไร" ORM เป็นกรณีทั่วไปที่ฉันเห็นที่นี่ น่าเสียดายที่ ORM ของ Django ค่อนข้างแย่เมื่อเทียบกับ SQLAlchemy ซึ่งมีเวทมนตร์น้อยกว่า เวทมนตร์เป็นสิ่งที่ไม่ดีและแว่นตาก็ไม่จำเป็นสำหรับสิ่งนี้
Ali Afshar

9
เมื่ออ่านคำพูดของ Tim Peters ในอดีตเวลาได้แสดงให้เห็นว่าคำพูดของเขาค่อนข้างไม่เป็นประโยชน์ จนกระทั่งการค้นคว้า Python metaclasses ที่นี่ใน StackOverflow ก็เห็นได้ชัดว่าจะใช้งานได้อย่างไร หลังจากบังคับตัวเองให้เรียนรู้วิธีการเขียนและใช้เมตาคลาสสิกความสามารถของพวกเขาทำให้ฉันประหลาดใจและทำให้ฉันเข้าใจมากขึ้นว่า Python ทำงานอย่างไร ชั้นเรียนสามารถให้รหัสที่ใช้ซ้ำได้และ metaclasses สามารถให้การปรับปรุงที่ใช้ซ้ำได้สำหรับชั้นเรียนเหล่านั้น
Noctis Skytower

6

Metaclasses มีประโยชน์สำหรับการสร้างภาษาเฉพาะโดเมนใน Python ตัวอย่างที่เป็นรูปธรรม ได้แก่ Django ซึ่งเป็นไวยากรณ์ที่เปิดเผยของสคีมาตาฐานข้อมูลของ SQLObject

ตัวอย่างพื้นฐานจากA Conservative Metaclassโดย Ian Bicking:

metaclasses ที่ฉันใช้เป็นหลักเพื่อสนับสนุนรูปแบบการเขียนโปรแกรมที่เปิดเผย ตัวอย่างเช่นพิจารณาสคีมาการตรวจสอบความถูกต้อง:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

เทคนิคอื่น ๆ : ส่วนผสมสำหรับการสร้าง DSL ใน Python (pdf)

แก้ไข (โดย Ali): ตัวอย่างของการทำสิ่งนี้โดยใช้คอลเลกชันและอินสแตนซ์คือสิ่งที่ฉันต้องการ ข้อเท็จจริงที่สำคัญคืออินสแตนซ์ซึ่งทำให้คุณมีอำนาจมากขึ้นและไม่ต้องใช้เหตุผลในการใช้แว่นตา ที่น่าสังเกตว่าตัวอย่างของคุณใช้คลาสและอินสแตนซ์ผสมกันซึ่งเป็นข้อบ่งชี้อย่างแน่นอนว่าคุณไม่สามารถทำได้ทั้งหมดด้วยเมตาคลาสสิก และสร้างวิธีการทำที่ไม่เหมือนกันอย่างแท้จริง

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]

มันไม่สมบูรณ์แบบ แต่มีเวทมนตร์เกือบเป็นศูนย์ไม่จำเป็นต้องใช้เมตาแกรมและปรับปรุงความสม่ำเสมอ


ขอบคุณสำหรับสิ่งนี้. นี่เป็นตัวอย่างที่ดีมากของกรณีการใช้งานที่ฉันคิดว่าไม่จำเป็นน่าเกลียดและไม่สามารถจัดการได้ซึ่งจะง่ายกว่าโดยใช้อินสแตนซ์การรวบรวมแบบธรรมดา (โดยมีคอลเลกชันที่ซ้อนกันตามต้องการ)
Ali Afshar

1
@Ali A: คุณสามารถเสนอตัวอย่างที่เป็นรูปธรรมของการเปรียบเทียบแบบเคียงข้างกันระหว่างไวยากรณ์ที่เปิดเผยผ่านเมตาคลาสสิกและวิธีการที่อิงจากอินสแตนซ์การรวบรวมอย่างง่าย
jfs

@Ali A: คุณสามารถแก้ไขคำตอบของฉันได้เพื่อเพิ่มตัวอย่างสไตล์คอลเลกชัน
jfs

โอเคเสร็จแล้ว ขออภัยวันนี้รีบไปหน่อย แต่จะพยายามตอบคำถามในภายหลัง / พรุ่งนี้ สุขสันต์วันหยุด!
Ali Afshar

2
ตัวอย่างที่สองน่าเกลียดเนื่องจากคุณต้องผูกอินสแตนซ์ตัวตรวจสอบความถูกต้องกับชื่อของพวกเขา วิธีที่ดีกว่าเล็กน้อยในการทำเช่นนี้คือการใช้พจนานุกรมแทนรายการ แต่ในชั้นเรียน python นั้นเป็นเพียงไวยากรณ์น้ำตาลสำหรับพจนานุกรมดังนั้นทำไมไม่ใช้คลาสล่ะ คุณจะได้รับการตรวจสอบชื่อฟรีเช่นกันเนื่องจาก python babes ไม่สามารถมีช่องว่างหรืออักขระพิเศษที่สตริงสามารถทำได้
Lie Ryan

6

รูปแบบการใช้ metaclass ที่สมเหตุสมผลคือการทำอะไรบางอย่างครั้งเดียวเมื่อมีการกำหนดคลาสแทนที่จะทำซ้ำเมื่อใดก็ตามที่มีการสร้างอินสแตนซ์คลาสเดียวกัน

เมื่อหลายชั้นเรียนมีพฤติกรรมพิเศษเหมือนกันการทำซ้ำ__metaclass__=Xจะดีกว่าการทำซ้ำรหัสวัตถุประสงค์พิเศษและ / หรือการแนะนำคลาสพิเศษที่ใช้ร่วมกันเฉพาะกิจ

แต่แม้จะมีเพียงหนึ่งชั้นเรียนพิเศษและไม่มีส่วนขยายอันใกล้ __new__และ__init__ของ metaclass มีวิธีทำความสะอาดเพื่อเริ่มต้นตัวแปรระดับหรือข้อมูลอื่น ๆ ทั่วโลกกว่า intermixing รหัสวัตถุประสงค์พิเศษและปกติdefและclassงบในร่างกายการกำหนดชั้น


5

ครั้งเดียวที่ฉันใช้ metaclasses ใน Python คือเมื่อเขียน Wrapper สำหรับ Flickr API

เป้าหมายของฉันคือการขูดไซต์ API ของ Flickrและสร้างลำดับชั้นของคลาสที่สมบูรณ์แบบไดนามิกเพื่ออนุญาตให้เข้าถึง API โดยใช้วัตถุ Python:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

ดังนั้นในตัวอย่างนั้นเนื่องจากฉันสร้าง Python Flickr API ทั้งหมดจากเว็บไซต์ฉันไม่รู้คำจำกัดความของคลาสที่รันไทม์ ความสามารถในการสร้างประเภทแบบไดนามิกมีประโยชน์มาก


2
คุณสามารถสร้างประเภทแบบไดนามิกโดยไม่ต้องใช้ metaclasses >>> help (type)
Ali Afshar

8
แม้ว่าคุณจะไม่รู้ตัว แต่คุณก็ใช้เมตาแว่นตาอยู่แล้ว type เป็น metaclass ซึ่งเป็นประเภทที่พบมากที่สุด :-)
Veky

5

ฉันคิดแบบเดียวกันเมื่อวานนี้และเห็นด้วยอย่างยิ่ง ความซับซ้อนในรหัสที่เกิดจากความพยายามที่จะทำให้มีการเปิดเผยมากขึ้นโดยทั่วไปทำให้ codebase ยากต่อการบำรุงรักษาอ่านยากและ pythonic น้อยลงในความคิดของฉัน นอกจากนี้ยังต้องใช้ copy.copy () ing จำนวนมาก (เพื่อรักษาการสืบทอดและคัดลอกจากคลาสหนึ่งไปยังอินสแตนซ์) และหมายความว่าคุณต้องดูในหลาย ๆ ที่เพื่อดูว่าเกิดอะไรขึ้น (มองจาก metaclass ขึ้นไปเสมอ) ซึ่งขัดกับ เมล็ดหลามยัง ฉันเลือกผ่าน formencode และ sqlalchemy code เพื่อดูว่ารูปแบบที่เปิดเผยนั้นคุ้มค่าหรือไม่และไม่ชัดเจน ควรปล่อยให้รูปแบบดังกล่าวเป็นตัวอธิบาย (เช่นคุณสมบัติและวิธีการ) และข้อมูลที่ไม่เปลี่ยนรูป Ruby ได้รับการสนับสนุนที่ดีขึ้นสำหรับรูปแบบการประกาศดังกล่าวและฉันดีใจที่ภาษา python หลักไม่ได้ไปตามเส้นทางนั้น

ฉันสามารถเห็นการใช้งานของพวกเขาสำหรับการดีบักเพิ่ม metaclass ในคลาสพื้นฐานทั้งหมดของคุณเพื่อรับข้อมูลที่สมบูรณ์ยิ่งขึ้น ฉันยังเห็นการใช้งานของพวกเขาในโครงการขนาดใหญ่ (มาก) เพื่อกำจัดโค้ดสำเร็จรูปบางส่วน (แต่เมื่อสูญเสียความชัดเจน) ตัวอย่างเช่น sqlalchemy ใช้ที่อื่นเพื่อเพิ่มวิธีการที่กำหนดเองโดยเฉพาะให้กับคลาสย่อยทั้งหมดตามค่าแอตทริบิวต์ในนิยามคลาสเช่นตัวอย่างของเล่น

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

อาจมีเมตาคลาสที่สร้างเมธอดในคลาสนั้นด้วยคุณสมบัติพิเศษตาม "สวัสดี" (พูดวิธีที่เพิ่ม "สวัสดี" ต่อท้ายสตริง) อาจเป็นการดีสำหรับการบำรุงรักษาเพื่อให้แน่ใจว่าคุณไม่ต้องเขียนเมธอดในทุกคลาสย่อยที่คุณทำแทนสิ่งที่คุณต้องกำหนดคือ method_maker_value

ความต้องการนี้หายากมากและลดการพิมพ์ลงเล็กน้อยซึ่งไม่คุ้มค่าที่จะพิจารณาเว้นแต่คุณจะมี codebase ที่ใหญ่พอ


5

คุณไม่จำเป็นต้องใช้ metaclass อย่างแน่นอนเนื่องจากคุณสามารถสร้างคลาสที่ทำในสิ่งที่คุณต้องการโดยใช้การสืบทอดหรือการรวมคลาสที่คุณต้องการแก้ไข

กล่าวได้ว่ามันมีประโยชน์มากใน Smalltalk และ Ruby ที่จะสามารถปรับเปลี่ยนคลาสที่มีอยู่ได้ แต่ Python ไม่ต้องการทำเช่นนั้นโดยตรง

มีบทความ DeveloperWorks ที่ยอดเยี่ยมเกี่ยวกับ metaclassing ใน Python ที่อาจช่วยได้ บทความวิกิพีเดียยังเป็นรักที่ดี


1
คุณไม่จำเป็นต้องใช้วัตถุในการเขียนโปรแกรมเชิงวัตถุ - คุณสามารถทำได้ด้วยฟังก์ชันระดับเฟิร์สคลาส คุณจึงไม่จำเป็นต้องใช้วัตถุ แต่พวกเขาอยู่ที่นั่นเพื่อความสะดวก ฉันไม่แน่ใจว่าคุณกำลังพยายามทำประเด็นใดในย่อหน้าแรก
Tyler Crompton

1
ย้อนดูคำถาม
Charlie Martin

4

Metaclasses ไม่ได้แทนที่การเขียนโปรแกรม! เป็นเพียงเคล็ดลับที่สามารถทำให้งานบางอย่างเป็นไปโดยอัตโนมัติหรือสวยงามมากขึ้น ตัวอย่างที่ดีของสิ่งนี้คือไลบรารีไฮไลต์ไวยากรณ์ของPy เซ็กเมนต์ มีคลาสที่เรียกว่าRegexLexerซึ่งช่วยให้ผู้ใช้กำหนดชุดของกฎ lexing เป็นนิพจน์ทั่วไปในคลาส metaclass ถูกใช้เพื่อเปลี่ยนคำจำกัดความให้เป็นตัวแยกวิเคราะห์ที่มีประโยชน์

พวกมันเหมือนเกลือ มันง่ายที่จะใช้มากเกินไป


ในความคิดของฉันเคส Py เซ็กเมนต์นั้นไม่จำเป็น ทำไมไม่แค่มีคอลเลกชันธรรมดา ๆ เช่น dict ทำไมต้องบังคับให้ชั้นเรียนทำเช่นนี้
Ali Afshar

4
เนื่องจากคลาสที่ดีจะสรุปความคิดของ Lexer และมีวิธีการที่มีประโยชน์อื่น ๆ เช่น guess_filename () เป็นต้น
Benjamin Peterson

4

ไลบรารี GUI บางไลบรารีมีปัญหาเมื่อหลายเธรดพยายามโต้ตอบกับไลบรารีเหล่านี้ tkinterเป็นตัวอย่างหนึ่ง และในขณะที่เราสามารถจัดการกับปัญหาเกี่ยวกับเหตุการณ์และคิวได้อย่างชัดเจน แต่การใช้ไลบรารีในลักษณะที่ไม่สนใจปัญหาโดยสิ้นเชิงก็ทำได้ง่ายกว่ามาก ดูเถิด - ความมหัศจรรย์ของแว่นตา

ความสามารถในการเขียนซ้ำทั้งไลบรารีแบบไดนามิกได้อย่างราบรื่นเพื่อให้ทำงานได้อย่างถูกต้องตามที่คาดไว้ในแอปพลิเคชันมัลติเธรดจะมีประโยชน์อย่างมากในบางสถานการณ์ safetkinterโมดูลไม่ว่าด้วยความช่วยเหลือของ metaclass ให้โดยที่threadboxโมดูล - เหตุการณ์และไม่จำเป็นต้องรอคิว

ลักษณะที่เรียบร้อยอย่างหนึ่งthreadboxคือไม่สนใจว่าจะโคลนคลาสใด มันเป็นตัวอย่างของวิธีการที่คลาสพื้นฐานทั้งหมดสามารถสัมผัสด้วยเมตาคลาสได้หากจำเป็น ประโยชน์เพิ่มเติมที่มาพร้อมกับ metaclasses คือพวกเขาทำงานในคลาสที่สืบทอดเช่นกัน โปรแกรมที่เขียนเอง - ทำไมไม่?


4

กรณีการใช้งาน metaclass ที่ถูกต้องตามกฎหมายเพียงอย่างเดียวคือเพื่อป้องกันไม่ให้นักพัฒนาซอฟต์แวร์รายอื่นแตะต้องโค้ดของคุณ เมื่อนักพัฒนาที่มีจมูกเป็นผู้เชี่ยวชาญด้านเมตาคลาสสิกและเริ่มแหย่เล่นกับคุณแล้วให้โยนอีกระดับหนึ่งหรือสองระดับเพื่อไม่ให้มันหลุดออกไป หากไม่ได้ผลให้เริ่มใช้type.__new__หรืออาจใช้แผนภาพบางอย่างโดยใช้เมตาคลาสแบบเรียกซ้ำ

(เขียนลิ้นในแก้ม แต่ฉันเคยเห็นการทำให้สับสนแบบนี้เสร็จแล้ว Django เป็นตัวอย่างที่สมบูรณ์แบบ)


7
ฉันไม่แน่ใจว่าแรงจูงใจใน Django เหมือนกัน
Ali Afshar

3

วิธีที่ฉันใช้ metaclasses คือการระบุคุณลักษณะบางอย่างให้กับชั้นเรียน ยกตัวอย่าง:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

จะใส่แอตทริบิวต์ชื่อในทุกคลาสที่จะมีการตั้งค่าเมตาคลาสให้ชี้ไปที่ NameClass


5
ใช่มันใช้งานได้ คุณสามารถใช้ซูเปอร์คลาสได้เช่นกันซึ่งอย่างน้อยก็ชัดเจนและสามารถติดตามได้ในโค้ด ไม่สนใจคุณใช้สิ่งนี้เพื่ออะไร?
Ali Afshar

2

นี่เป็นการใช้งานเล็กน้อย แต่ ... สิ่งหนึ่งที่ฉันพบว่า metaclasses มีประโยชน์คือการเรียกใช้ฟังก์ชันเมื่อใดก็ตามที่มีการสร้างคลาสย่อย ฉันประมวลกฎหมายนี้ใน metaclass ซึ่งมีลักษณะสำหรับ__initsubclass__แอตทริบิวต์: เมื่อใดก็ตามที่เป็น subclass __initsubclass__(cls, subcls)ถูกสร้างขึ้นทุกชั้นเรียนผู้ปกครองที่กำหนดวิธีการที่จะเรียกด้วย สิ่งนี้อนุญาตให้สร้างคลาสแม่ซึ่งจะลงทะเบียนคลาสย่อยทั้งหมดกับรีจิสตรีส่วนกลางบางตัวเรียกใช้การตรวจสอบคลาสย่อยที่ไม่สม่ำเสมอเมื่อใดก็ตามที่มีการกำหนดดำเนินการการเชื่อมโยงล่าช้า ฯลฯ ทั้งหมดนี้โดยไม่ต้องเรียกใช้ฟังก์ชันด้วยตนเองหรือสร้างเมตาคลาสที่กำหนดเองที่ ปฏิบัติหน้าที่แยกกันเหล่านี้

โปรดทราบว่าฉันค่อยๆตระหนักถึงความมหัศจรรย์โดยนัยของพฤติกรรมนี้ค่อนข้างไม่เป็นที่พึงปรารถนาเนื่องจากเป็นเรื่องที่ไม่คาดคิดหากมองไปที่คำจำกัดความของคลาสที่ไม่อยู่ในบริบท ... ดังนั้นฉันจึงย้ายออกจากการใช้โซลูชันนั้นสำหรับสิ่งที่ร้ายแรงนอกเหนือจาก การเตรียมใช้งาน__superแอตทริบิวต์สำหรับแต่ละคลาสและอินสแตนซ์


1

เมื่อเร็ว ๆ นี้ฉันต้องใช้เมตาคลาสเพื่อช่วยในการกำหนดโมเดล SQLAlchemy อย่างเปิดเผยรอบ ๆ ตารางฐานข้อมูลที่มีข้อมูลสำมะโนประชากรของสหรัฐอเมริกาจากhttp://census.ire.org/data/bulkdata.html

IRE จัดเตรียมเชลล์ฐานข้อมูลสำหรับตารางข้อมูลสำมะโนประชากรซึ่งสร้างคอลัมน์จำนวนเต็มตามหลักการตั้งชื่อจากสำนักสำรวจสำมะโนประชากรของ p012015, p012016, p012017 เป็นต้น

ฉันต้องการ a) สามารถเข้าถึงคอลัมน์เหล่านี้โดยใช้model_instance.p012017ไวยากรณ์ b) ค่อนข้างชัดเจนเกี่ยวกับสิ่งที่ฉันกำลังทำอยู่และ c) ไม่จำเป็นต้องกำหนดฟิลด์จำนวนมากบนโมเดลอย่างชัดเจนดังนั้นฉันจึงจัดคลาสย่อยของ SQLAlchemy DeclarativeMetaเพื่อทำซ้ำในช่วงของ คอลัมน์และสร้างฟิลด์โมเดลที่สอดคล้องกับคอลัมน์โดยอัตโนมัติ:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

จากนั้นฉันสามารถใช้เมตาคลาสนี้สำหรับนิยามโมเดลของฉันและเข้าถึงฟิลด์ที่แจกแจงโดยอัตโนมัติบนโมเดล:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...


0

ฉันต้องใช้ครั้งเดียวสำหรับตัวแยกวิเคราะห์ไบนารีเพื่อให้ใช้งานได้ง่ายขึ้น คุณกำหนดคลาสข้อความด้วยแอตทริบิวต์ของเขตข้อมูลที่อยู่บนสาย พวกเขาจำเป็นต้องได้รับคำสั่งในลักษณะที่พวกเขาประกาศให้สร้างรูปแบบลวดสุดท้ายจากมัน คุณสามารถทำได้ด้วย metaclasses หากคุณใช้เนมสเปซตามลำดับ ในความเป็นจริงมันอยู่ในตัวอย่างของ Metaclasses:

https://docs.python.org/3/reference/datamodel.html#metaclass-example

แต่โดยทั่วไปแล้ว: ประเมินอย่างรอบคอบหากคุณต้องการความซับซ้อนที่เพิ่มเข้ามาของ metaclasses จริงๆ


0

คำตอบจาก @Dan Gittik เจ๋งมาก

ตัวอย่างในตอนท้ายสามารถชี้แจงหลายสิ่งหลายอย่างฉันเปลี่ยนเป็น python 3 และให้คำอธิบาย:

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls

        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan
  • ทุกอย่างเป็นวัตถุดังนั้นคลาสจึงเป็นวัตถุ
  • คลาสอ็อบเจ็กต์ถูกสร้างขึ้นโดย metaclass
  • คลาสทั้งหมดที่สืบทอดมาจาก type คือ metaclass
  • metaclass สามารถควบคุมการสร้างคลาสได้
  • metaclass สามารถควบคุมการสร้าง metaclass ได้เช่นกัน (ดังนั้นจึงสามารถวนซ้ำได้ตลอดไป)
  • metaprograming นี้ ... คุณสามารถควบคุมระบบประเภทในขณะทำงานได้
  • อีกครั้งทุกอย่างเป็นวัตถุนี่คือระบบที่เหมือนกันพิมพ์ประเภทสร้างและพิมพ์สร้างอินสแตนซ์

0

การใช้งานอีกกรณีหนึ่งคือเมื่อคุณต้องการปรับเปลี่ยนแอตทริบิวต์ระดับคลาสและต้องแน่ใจว่ามีผลกับอ็อบเจ็กต์ที่อยู่ในมือเท่านั้น ในทางปฏิบัตินี่หมายถึง "การรวม" ขั้นตอนของการสร้างอินสแตนซ์ของเมตาคลาสและคลาสดังนั้นคุณจึงจัดการกับอินสแตนซ์คลาสของประเภท (เฉพาะ) ของตนเองเท่านั้น

ฉันยังมีการทำเมื่อ (สำหรับความกังวลของreadibilityและpolymorphism ) เราต้องการที่จะแบบไดนามิกกำหนด property s ซึ่งกลับค่า (พฤษภาคม) เป็นผลมาจากการคำนวณบนพื้นฐาน (มักจะมีการเปลี่ยนแปลง) คุณลักษณะเช่นระดับซึ่งสามารถทำได้เฉพาะในระดับชั้นเรียน , คือหลังจาก instantiation metaclass และก่อนการเริ่มชั้นเรียน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.