python - ما هو ميتاكلاس في بايثون؟


ما هي النظارات الطبية؟ ماذا يمكنك استخدامها ل؟



Answers



A ميتاكلاس هو فئة من فئة. وكما هو الحال في الفصل الدراسي، يعرف كيف يتصرف مثال من الصف، يعرف ميتاكلاس كيف تتصرف الطبقة. الفئة هي مثيل ميتاكلاس.

أثناء وجودك في بيثون، يمكنك استخدام المتصلين التعسفيين للأغراض الطبية (مثل عروض جروب )، والنهج الأكثر فائدة هو في الواقع لجعله فئة الفعلية نفسها. type هو ميتاكلاس المعتاد في بيثون. في حال كنت أتساءل، نعم، type هو في حد ذاته فئة، وأنه هو نوعه الخاص. لن تكون قادرا على إعادة شيء مثل type بحتة في بيثون، ولكن بيثون غش قليلا. لإنشاء ميتاكلاس الخاصة بك في بايثون كنت حقا تريد فقط type الفئة الفرعية.

يستخدم ميتاكلاس الأكثر شيوعا كمصنع فئة. مثل إنشاء مثيل من الطبقة عن طريق استدعاء الصف، بيثون يخلق فئة جديدة (عندما ينفذ "الطبقة" بيان) من خلال استدعاء ميتاكلاس. جنبا إلى جنب مع أساليب __init__ و __new__ العادية، وبالتالي يسمح للأسطح القيام "أشياء إضافية" عند إنشاء فئة، مثل تسجيل فئة جديدة مع بعض التسجيل، أو حتى استبدال الطبقة مع شيء آخر تماما.

عند تنفيذ بيان class ، يقوم بيثون أولا بتنفيذ نص بيان class ككتلة عادية من التعليمات البرمجية. مساحة الاسم الناتجة (a ديكت) يحمل سمات الفئة إلى أن يكون. يتم تحديد ميتاكلاس من خلال النظر في باسكلاسيس من فئة إلى أن (هي ورثت الأضلاع)، في السمة __metaclass__ من فئة إلى أن (إن وجدت) أو المتغير العالمي __metaclass__ . ثم يتم استدعاء ميتاكلاس مع الاسم والقواعد والصفات من الطبقة لتحقيق ذلك.

ومع ذلك، فإن النظارات الطبية تعرف في الواقع نوع الطبقة، وليس مجرد مصنع لذلك، لذلك يمكنك أن تفعل أكثر من ذلك بكثير معهم. يمكنك، على سبيل المثال، تحديد الطرق العادية على ميتاكلاس. هذه الأساليب ميتاكلاس مثل كلاسميثودس، في أنه يمكن استدعاؤها على الطبقة دون مثيل، لكنها أيضا ليست مثل كلاسثودس في أنها لا يمكن أن يطلق على مثيل من الطبقة. type.__subclasses__() مثال على طريقة على type ميتاكلاس. يمكنك أيضا تحديد طرق "السحر" العادية، مثل __add__ ، __iter__ و __getattr__ ، لتنفيذ أو تغيير كيفية تصرف الصف.

في ما يلي مثال مجمع للبتات والقطع:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(cls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, cls).__new__(cls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__



فئات كأشياء

قبل فهم النظارات الطبية، تحتاج إلى إتقان الفصول الدراسية في بيثون. و بيثون لديه فكرة غريبة جدا من ما هي الطبقات، اقترضت من لغة سمالتالك.

في معظم اللغات، والطبقات هي مجرد قطعة من التعليمات البرمجية التي تصف كيفية إنتاج كائن. هذا صحيح في بيثون أيضا:

>>> class ObjectCreator(object):
...       pass
... 

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

ولكن الطبقات أكثر من ذلك في بايثون. الفصول هي كائنات أيضا.

نعم، الكائنات.

بمجرد استخدام class الكلمة الرئيسية، يقوم بايثون بتنفيذه وإنشاء كائن. التعليمات

>>> class ObjectCreator(object):
...       pass
... 

يخلق في الذاكرة كائن باسم "أوبجكتكريتور".

هذا الكائن (الطبقة) هو نفسه قادر على خلق كائنات (مثيلات)، وهذا هو السبب في انها فئة .

ولكن لا يزال، انها كائن، وبالتالي:

  • يمكنك تعيينه لمتغير
  • يمكنك نسخه
  • يمكنك إضافة سمات إليه
  • يمكنك تمريره كمعلمة الدالة

على سبيل المثال:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
... 
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

إنشاء الطبقات ديناميكيا

منذ الطبقات هي الكائنات، يمكنك إنشاؤها على الطاير، مثل أي كائن.

أولا، يمكنك إنشاء فئة في وظيفة باستخدام class :

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...     
>>> MyClass = choose_class('foo') 
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

ولكنها ليست ديناميكية جدا، لأنه لا يزال لديك لكتابة الطبقة بأكملها نفسك.

منذ الطبقات هي الأشياء، يجب أن تتولد عن طريق شيء.

عند استخدام الكلمة الرئيسية class ، يقوم بيثون بإنشاء هذا الكائن تلقائيا. ولكن كما هو الحال مع معظم الأشياء في بيثون، فإنه يوفر لك وسيلة للقيام بذلك يدويا.

تذكر type الدالة؟ الوظيفة القديمة الجيدة التي تمكنك من معرفة ما نوع الكائن:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

حسنا، type لديه قدرة مختلفة تماما، فإنه يمكن أيضا إنشاء الطبقات على الطاير. type يمكن أن تأخذ وصف فئة كمعلمات، وإرجاع فئة.

(وأنا أعلم، انها سخيفة أن نفس الوظيفة يمكن أن يكون اثنين من استخدامات مختلفة تماما وفقا للمعلمات التي تمر إليها، انها قضية بسبب التوافق إلى الوراء في بايثون)

type يعمل بهذه الطريقة:

type(name of the class, 
     tuple of the parent class (for inheritance, can be empty), 
     dictionary containing attributes names and values)

على سبيل المثال:

>>> class MyShinyClass(object):
...       pass

يمكن إنشاء يدويا بهذه الطريقة:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

ستلاحظ أننا نستخدم "ميشينيكلاس" كاسم الطبقة وكمتغير لعقد مرجع الفئة. يمكن أن تكون مختلفة، ولكن ليس هناك سبب لتعقد الأمور.

type يقبل قاموس لتعريف سمات الطبقة. وبالتالي:

>>> class Foo(object):
...       bar = True

يمكن ترجمتها إلى:

>>> Foo = type('Foo', (), {'bar':True})

وتستخدم كطبقة عادية:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

وبطبيعة الحال، يمكنك أن ترث منه، لذلك:

>>>   class FooChild(Foo):
...         pass

سيكون:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

وفي النهاية، ستحتاج إلى إضافة طرق إلى الفصل الدراسي. مجرد تحديد وظيفة مع التوقيع الصحيح وتعيينه كخاصية.

>>> def echo_bar(self):
...       print(self.bar)
... 
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

ويمكنك إضافة المزيد من الأساليب بعد إنشاء الفئة ديناميكيا، تماما مثل إضافة أساليب إلى كائن فئة تم إنشاؤه بشكل طبيعي.

>>> def echo_bar_more(self):
...       print('yet another method')
... 
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

ترى أين نحن ذاهبون: في بيثون، والطبقات هي الكائنات، ويمكنك إنشاء فئة على الطاير، حيوي.

هذا هو ما يفعله بيثون عند استخدام class الكلمة الرئيسية، ويقوم بذلك باستخدام ميتاكلاس.

ما هي النظارات الطبية (أخيرا)

النظارات الشمسية هي "الأشياء" التي تخلق الطبقات.

يمكنك تعريف الطبقات من أجل خلق الكائنات، أليس كذلك؟

لكننا تعلمنا أن دروس بايثون هي أشياء.

حسنا، النظارات الشمسية هي ما خلق هذه الكائنات. هم الطبقات الطبقات، يمكنك صورة لهم بهذه الطريقة:

MyClass = MetaClass()
MyObject = MyClass()

لقد رأيت هذا type يتيح لك إجراء شيء من هذا القبيل:

MyClass = type('MyClass', (), {})

ذلك لأن type الوظيفة هو في الواقع ميتاكلاس. type هو يستخدم ميتاكلاس بيثون لإنشاء جميع الطبقات وراء الكواليس.

الآن كنت أتساءل لماذا هيك هو مكتوب في الأحرف الصغيرة، وليس Type ؟

حسنا، أعتقد انها مسألة الاتساق مع str ، والطبقة التي تخلق سلاسل الكائنات، و int الطبقة التي تخلق أجسام صحيحة. type هو مجرد فئة التي تخلق كائنات فئة.

ترى ذلك عن طريق التحقق من السمة __class__ .

كل شيء، وأعني كل شيء، هو كائن في بايثون. ويشمل ذلك إنتس، سلاسل، وظائف والطبقات. كل منهم كائنات. وكلها تم إنشاؤها من فئة:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

الآن، ما هو __class__ من أي __class__ ؟

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

لذلك، ميتاكلاس هو مجرد الأشياء التي تخلق الأجسام الطبقة.

يمكنك استدعاء "مصنع فئة" إذا كنت ترغب في ذلك.

type هو المدمج في ميتاكلاس يستخدم بيثون، ولكن بالطبع، يمكنك إنشاء ميتاكلاس الخاصة بك.

السمة __metaclass__

يمكنك إضافة سمة __metaclass__ عند كتابة فئة:

class Foo(object):
  __metaclass__ = something...
  [...]

إذا قمت بذلك، سوف بيثون استخدام ميتاكلاس لإنشاء فئة Foo .

حذرا، انها صعبة.

يمكنك كتابة class Foo(object) أولا، ولكن لم يتم إنشاء الكائن فئة Foo في الذاكرة حتى الآن.

ستبحث بيثون عن __metaclass__ في تعريف الفئة. إذا وجد ذلك، فإنه سيتم استخدامه لإنشاء فئة الكائن Foo . إذا لم يكن كذلك، فإنه سيتم استخدام type لإنشاء الفئة.

اقرأ ذلك عدة مرات.

عندما تفعل:

class Foo(Bar):
  pass

بيثون يفعل ما يلي:

هل هناك سمة __metaclass__ في Foo ؟

إذا كان الجواب نعم، إنشاء في الذاكرة كائن فئة (قلت كائن فئة، البقاء معي هنا)، مع اسم Foo باستخدام ما هو في __metaclass__ .

إذا كان بيثون لا يمكن العثور __metaclass__ ، وسوف نبحث عن __metaclass__ على مستوى __metaclass__ ، ومحاولة أن تفعل الشيء نفسه (ولكن فقط للفئات التي لا ترث أي شيء، والطبقات على الطراز القديم أساسا).

ثم إذا لم تتمكن من العثور على أي __metaclass__ على الإطلاق، فإنه سيتم استخدام Bar 's (أول الأم) ميتاكلاس الخاصة (التي قد تكون type الافتراضي) لإنشاء كائن فئة.

كن حذرا هنا أن السمة __metaclass__ لن تكون موروثة، ميتاكلاس من الأصل ( Bar.__class__ ) سيكون. إذا كان Bar يستخدم سمة __metaclass__ التي تم إنشاؤها Bar مع type() (وليس type.__new__() )، فإن الفئات الفرعية لن يرث هذا السلوك.

الآن السؤال الكبير هو، ماذا يمكنك وضع في __metaclass__ ؟

الجواب هو: شيء يمكن أن تخلق الطبقة.

وماذا يمكن أن تخلق فئة؟ type ، أو أي شيء الفئات الفرعية أو استخدامه.

النظارات الطبية المخصصة

والغرض الرئيسي من ميتاكلاس هو تغيير الطبقة تلقائيا، عندما يتم إنشاؤه.

وعادة ما تقوم بذلك لواجهات برمجة التطبيقات، حيث تريد إنشاء فئات تطابق السياق الحالي.

تخيل مثالا غبيا، حيث تقرر أن جميع الفئات في الوحدة النمطية يجب أن يكون لها سمات مكتوبة في الأحرف الكبيرة. هناك عدة طرق للقيام بذلك، ولكن طريقة واحدة هي تعيين __metaclass__ على مستوى الوحدة النمطية.

وبهذه الطريقة، سيتم إنشاء جميع فئات هذه الوحدة باستخدام هذه ميتاكلاس، وعلينا فقط أن نقول ميتاكلاس لتحويل كافة السمات إلى الأحرف الكبيرة.

لحسن الحظ، __metaclass__ يمكن أن يكون في الواقع أي استدعاء، فإنه لا يحتاج إلى أن تكون فئة رسمية (وأنا أعلم، شيء مع 'الطبقة' في اسمها لا تحتاج إلى أن تكون فئة، يذهب الشكل ... ولكن من المفيد).

لذلك سنبدأ بمثال بسيط، باستخدام وظيفة.

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
  """
    Return a class object, with the list of its attribute turned 
    into uppercase.
  """

  # pick up any attribute that doesn't start with '__' and uppercase it
  uppercase_attr = {}
  for name, val in future_class_attr.items():
      if not name.startswith('__'):
          uppercase_attr[name.upper()] = val
      else:
          uppercase_attr[name] = val

  # let `type` do the class creation
  return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
  # but we can define __metaclass__ here instead to affect only this class
  # and this will work with "object" children
  bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

الآن، دعونا نفعل بالضبط نفس الشيء، ولكن باستخدام فئة حقيقية ل ميتاكلاس:

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type): 
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name, 
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

ولكن هذا ليس حقا أوب. نحن ندعو type مباشرة ونحن لا تجاوز أو استدعاء الوالد __new__ . دعنا نقوم به:

class UpperAttrMetaclass(type): 

    def __new__(upperattr_metaclass, future_class_name, 
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name, 
                            future_class_parents, uppercase_attr)

ربما تكون لاحظت الوسيطة الإضافية upperattr_metaclass . لا يوجد شيء خاص حول هذا الموضوع: __new__ يتلقى دائما الطبقة التي تم تعريفها في، كمعلمة الأولى. تماما مثل لديك self للطرق العادية التي تتلقى مثيل كمعلمة الأولى، أو الطبقة تعريف لطرق الطبقة.

وبطبيعة الحال، فإن الأسماء التي استخدمتها هنا طويلة من أجل الوضوح، ولكن مثل self ، كل الحجج لها أسماء تقليدية. لذلك سوف ميتاكلاس الإنتاج الحقيقي تبدو مثل هذا:

class UpperAttrMetaclass(type): 

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

يمكننا أن نجعلها أكثر نظافة من خلال استخدام super ، والتي من شأنها أن تخفف من الميراث (لأن نعم، هل يمكن أن يكون النظارات، وراثة من النظارات، وراثة من نوع):

class UpperAttrMetaclass(type): 

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

هذا هو. ليس هناك حقا شيء أكثر عن النظارات الشمسية.

السبب وراء تعقيد التعليمات البرمجية باستخدام النظارات الشمسية ليس بسبب النظارات الطبية، وذلك لأنك عادة ما تستخدم النظارات الطبية للقيام الأشياء الملتوية الاعتماد على التأمل، والتلاعب في الميراث، فارس مثل __dict__ ، الخ.

في الواقع، النظارات الشمسية مفيدة بشكل خاص للقيام السحر الأسود، وبالتالي الاشياء المعقدة. ولكن في حد ذاتها، فهي بسيطة:

  • اعتراض خلق الطبقة
  • تعديل الطبقة
  • إعادة الطبقة المعدلة

لماذا ستستخدم دروس المناظير بدلا من الوظائف؟

منذ __metaclass__ يمكن قبول أي استدعاء، لماذا يمكنك استخدام الطبقة لأنه من الواضح أكثر تعقيدا؟

هناك عدة أسباب للقيام بذلك:

  • والنية واضحة. عندما تقرأ UpperAttrMetaclass(type) ، وانت تعرف ما سيتبع
  • يمكنك استخدام أوب. ميتاكلاس يمكن أن ترث من ميتاكلاس، تجاوز الأساليب الأم. يمكن للأغراض الطبية استخدام الأغطية.
  • الأطفال من فئة سيكون مثيلات ميتاسكلاس إذا قمت بتحديد فئة ميتاكلاس، ولكن ليس مع ميتاكلاس وظيفة.
  • يمكنك هيكلة التعليمات البرمجية بشكل أفضل. أنت لا تستخدم أبدا النظارات الطبية لشيء تافهة مثل المثال أعلاه. انها عادة لشيء معقد. وجود القدرة على جعل عدة طرق وتجميعها في فئة واحدة مفيد جدا لجعل التعليمات البرمجية أسهل للقراءة.
  • يمكنك ربط على __new__ ، __init__ و __call__ . والتي سوف تسمح لك أن تفعل أشياء مختلفة. حتى لو عادة يمكنك أن تفعل كل شيء في __new__ ، بعض الناس أكثر راحة فقط باستخدام __init__ .
  • وتسمى هذه النظارات، لعنة ذلك! يجب أن يعني شيئا!

لماذا تستخدمين النظارات الشمسية؟

الآن السؤال الكبير. لماذا سوف تستخدم بعض ميزة عرضة خطأ غامض؟

حسنا، عادة لا:

النظارات الشمسية هي السحر أعمق أن 99٪ من المستخدمين يجب أن تقلق أبدا. إذا كنت تتساءل عما إذا كنت في حاجة إليها، أنت لا (الناس الذين في الواقع في حاجة إليها يعرفون بالتأكيد مع أنهم بحاجة إليها، ولا تحتاج إلى تفسير لماذا).

بيثون جورو تيم بيترز

حالة الاستخدام الرئيسية ل ميتاكلاس هو إنشاء أبي. ومن الأمثلة النموذجية على ذلك نظام إدارة الموارد البشرية في جانغو.

انها تسمح لك لتحديد شيء من هذا القبيل:

class Person(models.Model):
  name = models.CharField(max_length=30)
  age = models.IntegerField()

ولكن إذا قمت بذلك:

guy = Person(name='bob', age='35')
print(guy.age)

لن يقوم IntegerField كائن IntegerField . وسوف يعود int ، وحتى يمكن أن تأخذ مباشرة من قاعدة البيانات.

هذا ممكن لأن __metaclass__ يعرف __metaclass__ ويستخدم بعض السحر الذي سيحول Person الذي عرفته فقط مع عبارات بسيطة في ربط معقدة إلى حقل قاعدة البيانات.

جانغو يجعل شيئا نظرة معقدة بسيطة من خلال تعريض أبي بسيطة واستخدام الأقمشة، وإعادة رمز من أبي للقيام بالوظيفة الحقيقية وراء الكواليس.

الكلمة الأخيرة

أولا، أنت تعرف أن الفئات هي الكائنات التي يمكن أن تخلق مثيلات.

حسنا في الواقع، والطبقات هي نفسها حالات. من النظارات الطبية.

>>> class Foo(object): pass
>>> id(Foo)
142630324

كل شيء هو كائن في بيثون، وكلها إما حالات من الطبقات أو حالات من النظارات الطبية.

باستثناء type .

type هو في الواقع ميتاكلاس الخاصة بها. هذا ليس شيئا يمكن أن تتكاثر في بيثون النقي، ويتم ذلك عن طريق الغش قليلا على مستوى التنفيذ.

ثانيا، معقدة معقد. قد لا ترغب في استخدامها لتغييرات الطبقة بسيطة جدا. يمكنك تغيير الطبقات باستخدام اثنين من التقنيات المختلفة:

99٪ من الوقت الذي تحتاجه تغيير الطبقة، وكنت أفضل حالا باستخدام هذه.

ولكن 98٪ من الوقت، لا تحتاج إلى تغيير الطبقة على الإطلاق.




ملاحظة، هذه الإجابة هي ل 2.x بيثون كما كان مكتوبا في عام 2008، والأقنعة هي مختلفة قليلا في 3.x، انظر التعليقات.

المختبرات هي الصلصة السرية التي تجعل العمل "الطبقة". يسمى ميتاكلاس الافتراضي لكائن نمط جديد "نوع".

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

تأخذ النظارات الطبية 3 أرج. ' اسم '، ' قواعد ' و ' ديكت '

هنا هو المكان الذي يبدأ السر. ابحث عن المكان الذي يأتي فيه الاسم والقواعد والدلالة في تعريف المثال هذا.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

يتيح تحديد ميتاس كلاس الذي سيوضح كيف ' كلاس: ' يطلق عليه.

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

والآن، وهو مثال يعني في الواقع شيئا، وهذا سيجعل المتغيرات تلقائيا في قائمة "سمات" تعيين على فئة، وتعيين إلى لا شيء.

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

لاحظ أن السلوك السحري الذي مكاسب "إينيتاليسد" من خلال وجود ميتاكلاس init_attributes لا يتم تمريرها إلى فئة فرعية من إنيتاليسد.

هنا هو مثال أكثر واقعية، وتبين كيف يمكنك فئة فرعية 'نوع' لجعل ميتاكلاس الذي يقوم بإجراء عند إنشاء فئة. هذا أمر صعب جدا:

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b



استخدام واحد للأسطح هو إضافة خصائص وطرق جديدة إلى مثيل تلقائيا.

على سبيل المثال، إذا نظرتم إلى نماذج جانغو ، فإن تعريفهم يبدو مربكا بعض الشيء. يبدو كما لو كنت تعرف خصائص الطبقة فقط:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

ومع ذلك، في وقت التشغيل يتم تعبئة الكائنات الشخص مع جميع أنواع الأساليب المفيدة. انظر المصدر لبعض ميتاكلاسيري مذهلة.




وقد أوضح آخرون كيف تعمل الأغطية الزجاجية وكيف أنها تناسب نظام نوع بيثون. إليك مثال لما يمكن استخدامه. في إطار الاختبار كتبت، أردت أن تتبع النظام الذي تم تعريف الطبقات، حتى أنني يمكن في وقت لاحق مثيل لهم في هذا النظام. لقد وجدت أنه من الأسهل القيام بذلك باستخدام ميتاكلاس.

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

أي شيء فئة فرعية من MyType ثم يحصل على سمة فئة _order الذي يسجل الترتيب الذي تم تعريف الفئات.




وأعتقد أن مقدمة أونلامب ل ميتاكلاس البرمجة مكتوبة بشكل جيد ويعطي مقدمة جيدة حقا للموضوع على الرغم من كونها عدة سنوات من العمر بالفعل.

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

باختصار: فئة هي مخطط لإنشاء مثيل، ميتاكلاس هو مخطط لإنشاء فئة. يمكن أن يرى بسهولة أنه في بيثون الطبقات تحتاج إلى أن تكون كائنات من الدرجة الأولى أيضا لتمكين هذا السلوك.

لم أكن قد كتبت نفسي أبدا، ولكن أعتقد أن واحدة من أجمل استخدامات النظارات يمكن أن ينظر إليها في إطار دجانغو . تستخدم الفئات النموذجية نهج ميتاكلاس لتمكين نمط إعلاني من كتابة نماذج جديدة أو فئات النموذج. في حين أن ميتاكلاس هو خلق الطبقة، جميع أعضاء الحصول على إمكانية لتخصيص الطبقة نفسها.

الشيء الذي تركه هو القول: إذا كنت لا تعرف ما هي النظارات الطبية، واحتمال أنك لن تحتاج إليها هو 99٪.




ما هي النظارات الطبية؟ ماذا يمكنك استخدامها ل؟

تلدر: ميتاكلاس إنستياتيس وتعرف السلوك لفئة تماما مثل إنستيناتس الطبقة ويحدد السلوك لمثيل.

شبة الكود:

>>> Class(...)
instance

يجب أن تبدو أعلاه مألوفة. حسنا، من أين تأتي Class ؟ انها مثيل ميتاكلاس (أيضا بسيودوكود):

>>> Metaclass(...)
Class

في رمز حقيقي، يمكننا تمرير ميتاكلاس الافتراضي، type ، كل ما نحتاج إلى إنشاء فئة ونحصل على فئة:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>

وضعها بشكل مختلف

  • فئة إلى مثيل كما ميتاكلاس هو لفئة.

    عندما نحصل على كائن، نحصل على مثيل:

    >>> object()                          # instantiation of class
    <object object at 0x7f9069b4e0b0>     # instance

    وبالمثل، عندما نحدد فئة صراحة مع ميتاكلاس الافتراضي، type ، ونحن إنشائها:

    >>> type('Object', (object,), {})     # instantiation of metaclass
    <class '__main__.Object'>             # instance
  • وبعبارة أخرى، فإن الفئة هي مثال على ميتاكلاس:

    >>> isinstance(object, type)
    True
  • وضع طريقة ثالثة، ميتاكلاس هو فئة الطبقة.

    >>> type(object) == type
    True
    >>> object.__class__
    <class 'type'>

عند كتابة تعريف فئة و بيثون ينفذ ذلك، فإنه يستخدم ميتاكلاس لبدء كائن فئة (والتي، بدورها، سيتم استخدامها لإنشاء مثيلات من هذا الصنف).

كما يمكننا استخدام تعريفات فئة لتغيير كيفية تصرف مثيلات الكائنات المخصصة، يمكننا استخدام تعريف فئة ميتاكلاس لتغيير طريقة تصرف كائن الفئة.

بماذا يستخدمن؟ من المستندات :

الاستخدامات المحتملة للأجهزة الطبية لا حدود لها. بعض الأفكار التي تم استكشافها تشمل قطع الأشجار، فحص الواجهة، التفويض التلقائي، إنشاء العقارات التلقائي، الوكلاء، الأطر، والتلقائي قفل الموارد / تزامن.

ومع ذلك، عادة ما يتم تشجيع المستخدمين على تجنب استخدام النظارات الطبية ما لم تكن هناك ضرورة مطلقة.

يمكنك استخدام ميتاكلاس في كل مرة تقوم فيها بإنشاء فئة:

عند كتابة تعريف فئة، على سبيل المثال، مثل هذا،

class Foo(object): 
    'demo'

يمكنك إنشاء كائن فئة.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

وهو نفس type الاتصال وظيفيا مع الوسيطات المناسبة وتعيين النتيجة إلى متغير من هذا الاسم:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

ملاحظة، بعض الأشياء تلقائيا الحصول على إضافة إلى __dict__ ، أي مساحة الاسم:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

و ميتاكلاس من الكائن أنشأنا، في كلتا الحالتين، هو type .

(ملاحظة جانبية على محتويات الصف __dict__ : __module__ هناك لأن الطبقات يجب أن تعرف أين يتم تعريفها، و __dict__ و __weakref__ هناك لأننا لا نعرف __slots__ - إذا كنا نحدد __slots__ سنوفر قليلا من في الحالات، كما يمكننا أن __dict__ و __weakref__ خلال استبعادها.على سبيل المثال:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... ولكن أنا يضغط.)

يمكننا تمديد type تماما مثل أي تعريف فئة أخرى:

في ما يلي __repr__ الافتراضي للفئات:

>>> Foo
<class '__main__.Foo'>

واحدة من أكثر الأشياء قيمة يمكننا القيام به افتراضيا في كتابة كائن بيثون هو توفيره مع __repr__ جيدة. عندما ندعو help(repr) ونحن نعلم أن هناك اختبارا جيدا ل __repr__ الذي يتطلب أيضا اختبار للمساواة - obj == eval(repr(obj)) . تنفيذ بسيط التالية من __repr__ و __eq__ فئة من نوع الفئة لدينا يوفر مظاهرة التي قد تحسن على الافتراضي __repr__ من الطبقات:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

وحتى الآن عندما نقوم بإنشاء كائن مع هذا metaclass، و __repr__ردد على سطر الأوامر يوفر مشهدا أقل بكثير من القبيح الافتراضي:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

مع لطيفة __repr__تعرف عن مثيل فئة، لدينا قدرة أقوى لتصحيح التعليمات البرمجية لدينا. ومع ذلك، أبعد من ذلك بكثير التحقق مع eval(repr(Class))غير المرجح (وظائف سيكون مستحيلا وليس إلى وحدة التقييم من الافتراضية __repr__الصورة).

لاستخدام المتوقع: __prepare__مساحة اسم

إذا، على سبيل المثال، نريد أن نعرف في ما تأمر يتم إنشاء طرق فئة في، ونحن يمكن أن توفر ديكت كما أمرت مساحة الاسم للفئة. وسوف نفعل هذا مع __prepare__التي ترجع ديكت مساحة للطبقة إذا تم تنفيذه في بيثون 3 :

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

والاستعمال:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

والآن لدينا سجل من الترتيب الذي تم إنشاء هذه الطرق (وسمات فئة أخرى):

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

ملاحظة تم تكييف هذا المثال من الوثائق - الجديد التعداد في المكتبة القياسية يفعل ذلك.

وذلك ما فعلناه هو مثيل metaclass من خلال خلق فئة. يمكننا أيضا علاج metaclass كما كنا أي فئة أخرى. كان لديه أمر الأسلوب دقة:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

ولها ما يقرب من الصحيح repr(وهو ما لم تعد قادرة حدة التقييم ما لم نتمكن من العثور على طريقة لتمثيل وظائفنا.):

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})



بيثون 3 التحديث

هناك (في هذه المرحلة) طريقتين الرئيسية في metaclass:

  • __prepare__ و
  • __new__

__prepare__يتيح لك توفير خرائط حسب الطلب (مثل OrderedDict) لاستخدامها في مساحة الاسم بينما يتم إنشاء الفئة. يجب العودة مثيل مهما كانت مساحة الاسم الذي تختاره. إذا لم يكن لتنفيذ __prepare__أمر طبيعي dictيستخدم.

__new__ هي المسؤولة عن خلق الفعلي / تعديل الدرجة النهائية.

A-عظام عارية، لا تفعل شيئا-اضافية يود metaclass:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

وهناك مثال بسيط:

يقول كنت تريد بعض رمز التحقق من الصحة بسيط لتشغيل على سمات بك - مثل ذلك يجب أن يكون دائما intأو str. دون metaclass، فإن الطبقة مظهرك شيء من هذا القبيل:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

كما ترون، لديك لتكرار اسم السمة مرتين. وهذا يجعل من الأخطاء المطبعية الممكنة جنبا إلى جنب مع الحشرات المزعجة.

A metaclass بسيط يمكن أن تعالج هذه المشكلة:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

هذا هو ما metaclass ستبدو (لا تستخدم __prepare__نظرا لعدم الحاجة إليها):

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

عينة المدى من:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

ينتج عنه:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

ملاحظة : هذا المثال بسيط جدا كان يمكن أن أيضا تم إنجازه مع الديكور الدرجة، ولكن يفترض على metaclass الفعلي سوف يفعل أكثر من ذلك بكثير.

و"ValidateType" الطبقة للرجوع اليها:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value



A metaclass هو الفئة التي يروي كيف ينبغي إنشاء (بعض) فئة أخرى.

هذا هو حال رأيت metaclass كحل لمشكلتي: كان لي مشكلة معقدة حقا، وهذا ربما كان من الممكن حلها بشكل مختلف، ولكن اخترت حلها باستخدام metaclass. بسبب تعقيد، وهي واحدة من عدد قليل من وحدات وقد كتبت فيها تعليقات في وحدة تتجاوز كمية من التعليمات البرمجية التي تم كتابتها. ها هو...

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()



دور metaclass في __call__()طريقة عند إنشاء مثيل فئة

إذا كنت قد فعلت البرمجة بايثون لأكثر من بضعة أشهر سوف تتعثر في نهاية المطاف على التعليمات البرمجية التي تبدو مثل هذا:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

وهذا الأخير هو ممكن عند تنفيذ __call__()أسلوب السحر على الطبقة.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

و __call__()يتم استدعاء الأسلوب عند استخدام مثيل فئة كما للاستدعاء. ولكن كما رأينا من الأجوبة السابقة فئة في حد ذاته هو مثيل metaclass، لذلك عندما نستخدم الطبقة كما للاستدعاء (أي عندما كنا إنشاء مثيل من ذلك) ونحن في الواقع يدعو metaclass لها في __call__()الأسلوب. في هذه المرحلة معظم المبرمجين بيثون يتم الخلط بين بعض الشيء لأنها قد قيل أنه عندما خلق مثيل مثل هذا instance = SomeClass()كنت تتصل انها __init__()طريقة. بعض الذين قد حفرت أعمق قليلا نعرف أن قبل __init__()هناك __new__(). حسنا، اليوم يتم كشف طبقة أخرى من الحقيقة، قبل أن __new__()هناك عام metaclass __call__().

دعونا دراسة سلسلة استدعاء الأسلوب من على وجه التحديد منظور إنشاء مثيل من فئة.

هذا هو metaclass الذي يسجل بالضبط لحظة قبل أن يتم إنشاء مثيل ولحظة انها على وشك إعادته.

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

هذه هي فئة يستخدم هذا metaclass

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

والآن دعونا إنشاء مثيل Class_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

رمز أعلاه في الواقع لا تفعل أي شيء آخر سوى تسجيل المهمة ومن ثم تفويض العمل الفعلي للوالد (أي الحفاظ على السلوك الافتراضي). حتى مع typeكونها Meta_1الصورة الفئة الأصل، يمكننا أن نتصور أن هذا من شأنه أن يكون تنفيذ الزائفة من type.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

يمكننا أن نرى أن metaclass في __call__()طريقة هو الذي يسمى أولا. ومن ثم خلق مندوبي المثيل إلى الفئة التي __new__()أسلوب والتهيئة إلى مثيل ل __init__(). كما انها واحدة أن يعود في نهاية المطاف المثيل.

من فوقه ينبع أن metaclass و __call__()تعطى أيضا الفرصة ليقرر ما إذا كان أو لم يكن دعوة ل Class_1.__new__()أو Class_1.__init__()في نهاية المطاف القيام بها. على مدى تنفيذه فإنه يمكن فعلا العودة كائن لم تطرق من قبل أي من هذه الأساليب. خذ على سبيل المثال هذا النهج للنمط المفرد:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__() 
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

دعونا نلاحظ ما يحدث عندما تحاول مرارا وتكرارا لإنشاء كائن من نوع Class_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True



typeهو في الواقع metaclass- فئة التي تخلق الطبقات آخر. معظم metaclassهي الفئات الفرعية لل type. و metaclassيتلقى newالدرجة الأولى كما حجتها وتوفير إمكانية الوصول إلى كائن الفئة مع التفاصيل المذكورة على النحو التالي:

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
... 

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
... 
class name: NewClass
Bases <class 'object'>: 
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

لاحظ أن الطبقة لم مثيل في أي وقت؛ فمجرد خلق الطبقة أثار تنفيذ metaclass.




ليرة تركية، نسخة الدكتور

و type(obj)ظيفة يحصل لك نوع كائن.

و type()من فئة غير التي metaclass .

لاستخدام metaclass:

class Foo(object):
    __metaclass__ = MyMetaClass



دروس بايثون هي نفسها الكائنات - كما في المثال - من هم من الطبقة الفوقية.

وmetaclass الافتراضية، التي يتم تطبيقها عند عند تحديد فئات على النحو التالي:

class foo:
    ...

وتستخدم الطبقة الفوقية لتطبيق بعض قاعدة لمجموعة كاملة من الطبقات. على سبيل المثال، لنفترض أنك بناء ORM الوصول إلى قاعدة بيانات، وتريد السجلات من كل جدول ليكون من فئة المعينة إلى هذا الجدول (على أساس المجالات، وقواعد العمل، الخ ..،)، واحتمال استخدام metaclass هو على سبيل المثال، والمنطق تجمع الاتصال، والذي هو حصة من قبل جميع الطبقات من سجل من كافة الجداول. استخدام آخر هو منطق لدعم المفاتيح الخارجية، الذي ينطوي على فئات متعددة من السجلات.

عند تعريف metaclass، أنت نوع فرعية، ويمكن overrided الطرق السحرية التالية لإدراج المنطق الخاص.

class somemeta(type):
    __new__(mcs, name, bases, clsdict):
      """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes 
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass. 
 ie: 
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}

      you can modify any of these values before passing on to type
      """
      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
      """ 
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
      """
      pass


    def __prepare__():
        """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
        """
        return dict()

    def mymethod(cls):
        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
        """
        pass

على أية حال، أولئك هما السنانير الأكثر شيوعا. metaclassing هي قوية، وسبق هو في أي مكان بالقرب من قائمة وشاملة من الاستخدامات لmetaclassing.




A metaclass هو أساسا فئة قاعدة مجردة - وهو مفهوم تدرس في معظم دورات برمجة الكمبيوتر وسيطة.