Python মধ্যে metaclasses কি কি?




oop python-datamodel (10)

Metaclasses কি কি? আপনি তাদের জন্য কি ব্যবহার করেন?

TLDR: একটি মেটাল্লাস একটি ক্লাসের মতো আচরণের জন্য তাত্ক্ষণিকভাবে সংজ্ঞা দেয় এবং সংজ্ঞা দেয় এবং ইনস্ট্যান্সের আচরণকে সংজ্ঞায়িত করে।

pseudocode:

>>> 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
    
  • অন্য উপায় রাখুন, একটি ক্লাস একটি metaclass একটি উদাহরণ:

    >>> isinstance(object, type)
    True
    
  • একটি তৃতীয় উপায় রাখুন, একটি metaclass একটি বর্গ শ্রেণীর হয়।

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

যখন আপনি একটি বর্গ সংজ্ঞা লেখেন এবং পাইথন এটি কার্যকর করেন, তখন এটি ক্লাস অবজেক্টটি তাত্ক্ষণিক করতে একটি মেটাল্লাস ব্যবহার করে (যা পরিবর্তে, সেই শ্রেণির দৃষ্টান্তগুলি তাত্পর্যপূর্ণ করার জন্য ব্যবহার করা হবে)।

আমরা কীভাবে কাস্টম অবজেক্টের আচরণগুলি ব্যবহার করে তা পরিবর্তন করতে শ্রেণির সংজ্ঞাগুলি ব্যবহার করতে পারি, আমরা ক্লাসের বস্তুর আচরণের ধরন পরিবর্তন করার জন্য একটি মেটালাস ক্লাস সংজ্ঞা ব্যবহার করতে পারি।

তারা কি জন্য ব্যবহার করা যেতে পারে? docs থেকে:

Metaclasses জন্য সম্ভাব্য ব্যবহার সীমাহীন হয়। অনুসন্ধান করা হয়েছে এমন কিছু ধারনা লগিং, ইন্টারফেস চেকিং, স্বয়ংক্রিয় প্রতিনিধিদল, স্বয়ংক্রিয় সম্পত্তি তৈরি, প্রক্সি, ফ্রেমওয়ার্ক এবং স্বয়ংক্রিয় সম্পদ লকিং / সিঙ্ক্রোনাইজেশন অন্তর্ভুক্ত।

তা সত্ত্বেও, ব্যবহারকারীদের জন্য একেবারে প্রয়োজন না হওয়া পর্যন্ত মেটালেসগুলি ব্যবহার করা এড়াতে সাধারণত উত্সাহ দেওয়া হয়।

আপনি যখন একটি ক্লাস তৈরি করেন তখন আপনি একটি মেটাল্লাস ব্যবহার করেন:

যখন আপনি একটি বর্গ সংজ্ঞা লিখেন, উদাহরণস্বরূপ, এটি পছন্দ করেন,

class Foo(object): 
    'demo'

আপনি একটি বর্গ বস্তু তাত্ক্ষণিক।

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

এটি যথাযথ আর্গুমেন্টগুলির সাথে কার্যকরীভাবে কলিংয়ের মতো এবং একই নামের একটি পরিবর্তনশীল ফলাফল প্রদানের মতো একই:

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'})

উভয় ক্ষেত্রে আমরা তৈরি বস্তুর metaclass , type

( __dict__ __module__ __dict__ __module__ __dict__ বিষয়বস্তুর একটি পার্শ্ব-নোট আছে কারণ ক্লাসগুলি অবশ্যই কোথায় সংজ্ঞায়িত করা উচিত, এবং __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) ডাকি help(repr) আমরা জানতে পারি যে __repr__ জন্য একটি ভাল পরীক্ষা আছে যার জন্য সমতার জন্য একটি পরীক্ষা প্রয়োজন - obj == eval(repr(obj)) । আমাদের টাইপ ক্লাসের ক্লাসের __eq__ জন্য __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__)

তাই এখন যখন আমরা এই মেটাল্লাসের সাথে একটি বস্তু তৈরি করি, __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))করা অসম্ভাব্য (কারন ফাংশনগুলি তাদের ডিফল্ট থেকে eval করা অসম্ভব হবে __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')

দ্রষ্টব্য, এই উদাহরণটি docs থেকে অভিযোজিত হয়েছিল - স্ট্যান্ডার্ড লাইব্রেরিতে নতুন enum এই কাজ করে।

সুতরাং আমরা কি একটি ক্লাস তৈরি করে একটি metaclass তাত্ক্ষনিক ছিল। আমরা metaclass চিকিত্সা হিসাবে আমরা অন্য কোন ক্লাস করতে পারেন। এটি একটি পদ্ধতির রেজোলিউশন অর্ডার আছে:

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

এবং এটি প্রায় সঠিক repr(যা আমরা আমাদের ক্রিয়া প্রতিনিধিত্ব করার উপায় খুঁজে পেতে পারব না যতক্ষণ না আমরা আর eval করতে পারেন।):

>>> 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>})

Metaclasses কি এবং আমরা তাদের জন্য কি ব্যবহার করবেন?


বস্তুর হিসাবে ক্লাস

Metaclasses বুঝতে আগে, আপনি পাইথন ক্লাস আয়ত্ত করতে হবে। এবং পাইথনের ছোট্ট ভাষা থেকে ধার করা শ্রেণিগুলির একটি খুব অদ্ভুত ধারণা রয়েছে।

বেশিরভাগ ভাষায়, ক্লাস কেবল কোডের টুকরা যা একটি বস্তু তৈরি করতে বর্ণনা করে। Python খুব যে সত্য সত্য:

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

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

কিন্তু ক্লাস পাইথনের চেয়ে বেশি। ক্লাস খুব বস্তু।

হ্যাঁ, বস্তু।

যত তাড়াতাড়ি আপনি কীওয়ার্ড class ব্যবহার করেন, পাইথন এটি কার্যকর করে এবং একটি OBJECT তৈরি করে। নির্দেশনা

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

স্মৃতিতে একটি বস্তু তৈরি করে যার নাম "ObjectCreator"।

এই বস্তু (বর্গ) নিজেই বস্তু (দৃষ্টান্ত) তৈরি করতে সক্ষম, এবং এটি একটি বর্গ কেন

কিন্তু এখনও, এটি একটি বস্তু, এবং তাই:

  • আপনি একটি পরিবর্তনশীল এটি বরাদ্দ করতে পারেন
  • আপনি এটি অনুলিপি করতে পারেন
  • আপনি এটি গুণাবলী যোগ করতে পারেন
  • আপনি একটি ফাংশন পরামিতি হিসাবে এটি পাস করতে পারেন

উদাহরণ:

>>> 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>

গতিশীল ক্লাস তৈরি করা

যেহেতু ক্লাসগুলি অবজেক্ট, তাই আপনি কোন বস্তুর মতো ফ্লাইতে তাদের তৈরি করতে পারেন।

প্রথমত, আপনি শ্রেণী ব্যবহার করে একটি ফাংশনে একটি বর্গ তৈরি করতে পারেন:

>>> 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>

আপনি লক্ষ্য করবেন যে আমরা ক্লাসের নাম হিসাবে "MyShinyClass" এবং ভেরিয়েবল হিসাবে ক্লাস রেফারেন্স ধরে রাখতে ব্যবহার করি। তারা ভিন্ন হতে পারে, কিন্তু কিছু জটিল করার কারণ নেই।

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 ব্যবহার করেন তখন পাইথন এটি করে এবং এটি একটি মেটাল্লাস ব্যবহার করে এটি করে।

Metaclasses কি (অবশেষে)

Metaclasses ক্লাস তৈরি করে যে 'স্টাফ' হয়।

আপনি বস্তু তৈরি করার জন্য ক্লাস সংজ্ঞায়িত, অধিকার?

কিন্তু আমরা পাইথন ক্লাস বস্তু।

ওয়েল, metaclasses কি এই বস্তু তৈরি। তারা ক্লাস ক্লাস, আপনি তাদের এই ভাবে ছবিতে পারেন:

MyClass = MetaClass()
my_object = MyClass()

আপনি দেখেছেন যে type আপনাকে এমন কিছু করতে দেয়:

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

এটি কারণ ফাংশন type আসলে একটি metaclass হয়। type পিছনে সমস্ত ক্লাস তৈরি করতে মেটালাস পাইথন ব্যবহার করে।

এখন আপনি আশ্চর্য কেন হ্যাক এটি ছোট হাতের অক্ষরে লেখা হয়, না Type ?

আচ্ছা, আমি অনুমান করি এটি স্ট্রিং, স্ট্রিং বস্তু তৈরি করে এমন শ্রেণী এবং পূর্ণসংখ্যা বস্তুগুলি তৈরি করে এমন শ্রেণির সাথে সামঞ্জস্যের বিষয়। type শুধুমাত্র ক্লাস যে বস্তু বস্তু তৈরি করে।

আপনি __class__ বৈশিষ্ট্যটি চেক করে দেখুন।

সবকিছু, এবং আমি সবকিছু মানে, পাইথন একটি বস্তু। যে ints, স্ট্রিং, ফাংশন এবং ক্লাস রয়েছে। তাদের সব বস্তু। এবং তাদের সব একটি ক্লাস থেকে তৈরি করা হয়েছে:

>>> 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__ কি?

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

সুতরাং, একটি মেটাল্লাস শুধুমাত্র স্টাফ যা ক্লাস অবজেক্ট তৈরি করে।

আপনি যদি চান তবে এটি একটি 'ক্লাস কারখানা' বলতে পারেন।

type হল অন্তর্নির্মিত মেটাচ্লাস পাইথন ব্যবহার করে, তবে অবশ্যই, আপনি নিজের মেটালেস তৈরি করতে পারেন।

__metaclass__ বৈশিষ্ট্য

পাইথন 2 এ, আপনি একটি __metaclass__ বৈশিষ্ট্য যোগ করতে পারেন যখন আপনি একটি শ্রেণী লিখেন (পাইথন 3 সিনট্যাক্সের জন্য পরবর্তী বিভাগ দেখুন):

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

যদি আপনি এটি করেন, পাইথ ক্লাস Foo তৈরি করতে মেটালেস ব্যবহার করবেন।

সাবধান, এটা চতুর।

আপনি class Foo(object) প্রথমে লিখেন তবে ক্লাস অবজেক্ট Foo এখনও মেমরিতে তৈরি হয় নি।

পাইথন __metaclass__ শ্রেণীর সংজ্ঞাতে সন্ধান করবে। এটি যদি এটি খুঁজে পায়, এটি অবজেক্ট ক্লাস Foo তৈরি করতে এটি ব্যবহার করবে। এটি না করলে, এটি ক্লাস তৈরির জন্য type ব্যবহার করবে।

যে কয়েক বার পড়ুন।

যখন তুমি কর:

class Foo(Bar):
    pass

পাইথন নিম্নলিখিত কাজ করে:

Foo __metaclass__ বৈশিষ্ট্য আছে?

যদি হ্যাঁ, মেমরিতে একটি বর্গ বস্তু তৈরি করুন (আমি একটি ক্লাস অবজেক্ট বলেছি, এখানে আমার সাথে থাকুন), __metaclass__ যা ব্যবহার করে Foo নাম দিয়ে।

পাইথন __metaclass__ খুঁজে __metaclass__ না, এটি মডিউল স্তরে __metaclass__ সন্ধান করবে এবং একই কাজ করার চেষ্টা করবে (তবে শুধুমাত্র এমন ক্লাসের জন্য যা কিছু প্রাপ্তি না, মূলত পুরাতন-শৈলী ক্লাস)।

তারপরে যদি এটি __metaclass__ কোনও না খুঁজে পায় তবে এটি ক্লাস অবজেক্ট তৈরি করতে Bar (প্রথম পিতামাতা) নিজের মেটালেস (যা ডিফল্ট type হতে পারে) ব্যবহার করবে।

__metaclass__ বৈশিষ্ট্যটি উত্তরাধিকারসূত্রে প্রাপ্ত হবে না __metaclass__ সতর্ক থাকুন, পিতামাতার Bar.__class__ ( Bar.__class__ ) হবে। যদি Bar একটি __metaclass__ বৈশিষ্ট্য ব্যবহার করে যা Bar type() type.__new__() এবং type.__new__() নয়, উপশ্রেণীগুলি সেই আচরণের উত্তরাধিকারী হবে না।

এখন বড় প্রশ্ন হচ্ছে, আপনি __metaclass__ কী __metaclass__ ?

উত্তর হল: একটি বর্গ তৈরি করতে পারেন যে কিছু।

এবং কি একটি বর্গ তৈরি করতে পারেন? type , বা subclasses বা এটি ব্যবহার করে যে কিছু।

পাইথন 3 মধ্যে Metaclasses

পাইথন 3 এ মেটালেস সেট করতে সিনট্যাক্স পরিবর্তন করা হয়েছে:

class Foo(object, metaclass=something):
    [...]

অর্থাৎ __metaclass__ এট্রিবিউটটি আর ব্যবহৃত হয় না, মূল ক্লাসের তালিকায় একটি কীওয়ার্ড আর্গুমেন্টের পক্ষে।

Metaclasses আচরণ যদিও মূলত একই থাকে।

কাস্টম metaclasses

একটি মেটাল্লাসের প্রধান উদ্দেশ্যটি তৈরি হওয়ার পরে স্বয়ংক্রিয়ভাবে ক্লাস পরিবর্তন করা হয়।

আপনি সাধারণত API এর জন্য এটি করেন, যেখানে আপনি বর্তমান প্রসঙ্গের সাথে মেলে এমন ক্লাস তৈরি করতে চান।

একটি মূঢ় উদাহরণ কল্পনা করুন, যেখানে আপনি সিদ্ধান্ত নিবেন যে আপনার মডিউলের সমস্ত ক্লাসগুলির উচ্চতর অক্ষরে লিখিত বৈশিষ্ট্যগুলি থাকা উচিত। এটি করার বিভিন্ন উপায় রয়েছে, তবে এক উপায় হল __metaclass__ মডিউল স্তরে সেট করা।

এই ভাবে, এই মেটাল্লাসটি ব্যবহার করে এই মডিউলটির সমস্ত ক্লাস তৈরি করা হবে এবং আমাদের সমস্ত বৈশিষ্টগুলিকে বড় হাতের অক্ষরে পরিণত করতে মেটালেসকে কেবল বলতে হবে।

সৌভাগ্যক্রমে, __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)

কিন্তু এই সত্যিই OOP হয় না। আমরা সরাসরি type কল করি এবং আমরা __new__ বা parent __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 মতো, সমস্ত আর্গুমেন্টগুলির প্রচলিত নাম আছে। সুতরাং একটি বাস্তব উত্পাদন metaclass এই মত দেখতে হবে:

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)

এটাই. Metaclasses সম্পর্কে সত্যিই কিছুই নেই।

Metaclasses ব্যবহার করে কোড জটিলতা জটিলতার পিছনে কারণ metaclasses হয় না, কারণ আপনি সাধারণত introspection উপর নির্ভর twisted জিনিস, উত্তরাধিকার manipulating, যেমন __dict__ ইত্যাদি vars করতে __dict__

প্রকৃতপক্ষে, ম্যাটাক্ল্যাসগুলি কালো জাদু কাজ বিশেষ করে দরকারী, এবং অতএব জটিল উপাদান। কিন্তু নিজেদের দ্বারা, তারা সহজ:

  • একটি ক্লাস সৃষ্টি বাধা
  • ক্লাস পরিবর্তন করুন
  • সংশোধিত ক্লাস ফিরে

কেন আপনি ফাংশন পরিবর্তে metaclasses ক্লাস ব্যবহার করবে?

যেহেতু __metaclass__ কোন __metaclass__ গ্রহণ করতে পারে তাই, কেন আপনি এটি অবশ্যই আরো জটিল হিসাবে একটি ক্লাস ব্যবহার করবেন?

তাই করার জন্য বিভিন্ন কারণ আছে:

  • অভিপ্রায় স্পষ্ট। যখন আপনি UpperAttrMetaclass(type) পড়েন, তখন আপনি কি অনুসরণ করতে যাচ্ছেন তা জানেন
  • আপনি OOP ব্যবহার করতে পারেন। Metaclass মেটাল্লাস থেকে উত্তরাধিকারী করতে পারেন, পিতামাতার পদ্ধতি override। Metaclasses এমনকি metaclasses ব্যবহার করতে পারেন।
  • যদি আপনি একটি মেটালেস-ক্লাস নির্দিষ্ট করেন তবে একটি মেটালাস-ফাংশন সহ কোন শ্রেণীর উপশ্রেণীগুলি তার মেটালাসগুলির উদাহরণ হবে।
  • আপনি আপনার কোড ভাল গঠন করতে পারেন। আপনি উপরের উদাহরণ হিসাবে তুচ্ছ কিছু জন্য metaclasses ব্যবহার করবেন না। এটা জটিল কিছু জন্য সাধারণত। বিভিন্ন পদ্ধতি তৈরি করার এবং এক শ্রেণিতে তাদের গোষ্ঠী করার ক্ষমতা থাকা কোডটি সহজে পড়ার জন্য খুব দরকারী।
  • আপনি __new__ , __init__ এবং __call__ হুক করতে পারেন। যা আপনি বিভিন্ন স্টাফ করতে পারবেন। এমনকি যদি আপনি এটি __new__ সব করতে পারেন তবে কিছু লোক __init__ ব্যবহার করে আরও বেশি আরামদায়ক।
  • এই metaclasses বলা হয়, এটা অসম্মান! এটা কিছু বলতে হবে!

কেন আপনি metaclasses ব্যবহার করবেন?

এখন বড় প্রশ্ন। কেন আপনি কিছু অস্পষ্ট ত্রুটি প্রবণ বৈশিষ্ট্য ব্যবহার করবে?

ভাল, সাধারণত আপনি না:

মেটাল্লাসগুলি গভীর জাদু যা 99% ব্যবহারকারীদের কখনই চিন্তা করা উচিত নয়। আপনি যদি তাদের প্রয়োজন বোধ করেন, তবে আপনি তা করেন না (আপনি যেসব লোকের আসলে তাদের দরকার সেগুলি তাদের অবশ্যই দরকার, এবং কেন সে সম্পর্কে ব্যাখ্যা করার দরকার নেই)।

পাইথন গুরু টিম Peters

একটি মেটাল্লাসের জন্য প্রধান ব্যবহার কেস একটি API তৈরি করা হয়। এই একটি সাধারণ উদাহরণ Django ORM হয়।

এটি আপনাকে এইরকম কিছু সংজ্ঞায়িত করতে দেয়:

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

কিন্তু আপনি যদি এটি করেন:

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

এটি একটি IntegerField অবজেক্ট ফিরিয়ে দেবে না। এটা একটি int ফিরে আসবে, এবং এমনকি ডাটাবেস থেকে এটি সরাসরি নিতে পারেন।

এটি সম্ভব কারণ models.Model __metaclass__ models.Model সংজ্ঞায়িত __metaclass__ এবং এটি এমন কিছু যাদু ব্যবহার করে যা আপনি সহজ বিবৃতি দিয়ে সংজ্ঞায়িত Person একটি ডাটাবেস ক্ষেত্রের জটিল হুকের মধ্যে সংশোধন করে।

Django একটি সহজ এপিআই প্রকাশ করে এবং মেটাল্লাস ব্যবহার করে কিছু জটিল চেহারা সহজ করে, এই API থেকে কোড পুনঃপ্রবর্তন করে দৃশ্যগুলির পিছনে আসল কাজ করে।

শেষ কথা

প্রথমত, আপনি জানেন যে ক্লাস এমন বস্তু যা দৃষ্টান্ত তৈরি করতে পারে।

ওয়েল আসলে, ক্লাস নিজেদের উদাহরণ। Metaclasses এর।

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

সবকিছু পাইথন একটি বস্তু, এবং তারা উভয় ক্লাস বা metaclasses দৃষ্টান্তের উদাহরণ হয়।

type ছাড়া।

type আসলে তার নিজস্ব metaclass হয়। এটি এমন কিছু নয় যা আপনি বিশুদ্ধ পাইথনে পুনরুত্পাদন করতে পারেন এবং বাস্তবায়ন পর্যায়ে প্রতারণার দ্বারা এটি করা হয়।

দ্বিতীয়ত, metaclasses জটিল। আপনি খুব সহজ ক্লাস পরিবর্তন জন্য তাদের ব্যবহার করতে চান না। আপনি দুটি ভিন্ন কৌশল ব্যবহার করে ক্লাস পরিবর্তন করতে পারেন:

আপনি ক্লাস পরিবর্তন প্রয়োজন 99% সময়, আপনি এই ব্যবহার বন্ধ ভাল।

কিন্তু 98% সময়, আপনাকে অবশ্যই ক্লাস পরিবর্তন প্রয়োজন হবে না।


Tl; dr সংস্করণ

type(obj)ফাংশন আপনি একটি বস্তুর টাইপ পায়।

type()একটি বর্গ তার হয় ক্লাসের অধীনে একটি ক্লাস

একটি metaclass ব্যবহার করতে:

class Foo(object):
    __metaclass__ = MyMetaClass

Metaclasses জন্য এক ব্যবহার স্বয়ংক্রিয়ভাবে একটি উদাহরণে নতুন বৈশিষ্ট্য এবং পদ্ধতি যোগ করা হয়।

উদাহরণস্বরূপ, আপনি যদি Django মডেলগুলি দেখেন তবে তাদের সংজ্ঞাটি একটু বিভ্রান্তিকর মনে হচ্ছে। মনে হচ্ছে আপনি কেবল শ্রেণির বৈশিষ্ট্যাবলী নির্ধারণ করছেন:

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

যাইহোক, রানটাইম সময়ে ব্যক্তি বস্তুগুলি সব ধরণের কার্যকর পদ্ধতিতে ভরা হয়। কিছু আশ্চর্যজনক metaclassery জন্য source দেখুন।


আমি মনে করি মেটাল্লাস প্রোগ্রামিংতে ONLamp ভূমিকাটি ভালভাবে লেখা হয়েছে এবং ইতিমধ্যে বহু বছর বয়সী হওয়া সত্ত্বেও বিষয়টিকে সত্যিই ভাল ভূমিকা দেয়।

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html ( https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html এ সংরক্ষণাগারভুক্ত https://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html )

সংক্ষেপে: একটি বর্গ একটি উদাহরণ তৈরির জন্য একটি নিদর্শন, একটি মেটালাস একটি বর্গ তৈরির জন্য একটি মূর্তক। এটি সহজেই দেখা যায় যে পাইথন ক্লাসগুলিতে এই আচরণটি সক্ষম করার জন্য প্রথম শ্রেণীর বস্তুগুলিও হতে হবে।

আমি নিজেকে কখনোই লিখিত করেছি না, তবে আমি মনে করি জ্যান্তাক্ল্যাসগুলির সবচেয়ে নিকৃষ্টতম ব্যবহারগুলির মধ্যে একটি Django ফ্রেমওয়ার্ক দেখা যায়। মডেল ক্লাস নতুন মডেল বা ফর্ম ক্লাস লেখার একটি ঘোষণামূলক শৈলী সক্রিয় করার জন্য একটি মেটালাস পদ্ধতি ব্যবহার করুন। মেটাল্লাস ক্লাস তৈরি করছে, তবে সব সদস্যই ক্লাসটিকে নিজের কাস্টমাইজ করার সম্ভাবনা পায়।

যা বলা বাকি আছে তা হল: যদি আপনি না জানেন যে metaclasses কি, আপনি তাদের প্রয়োজন হবে না সম্ভাবনা 99%।


একটি metaclass একটি বর্গ শ্রেণীর হয়। একটি বর্গের মতো ক্লাসের একটি উদাহরণ কীভাবে আচরণ করে তা সংজ্ঞায়িত করে, একটি মেটাল্লাস কীভাবে একটি বর্গ আচরণ করে তা সংজ্ঞায়িত করে। একটি ক্লাস একটি metaclass একটি উদাহরণ।

পাইথনটিতে আপনি মেটাল্লাসের জন্য নির্বিচারে কল্যাবলগুলি ব্যবহার করতে পারেন (যেমন Jerub শোগুলি), আরও কার্যকর পদ্ধতি আসলে এটি একটি প্রকৃত বর্গ তৈরি করতে হয়। type পাইথন মধ্যে সাধারণ মেটাল্লাস। যদি আপনি হতাশ হন, হ্যাঁ, type নিজেই একটি বর্গ, এবং এটি নিজস্ব টাইপ। আপনি পাইথন-এ সম্পূর্ণরূপে type মতো কিছু পুনরায় তৈরি করতে পারবেন না, তবে পাইথন কিছুটা প্রতারণা করে। পাইথন আপনার নিজস্ব মেটাল্লাস তৈরি করতে আপনি সত্যিই type subclass করতে চান।

একটি মেটালাস সাধারণত একটি ক্লাস-কারখানা হিসাবে ব্যবহৃত হয়। আপনি ক্লাসটিকে কল করে ক্লাসের একটি উদাহরণ তৈরি করেন, পাইথন মেটাল্লাস কল করে একটি নতুন বর্গ তৈরি করে (যখন এটি 'বর্গ' বিবৃতিটি চালায়)। স্বাভাবিক __init__ এবং __new__ পদ্ধতিগুলির সাথে মিলিত হলে, __new__ আপনাকে ক্লাস তৈরি করার সময় 'অতিরিক্ত জিনিসগুলি' করার অনুমতি দেয়, যেমন কোন নিবন্ধটি সহ নতুন শ্রেণিটি নিবন্ধন করা, বা এমনকি পুরোপুরি অন্য কিছু দিয়ে শ্রেণিকে প্রতিস্থাপন করতে।

যখন class বিবৃতিটি কার্যকর করা হয়, পাইথন প্রথমে class বিবৃতির একটি সাধারণ কোড হিসাবে ব্লকটি চালায়। ফলে নামস্থান (একটি স্বতন্ত্র) শ্রেণী-থেকে-হতে বৈশিষ্ট্যাবলী ধারণ করে। মেটাল্লাসটি ক্লাস-টু-বি (মেট্যাক্ল্যাসগুলি উত্তরাধিকারসূত্রে প্রাপ্ত) এর __metaclass__ দিকে লক্ষ্য করে __metaclass__ হয়, __metaclass__ শ্রেণীর-থেকে-হতে (যদি থাকে) বৈশিষ্ট্য বা __metaclass__ গ্লোবাল ভেরিয়েবলের বৈশিষ্ট্য। মেটাল্লাসটি তারপর তা তাত্ক্ষণিকভাবে শ্রেণীটির নাম, ঘাঁটি এবং গুণাবলীগুলির সাথে ডাকা হয়।

যাইহোক, মেটাল্লাসগুলি আসলে কেবলমাত্র একটি ফ্যাক্টরিতে নয়, এটির জন্য একটি শ্রেণির ধরন সংজ্ঞায়িত করে, যাতে আপনি তাদের সাথে আরও কিছু করতে পারেন। উদাহরণস্বরূপ, আপনি মেটাল্লাসে স্বাভাবিক পদ্ধতিগুলি সংজ্ঞায়িত করতে পারেন। এই metaclass- পদ্ধতি শ্রেণীকক্ষের মত, তারা একটি উদাহরণ ছাড়াই ক্লাসে বলা যেতে পারে, কিন্তু তারা ক্লাসের উদাহরণে বলা যায় না যে ক্লাস class পদ্ধতি মত হয় না। 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__(mcls, 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, mcls).__new__(mcls, 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__

দ্রষ্টব্য, এই উত্তরটি পাইথন 2.x এর জন্য ২008 সালে লেখা হয়েছিল, মেটাল্লাসগুলি 3.x তে সামান্য ভিন্ন, মন্তব্য দেখুন।

Metaclasses 'বর্গ' কাজ করে যা গোপন সস। একটি নতুন শৈলী বস্তুর জন্য ডিফল্ট মেটালাসকে 'টাইপ' বলা হয়।

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

Metaclasses 3 Args নিতে। ' নাম ', ' বেস ' এবং ' dict '

গোপন শুরু যেখানে এখানে। নাম, ঘাঁটি এবং dict এই উদাহরণ বর্গ সংজ্ঞা থেকে আসে যেখানে জন্য সন্ধান করুন।

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 দ্বারা 'Initalised' লাভের যাদু আচরণটি Initalised একটি উপশ্রেণীতে প্রেরণ করা হয় না।

এখানে আরও একটি কংক্রিট উদাহরণ রয়েছে যা দেখায় যে ক্লাস তৈরি করার সময় কোনও কার্য সম্পাদনকারী মেটাল্লাস তৈরি করতে আপনি কীভাবে 'টাইপ' উপবিষয় করতে পারেন। এটি বেশ চতুর:

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

পাইথন 3 আপডেট

একটি metaclass মধ্যে দুটি মূল পদ্ধতি আছে (এই সময়ে):

  • __prepare__ , এবং
  • __new__

__prepare__OrderedDictক্লাস তৈরি করার সময় আপনি নামস্থান হিসাবে ব্যবহার করার জন্য একটি কাস্টম ম্যাপিং (যেমন একটি ) সরবরাহ করতে দেয় । আপনি যে কোনও নামস্পেসের পছন্দ করে নিন তা অবশ্যই আপনাকে অবশ্যই ফেরত দিতে হবে। আপনি __prepare__একটি স্বাভাবিক বাস্তবায়ন না হলে dictব্যবহার করা হয়।

__new__ চূড়ান্ত বর্গ প্রকৃত সৃষ্টি / সংশোধন জন্য দায়ী।

একটি বেয়ার-হাড়, কি-কিছুই-অতিরিক্ত মেটাল্লাস চাইবেন:

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)

আপনি দেখতে পারেন, আপনি বৈশিষ্ট্যাবলীটির নাম দুবার পুনরাবৃত্তি করতে হবে। এই বিরক্তিকর বাগ সঙ্গে সম্ভব typos তোলে।

একটি সহজ 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')

দ্রষ্টব্য : এই উদাহরণটি সহজ যে এটি একটি শ্রেণিবদ্ধ সজ্জার সাথেও সম্পন্ন করা যেতে পারে তবে সম্ভবত একটি প্রকৃত মেটাল্লাস আরো অনেক কিছু করা হবে।

রেফারেন্সের জন্য 'বৈধতা টাইপ' ক্লাস:

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

টাইপ () ফাংশন একটি বস্তুর ধরন ফেরত বা একটি নতুন ধরনের তৈরি করতে পারেন,

উদাহরণস্বরূপ, আমরা টাইপ () ফাংশন সহ একটি হাই ক্লাস তৈরি করতে এবং ক্লাস হাই (অবজেক্ট) সহ এই ভাবে ব্যবহার করার প্রয়োজন নেই:

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

গতিশীলভাবে ক্লাসগুলি তৈরি করার জন্য টাইপ () ব্যবহার করার পাশাপাশি, আপনি শ্রেণির সৃষ্টি আচরণ নিয়ন্ত্রণ করতে এবং মেটালাস ব্যবহার করতে পারেন।

পাইথন অবজেক্ট মডেলের মতে, শ্রেণীটি বস্তু, তাই ক্লাস অবশ্যই অন্য নির্দিষ্ট শ্রেণীর একটি উদাহরণ হতে হবে। ডিফল্টরূপে, একটি পাইথন ক্লাস টাইপ ক্লাসের উদাহরণ। অর্থাৎ, অন্তর্নির্মিত শ্রেণীর অধিকাংশ মেটাল্লাস এবং ব্যবহারকারী-সংজ্ঞায়িত ক্লাসগুলির মেটাল্লাস টাইপ।

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

যখন আমরা মেটাল্লাসে কীওয়ার্ড আর্গুমেন্ট পাস করি তখন ম্যাজিক কার্যকর হবে, এটি তালিকামেটাক্লাসের মাধ্যমে কাস্টমলস্ট তৈরি করতে পাইথন ইন্টারপ্রেটারটিকে নির্দেশ করে। নতুন (), এই সময়ে, আমরা ক্লাস সংজ্ঞা সংশোধন করতে পারি, উদাহরণস্বরূপ, এবং একটি নতুন পদ্ধতি যোগ করে সংশোধিত সংজ্ঞাটি ফেরত পাঠাতে পারি।


পাইথন ক্লাস নিজেই বস্তু - উদাহরণস্বরূপ - তাদের মেটা-ক্লাসের।

ডিফল্ট মেটাল্লাস, যখন আপনি ক্লাস নির্ধারণ করেন তখন প্রয়োগ করা হয়:

class foo:
    ...

মেটা ক্লাস ক্লাসের একটি সম্পূর্ণ সেট কিছু নিয়ম প্রয়োগ করতে ব্যবহৃত হয়। উদাহরণস্বরূপ, অনুমান করুন যে আপনি একটি ডাটাবেস অ্যাক্সেস করতে একটি ORM তৈরি করছেন এবং আপনি প্রতিটি টেবিলের রেকর্ডগুলি টেবিলে (ক্ষেত্র, ব্যবসায়ের নিয়ম, ইত্যাদি উপর ভিত্তি করে) শ্রেণিবদ্ধ একটি শ্রেণির হতে চান, মেটালাসের সম্ভাব্য ব্যবহার উদাহরণস্বরূপ, সংযোগ পুল যুক্তি, যা সব টেবিল থেকে রেকর্ডের সব ক্লাসের দ্বারা ভাগ করা হয়। অন্য একটি ব্যবহার বিদেশী কী সমর্থন করার যুক্তি, যা রেকর্ড একাধিক ক্লাস জড়িত।

যখন আপনি মেটাল্লাস সংজ্ঞায়িত করেন, তখন আপনি subclass টাইপ করেন এবং আপনার যুক্তি যুক্ত করতে নিম্নলিখিত যাদু পদ্ধতিগুলিকে ওভাররাইড করতে পারেন।

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 জন্য কোথাও কাছাকাছি এবং সম্পূর্ণ তালিকা নেই।





python-datamodel