আমাকে সম্প্রতি একই প্রশ্ন করা হয়েছিল, এবং বেশ কয়েকটি উত্তর নিয়ে এসেছি। আমি আশা করি যে এই থ্রেডটি পুনরুদ্ধার করা ঠিক আছে, কারণ আমি উল্লিখিত কয়েকটি ব্যবহারের ক্ষেত্রে বিশদভাবে বর্ণনা করতে এবং কয়েকটি নতুন যুক্ত করতে চেয়েছিলাম।
আমি দেখা বেশিরভাগ মেটাচ্লাস দুটি জিনিসগুলির মধ্যে একটি করে:
নিবন্ধকরণ (একটি ডাটা স্ট্রাকচারে একটি ক্লাস যুক্ত করা):
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 :(
রিফ্যাক্টরিং (শ্রেণীর বৈশিষ্ট্যগুলি সংশোধন করা বা নতুন যুক্ত করা):
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)
তবে এটি অনেকটা সাধারণ মেটাক্লাসের চেয়ে ভুডু-এআর (বিশেষত প্রথমটি যা আপনার মস্তিষ্ককে সত্যই গলে দেয়)। আমার বক্তব্যটি হ'ল, আপনি মেটাচ্লাসগুলিকে অপরিচিত এবং পাল্টা স্বজ্ঞাত হিসাবে দেখেন, তবে আপনি এগুলি প্রোগ্রামিং ভাষায় বিবর্তনের পরবর্তী ধাপ হিসাবেও দেখতে পারেন: আপনাকে কেবল নিজের মানসিকতা সামঞ্জস্য করতে হবে। সর্বোপরি, আপনি সম্ভবত ফাংশন পয়েন্টারগুলির সাথে স্ট্রাক্ট সংজ্ঞায়িত করা এবং এটির কার্যকারণের প্রথম আর্গুমেন্ট হিসাবে প্রেরণ সহ সি তে সমস্ত কিছু করতে পারেন। কোনও ব্যক্তি প্রথমবারের মতো সি ++ দেখছেন বলতে পারেন, "এই ম্যাজিকটি কী? সংকলকটি কেন স্পষ্টভাবে পাস করছে?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
সম্পাদন করা
এটি বেশ পুরানো প্রশ্ন, তবে এটি এখনও উপার্জন পাচ্ছে, তাই আমি ভেবেছিলাম আমি আরও বিস্তৃত উত্তরের একটি লিঙ্ক যুক্ত করব। আপনি যদি মেটাক্লাস এবং সেগুলির ব্যবহার সম্পর্কে আরও পড়তে চান তবে আমি এটি সম্পর্কে এখানে একটি নিবন্ধ প্রকাশ করেছি ।