मुझे हाल ही में एक ही सवाल पूछा गया था, और कई जवाब आए। मुझे आशा है कि इस धागे को पुनर्जीवित करना ठीक है, जैसा कि मैं उपयोग किए गए कुछ मामलों पर विस्तार से बताना चाहता हूं, और कुछ नए जोड़ देता हूं।
अधिकांश मेटाक्लासेस मैंने दो चीजों में से एक को देखा है:
पंजीकरण (डेटा संरचना में एक वर्ग जोड़ना):
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)
वास्तव में, यह बहुत अधिक समान है: आप वर्ग सज्जाकारों का प्रतिकूल उल्लेख करते हैं, लेकिन यह वास्तव में एक वर्ग पर एक समारोह आह्वान के लिए सिंटैक्टिक चीनी से ज्यादा कुछ नहीं है, इसलिए इसके बारे में कोई जादू नहीं है।
वैसे भी, इस मामले में मेटाक्लासिस का लाभ विरासत है, क्योंकि वे किसी भी उपवर्ग के लिए काम करते हैं, जबकि अन्य समाधान केवल उपवर्गों के लिए स्पष्ट रूप से सजाए गए या पंजीकृत होते हैं।
>>> class B(A):
... pass
...
>>> models
{'A': <__main__.A class at 0x...> # No B :(
Refactoring (वर्ग विशेषताओं को संशोधित करना या नए जोड़ना):
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
यह चेतावनी जारी करने के लिए चौखटे में उपयोगी हो सकता है जब भी समान नाम या अपूर्ण वंशानुक्रम वाले वर्गों को परिभाषित किया जाता है, लेकिन मैं वास्तव में इन मूल्यों को बदलने के लिए ट्रोलिंग के साथ एक कारण के बारे में नहीं सोच सकता। शायद डेविड बेज़ले कर सकते हैं।
वैसे भी, पायथन 3 में, मेटाक्लासेस की भी __prepare__
विधि है, जो आपको क्लास बॉडी का मूल्यांकन मैपिंग के अलावा अन्य में करने देता है 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)
लेकिन यह बहुत है, एक साधारण मेटाक्लास (विशेष रूप से पहला वाला, जो वास्तव में आपके मस्तिष्क को पिघला देता है) की तुलना में म्यू-वूडू-एर। मेरा कहना है, आप मेटाक्लस को अपरिचित और प्रति-सहज के रूप में देखते हैं, लेकिन आप उन्हें प्रोग्रामिंग भाषाओं में विकास के अगले चरण के रूप में भी देख सकते हैं: आपको बस अपनी मानसिकता को समायोजित करना होगा। आखिरकार, आप संभवतः C में सब कुछ कर सकते हैं, जिसमें फ़ंक्शन पॉइंटर्स के साथ एक संरचना को परिभाषित करना और इसे अपने कार्यों के लिए पहले तर्क के रूप में पारित करना शामिल है। पहली बार C ++ देखने वाला व्यक्ति कह सकता है, "यह जादू क्या है? संकलक क्यों गुजर रहा हैthis
तरीकों के लिए, लेकिन नियमित और स्थिर कार्यों के लिए नहीं? यह स्पष्ट होना बेहतर है और अपने तर्कों के बारे में बात करें "। लेकिन फिर, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग बहुत ही शक्तिशाली है एक बार जब आप इसे प्राप्त करते हैं, और इसलिए यह है, उह ... क्वैसी-पहलू-उन्मुख प्रोग्रामिंग, मुझे लगता है। और एक बार आप। मेटाक्लास को समझें, वे वास्तव में बहुत सरल हैं, इसलिए सुविधाजनक होने पर उनका उपयोग क्यों न करें?
और अंत में, मेटाक्लास रेड हैं, और प्रोग्रामिंग मजेदार होनी चाहिए। मानक प्रोग्रामिंग कंस्ट्रक्शन और डिज़ाइन पैटर्न का उपयोग करना हर समय उबाऊ और उदासीन है, और आपकी कल्पना में बाधा डालता है। थोड़ा और जियो! यहाँ सिर्फ आपके लिए एक मेटामेकाॅक्लस है।
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
संपादित करें
यह एक बहुत पुराना सवाल है, लेकिन यह अभी भी बढ़ रहा है, इसलिए मैंने सोचा कि मैं एक अधिक व्यापक उत्तर के लिए एक लिंक जोड़ूंगा। यदि आप मेटाक्लस और उनके उपयोगों के बारे में अधिक पढ़ना चाहते हैं, तो मैंने इसके बारे में एक लेख प्रकाशित किया है ।