Pythonda metaclasses hansılardır?

Metaclasses hansılardır və nədən istifadə edirik?

4879
19 сент. e-tövsiyə 19 Sentyabr. 2008-09-19 09:10 '08 saat 09:10 'da 2008-09-19 09:10
@ 1 cavab

Metaclass bir sinif sinifidir. Bir sinif sinif nümunəsinin davranışını müəyyən edirsə, metaclass bir sinifin davranışını təyin edir. Bir sinif bir metaclass nümunəsidir.

Python-da metaclasses (Yerubda göstərildiyi kimi) üçün özbaşına çağırışlar istifadə edə bilərsiniz, daha faydalı bir yanaşma həqiqətən sinfi özü etməkdir. type Pythonda müntəzəm metaclassdir. Əgər maraqlıysanız, bəli, type özü bir sinifdir və bu da öz növbəsidir. Python-da yalnız bir type kimi bir şey yarada bilməzsiniz, lakin Python bir az aldadıcı olur. Python'da öz meta sinifinizi yaratmaq üçün yalnız bir type alt sinfi istifadə etmək istəyir.

Metaclass ən çox sinif fabriki olaraq istifadə olunur. Bir sinif çağıraraq bir sinif örneği yaratdığınız kimi, Python metaclass çağıraraq yeni bir sinif ('sinif' operatorunu icra edərkən) yaradır. __init__ üsulları __init____new__ metaclasses ilə birlikdə, beləliklə, bir sinifdə yeni bir sinif qeydiyyatı və ya hətta tamamilə başqa bir şey ilə əvəz kimi bir sinif __new__ "əlavə şeylər" yarada.

Bir class bəyanatı icra edildikdə, Python ilk növbədə class bəndini normal bir kod bloku kimi icra edir. Nəticədə çıxan ad sahəsi (dict) gələcək sinifin xüsusiyyətlərini ehtiva edir. __metaclass__ , gələcək sinifin əsas siniflərini (metaclasses miras alınmış), sinifin __metaclass__ xüsusiyyətini (əgər varsa) və ya qlobal dəyişən __metaclass__ . Metaclass sonra nümunə yaratmaq üçün adı, əsasları və sinif öznitelikleri ilə çağırılır.

Lakin, metaclasses həqiqətən bir sinif növü müəyyən, bunun üçün bir fabrik deyil, belə ki, onlarla daha çox edə bilərsiniz. Məsələn, bir metaclassda normal metodları müəyyən edə bilərsiniz. Bu metaclass metodları sinfi üsullarına bənzəyir, çünki onlar bir sinif olmadan bir sinifdə çağırılsınlar, amma sinif metodları kimi deyil, çünki bir sinifdə bir nümunədə çağırılamazlar. type.__subclasses__() type metaclass metodunun nümunəsidir. Bir sinifin davranışını tətbiq etmək və ya dəyişdirmək üçün __add__ , __iter____getattr__ kimi adi "sehrli" üsulları da müəyyən edə bilərsiniz.

Bitsin toplanmış nümunəsi:

 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 called 'AutoClass' print ExampleSibling print ExampleSibling.__mro__ 
2286
19 сент. Thomas Wouters Sep 19- ə cavab verin 2008-09-19 10:01 '08 saat 10:01 'da 2008-09-19 10:01

Obyektlər kimi dərslər

Metaclasses anlamaq əvvəl Python dərsləri master lazımdır. Python, Smalltalk dilindən hansı dərslərə borclu olduğuna dair çox təəccüblü bir fikirə sahibdir.

Çox dildə dərslər sadəcə bir obyektin necə yaradılacağını təsvir edən kod parçalarıdır. Bu Python da doğrudur:

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

Amma dərslər pitondan daha böyükdür. Dərslər də obyektlərdir.

Bəli, obyektlər.

class açar sözünü istifadə etdiyiniz kimi, Python onu icra edir və bir obyekt yaratır. Təlimat

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

"ObjectCreator" adlı yaddaşda bir obyekt yaradır.

Bu obyekt (sinif) obyektlərin (nüsxələrin) yaranmasına qadirdir və buna görə də bir sinifdir .

Amma yenə də bu bir obyektdir və buna görə:

  • Bunu bir dəyişənə təyin edə bilərsiniz.
  • Bunu kopyalayabilirsiniz
  • Siz ona atributlar əlavə edə bilərsiniz.
  • Bir funksiya parametri olaraq qəbul edə bilərsiniz.

məsələn:

 >>> print(ObjectCreator) # you can print a class because it 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> 

Dərsləri dinamik şəkildə yaradır

Dərslər obyektlər olduğundan, hər hansı bir obyekt kimi, onları tezliklə yarada bilərsiniz.

Birincisi, sinfi istifadə edərək funksiyanı bir sinif yarada bilərsiniz:

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

Amma bu qədər dinamik deyil, çünki hələ də bütün sinfi özünüz yazmalısınız.

Dərslər obyekt olduğundan, onlar bir şey tərəfindən yaradılmalıdır.

class açar sözünü istifadə etdikdə, Python bu obyekti avtomatik olaraq yaradır. Lakin, Python-da ən çox şeylər kimi, əl ilə bunu etmək imkanı verir.

Funksiyanın type yadda saxla? Nişan tipini öyrənməyə imkan verən yaxşı köhnə funksiya:

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

Yaxşı, type tamamilə fərqli bir qabiliyyətə malikdir, həmçinin siniflərdə dərslər yarada bilər. type bir parametr kimi bir sinif təsviri ala və bir sinifə dönə bilər.

(Bu funksiyaya keçdiyiniz parametrlərə görə eyni funksiyanı iki tamamilə fərqli istifadə edə biləcəyini bilirəm.Bu Python-da geri uyğunluq səbəbiylə problemdir)

type aşağıdakı kimi işləyir:

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

məsələn:

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

əl ilə bu şəkildə yaradıla bilər:

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

Siz "MyShinyClass" ı sinfi adını və sinifə istinad saxlamağı dəyişən olaraq istifadə etdiyini görəcəksiniz. Onlar fərqli ola bilər, amma şeyi çətinləşdirməyə heç bir səbəb yoxdur.

type , sinif xüsusiyyətlərini müəyyən etmək üçün lüğət alır. Beləliklə:

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

Tərcümə edə bilərsiniz:

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

Və normal bir sinif kimi istifadə olunur:

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

Əlbəttə ki, ondan miras ala bilərsiniz, belə ki:

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

olacaqdır:

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

Nəticədə, sinifə metodlar əlavə etmək istəyirəm. Sadəcə düzgün imza ilə funksiyanı müəyyənləşdirin və bir atribut kimi təyin edin.

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

Bir dinamik olaraq yaradan, adətən yaradılmış sinif obyektinə üsullar əlavə etməklə, daha çox üsul əlavə edə bilərsiniz.

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

Gittiğimiz yeri görürsünüz: Python'da, dərslər obyektlərdir və dinamik olaraq siniflərdə siniflər yarada bilərsiniz.

class açar sözünü istifadə edərkən Python bunu edir və bu metaclass istifadə edilir.

Metaclasses (nəhayət)

Metaclasses dərsləri yaradan "material" dir.

Siz obyektlərin yaradılması üçün dərsləri müəyyən edirsiniz?

Lakin biz Python dərsləri obyektlər olduğunu öyrəndik.

Yaxşı, metaclasses bu obyektlərin yaratmaq nədir. Bunlar dərs dərsləri, bunları aşağıdakı kimi təqdim edə bilərsiniz:

 MyClass = MetaClass() my_object = MyClass() 

Bu type belə bir şey etməyə imkan verdiyini gördünüz:

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

Çünki funksiyanın type əslində bir metaklasdır. Python səhnələri arxasında bütün siniflər yaratmaq üçün istifadə etdiyi metaclassdir.

İndi təəccüb edirsiniz, cəhənnəm nədir?

Bəli, məncə, str , string obyektləri yaradan sinif və int ilə tamsayı obyektləri yaradan sinif ilə uyğunluq məsələsidir. type yalnız sinif obyektləri yaradan bir sinifdir.

Bunu __class__ yoxlayaraq görürsünüz.

Hər şey, demək hər şey Pythonda bir obyektdir. Bu, tamsayılar, simlər, funksiyalar və siniflər daxildir. Bütün bunlar obyektlərdir. Bütün bunlar sinifdən yaradılmışdı:

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

Hər __class__ nədir?

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

Beləliklə, metaclass yalnız bir sinif obyektlərini yaradan maddədir.

Istədiyiniz halda onu bir "sinif fabriki" deyə bilərsiz.

type Python istifadə edən daxili metaklasdır, lakin, əlbəttə ki, öz metaslasınızı yarada bilərsiniz.

__metaclass__

Python 2-də, bir sinif yazarkən __metaclass__ əlavə edə bilərsiniz (Python 3 sözdiziminin aşağıdakı hissəsinə baxın):

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

Əgər bunu yaparsanız, Python Foo sinifi yaratmaq üçün metaclass istifadə edəcək.

Diqqətlə, bu çətindir.

Əvvəl class Foo(object) , ancaq Foo sinif obyekti hələ yaddaşda yaradılmır.

Python sinif tərifində __metaclass__ . O tapsa, Foo obyektlərinin bir sinfi yaratmaq üçün istifadə edəcək. Əgər deyilsə, sinif yaratmaq üçün tipdən istifadə edəcəkdir.

border=0

Bunu bir neçə dəfə oxuyun.

Bunu etdiyiniz zaman:

 class Foo(Bar): pass 

Python aşağıdakıları edir:

__metaclass__ xüsusiyyət varmı?

Əgər belədirsə, __metaclass____metaclass__ istifadə edərək, yaddaşda bir sinif obyekti yaradın (mən bir sinfi obyekt dedim, mənimlə birlikdə __metaclass__ .

Python __metaclass__ tapa __metaclass__ , MODULE səviyyəsində __metaclass__ üçün __metaclass__ və eyni şeyi etməyə çalışacaq (lakin yalnız heç bir şeyi, daha çox köhnə tərzdə dərsləri devraldıran dərslər üçün).

Sonra, __metaclass__ tapa __metaclass__ sinif obyekti yaratmaq üçün öz metaclass Bar (ilk valideyn) istifadə edəcək (default type ola bilər).

Burada diqqətli olmalı, __metaclass__ özniteliği __metaclass__ miras __metaclass__ və valideynin ( Bar.__class__ ) Bar.__class__ olacaqdır. Bar type() ( type.__new__() ) ilə deyil, yaradılan __metaclass__ funksiyasını istifadə __metaclass__ , alt siniflər bu davranışı devralmayacaqdır.

İndi böyük sual __metaclass__ nə qoymaq olar?

Cavab: bir sinif yarada bilən bir şey.

Nə sinf yarada bilər? type və ya subclasses və ya istifadə edən bir şey.

Pythonda metaclasses 3

Meta sinifini təyin etmək üçün sintaksis Python 3:

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

yəni, __metaclass__ atributu artıq əsas siniflər siyahısında əsas sübutun lehinə istifadə edilmir.

Lakin metaclasses davranışı əsasən eyni qalır.

Python 3-də metaclasses-ə əlavə olunan bir şey, beləliklə, öznitelikləri sözdəki argumentlər kimi bir metaclass kimi qəbul edə bilərsiniz:

 class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2): ... 

Python'un bunun necə olduğunu öyrənmək üçün aşağıdakı bölümü oxuyun.

Xüsusi metaclasses

Metaclassın əsas məqsədi yaradıldığı zaman sinfi avtomatik olaraq dəyişdirməkdir.

Siz adətən mövcud kontekst uyğun olan dərslər yaratmaq istədiyiniz API üçün bunu edərsiniz.

Modulunuzdakı bütün dərslərin yuxarı vəziyyətdə yazılmış xüsusiyyətlərə malik olmasına qərar verdiyiniz zaman axmaq bir nümunə düşünün. Bunu etmək üçün bir neçə yol vardır, amma bunlardan biri modul səviyyəsində __metaclass__ .

Beləliklə, bu modulun bütün sinifləri bu metaklasdan istifadə etməklə yaradılacaq və biz bütün metodları müəyyən etmək lazımdır, belə ki, bütün xüsusiyyətlər yuxarı vəziyyətə çevrilir.

Xoşbəxtlikdən, __metaclass__ həqiqətən hər hansı bir zəng edə bilər, rəsmi bir sinif olmamalıdır (adındakı 'sinif' adı ilə bir şeyin bir sinif olması lazım olduğunu anlayıram ... ancaq bu faydalıdır).

Beləliklə, funksiyanı istifadə edərək sadə bir nümunə ilə başlayırıq.

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

İndi eyni edək, lakin metaclass üçün real sinfi istifadə edin:

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

Amma bu, FKÖ deyil. Biz __new__ deyil, __new__ deyirik və ya ana __new__ . Bunu edək:

 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) 

Əlavə arqumenti upperattr_metaclass . Bu barədə xüsusi bir şey yoxdur: __new__ həmişə birinci parametr kimi müəyyən olunan sinfi alır. Birinci parametr kimi nümunə almaq və ya sinif metodları üçün müəyyən bir sinif olan normal metodlar üçün özünüz var.

Əlbəttə ki, mən burada istifadə olunan adlar dəqiqliklə uzanıram, amma self üçün bütün arqumentlərin şərti adları var. Beləliklə, real istehsal metaclass belə görünür:

 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) 

Üstəlik, daha asanlıqla istifadə etməklə onu daha da təmizləyə bilərik (buna görə də, bəli, siz metaclasses ola bilər, metaclasses dən miras, növü inheriting) var:

 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) 

Bəli, və Python 3-də, əgər bu zəngləri söz arqumentləri ilə, məsələn:

 class Foo(object, metaclass=Thing, kwarg1=value1): ... 

Bu onu istifadə etmək üçün bir metaclass halına çevirir:

 class Thing(type): def __new__(class, clsname, bases, dct, kwargs1=default): ... 

Odur. Metaclasses başqa bir şey yoxdur.

Metaclasses istifadə kodun mürəkkəbliyi səbəb metaclasses deyil, ancaq, həqiqətən, burulma üçün metaclasses istifadə, həqiqətən, introspection, miras manipulyasiya, __dict__ kimi __dict__ və s.

Həqiqətən, metaclasses xüsusilə qara sehrli və buna görə də kompleks şeylər üçün faydalıdır. Ancaq özləri tərəfindən sadədirlər.

  • sinifin yaradılması
  • dəyişdirmək sinfi
  • dəyişdirilmiş sinfi qaytarır

Nə funksiyalar yerinə metaclass dərsləri istifadə edirsiniz?

__metaclass__ hər hansı bir çağırılabilir __metaclass__ qəbul edə bilər ildən, niyə daha mürəkkəbdir, çünki sinfi istifadə etmək lazımdır?

Bunun bir neçə səbəbi var:

  • Niyyət aydındır. UpperAttrMetaclass(type) oxuduğunuzda, aşağıdakıları bilirsiniz.
  • OOP-dan istifadə edə bilərsiniz. Metaclass, metaclassdan devrala bilər, ana metodları ləğv edə bilər. Metaclasses hətta metaclasses istifadə edə bilərsiniz.
  • Bir sinifin alt sinfi, metaclass sinifini təyin etsəniz metaclass funksiyasiyla deyil, onun metaklas nümunasidir.
  • Kodunuzu daha yaxşı qura bilərsiniz. Siz yuxarıdakı nümunə kimi əhəmiyyətsiz bir şey üçün metaclasses istifadə etməyin. Bu, adətən, çətin bir şeydir. Bir neçə metoddan istifadə etmək və onları bir sinifə toplamaq bacarığı kodu oxumaq asanlaşdırmaq üçün çox faydalıdır.
  • __new__ , __init____call__ __init__ qoşula bilərsiniz. Bu fərqli şeylər etməyə imkan verəcək. Hələ __new__ də bütün bunları edə bilərsiniz __new__ , bəzi insanlar üçün __init__ istifadə etmək daha rahatdır.
  • Bu metaclasses deyilir, lənətləmək! Bu bir şey deməkdir!

Niyə metaclasses istifadə edirsiniz?

İndi böyük sual. Niyə bəzi gizli səhvlərə ehtiyacınız var?

Bəli, adətən bunu etmirsiniz:

Metaclasses daha dərin sehrdir, istifadəçilərin 99% -i narahat olmayacaq. Onlara ehtiyacınız olub-olmadığını merak edərsənsə, ona ehtiyac yoxdur (həqiqətən onlara ehtiyacı olan insanlar və bunların nə üçün bir izahat lazım olmadıqları barədə dəqiq bilirlər).

Python Guru Tim Peters

Metaclass üçün əsas istifadə forması bir API yaratmaqdır. Bunun tipik bir nümunəsi Django ORM'dir.

Bu, bu kimi bir şeyi müəyyən etməyə imkan verir:

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

Ancaq bunu edərsən:

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

IntegerField obyektini qaytarmayacaq. Bir int geri qayıdacaq və hətta verilənlər bazasından birbaşa götürə bilər.

models.Model çünki models.Model müəyyən edir və sadə operatorları istifadə edərək, müəyyən etdiyiniz şəxsi bir verilənlər bazası sahəsinə kompleks bir şəkildə bağlayan sehrdən istifadə edir.

Django sadə bir API təmin edən və metaclasses istifadə edərək, səhvlərin arxasında real iş üçün bu API kodunu yaratmaqla mürəkkəb sadə bir şey edir.

Son söz

Birincisi, dərslər nümunələri yarada bilən obyektlər olduğunu bilirsiniz.

Bəli, əslində, dərslər özləridir. Metaclasses.

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

Python'da hər şey bir obyektdir və onlar bütün siniflər və ya metaclasses nümunələridir.

type istisna olmaqla.

type əslində öz metaclassıdır. Bu, təmiz Python'ta çoğaltabileceğiniz bir şey deyil və bu, tətbiq səviyyəsində aldadıcılıqla həyata keçirilir.

Во-вторых, метаклассы сложны. Возможно, вы не захотите использовать их для очень простых изменений класса. Вы можете изменить классы, используя два разных метода:

В 99% случаев вам нужно изменить класс, лучше использовать их.

Но в 98% случаев вам вообще не нужно менять класс.

6009
ответ дан e-satis 05 июля '11 в 14:29 2011-07-05 14:29

Обратите внимание, этот ответ для Python 2.x, как он был написан в 2008 году, метаклассы немного отличаются в 3.x, см. комментарии.

Метаклассы - это секретный соус, который делает работу "класса". Метакласс по умолчанию для нового объекта стиля называется "type".

330
ответ дан Jerub 19 сент. '08 в 9:26 2008-09-19 09:26

Одно использование для метаклассов - это добавление новых свойств и методов в экземпляр автоматически.

Например, если вы посмотрите на модели Django , их определение выглядит немного запутанным. Похоже, что вы определяете свойства класса:

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

Однако во время выполнения объекты Person заполняются всякими полезными методами. См. источник для некоторой удивительной метаклассификации.

134
ответ дан Antti Rasinen 19 сент. '08 в 9:45 2008-09-19 09:45

Другие объяснили, как работают метаклассы и как они вписываются в систему типа Python. Вот пример того, к чему их можно использовать. В рамках тестирования, который я написал, я хотел отслеживать порядок, в котором были определены классы, чтобы впоследствии описать их в этом порядке. Мне было проще сделать это, используя метакласс.

 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 , который записывает порядок, в котором были определены классы.

129
ответ дан kindall 21 июня '11 в 19:30 2011-06-21 19:30

Я думаю, что введение 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 )

Вкратце: класс - это план создания экземпляра, метакласс - это план создания класса. Легко видеть, что в классах Python должны быть объекты первого класса, чтобы включить это поведение.

Я никогда не писал сам, но я думаю, что одно из самых хороших применений метаклассов можно увидеть в структуре Django . Классы моделей используют метаклассовый подход, чтобы включить декларативный стиль написания новых моделей или классов форм. Пока метакласс создает класс, все члены получают возможность настраивать сам класс.

Осталось сказать следующее: если вы не знаете, что такое метаклассы, вероятность того, что они вам не понадобятся, составляет 99%.

96
ответ дан Matthias Kestenholz 19 сент. '08 в 9:32 2008-09-19 09:32

Что такое метаклассы? Для чего вы их используете?

TL;DR: метаклас создает экземпляр и определяет поведение для класса точно так же, как экземпляр класса и определяет поведение для экземпляра.

псевдокод:

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

Когда вы пишете определение класса, и Python выполняет его, он использует метакласс для создания экземпляра объекта класса (который, в свою очередь, будет использоваться для создания экземпляров этого класса).

Так же, как мы можем использовать определения классов для изменения поведения пользовательских объектов, мы можем использовать определение класса метакласса, чтобы изменить способ поведения объекта класса.

Для чего их можно использовать? Из docs :

Потенциальное использование метаклассов безгранично. Некоторые идеи, которые были изучены, включают в себя ведение журнала, проверку интерфейса, автоматическое делегирование, автоматическое создание свойств, прокси, фреймворки и автоматическую блокировку/синхронизацию ресурсов.

Тем не менее, обычно пользователям рекомендуется избегать использования метаклассов, если это абсолютно необходимо.

Вы используете метакласс каждый раз, когда вы создаете класс:

Когда вы пишете определение класса, например, как это,

 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__ - если мы define __slots__ , мы сохраним немного места в экземплярах, так как мы можем запретить __dict__ и __weakref__ , исключив их. пример:

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

... но я отвлекся.)

Мы можем расширить type так же, как и любое другое определение класса:

Здесь по умолчанию __repr__ классов:

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

Одна из самых ценных вещей, которые мы можем сделать по умолчанию при написании объекта Python, - предоставить ему хороший __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__) 

Итак, теперь, когда мы создаем объект с этим метаклассом, __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__ пространство имен

Если, например, мы хотим знать, в каком порядке создаются методы класса, мы могли бы предоставить упорядоченный dict как пространство имен класса. Мы будем делать это с помощью __prepare__ , который возвращает пространство имен dict для класса, если оно реализовано в Python 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') 

Qeyd Этот пример был адаптирован из документации - нового перечисления в стандартной библиотеке .

Итак, что мы сделали, это создать метакласс, создав класс. Мы также можем рассматривать метакласс как и любой другой класс. Он имеет порядок разрешения метода:

 >>> 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>}) 
 >>> 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>}) 
83
ответ дан Aaron Hall 11 авг. '15 в 2:28 2015-08-11 02:28

Обновление Python 3

Есть (в этой точке) два ключевых метода в метаклассе:

  • __prepare__ и
  • __new__

__prepare__ позволяет вам создавать настраиваемое сопоставление (например, OrderedDict ), которое будет использоваться в качестве пространства имен во время создания класса. Вы должны вернуть экземпляр любого пространства имен, которое вы выберете. Если вы не реализуете __prepare__ , используется обычный dict .

__new__ отвечает за фактическое создание/модификацию окончательного класса.

Голые кости, do-nothing-extra metaclass хотели бы:

 class Meta(type): def __prepare__(metaclass, cls, bases): return dict() def __new__(metacls, cls, bases, clsdict): return super().__new__(metacls, cls, bases, clsdict) 

Sadə bir nümunə:

Предположим, что для ваших атрибутов нужен какой-то простой код проверки. Как всегда, он должен быть int или str . Без метакласса ваш класс будет выглядеть примерно так:

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

Как вы можете видеть, вам нужно дважды повторить имя атрибута. Это делает опечатки возможными наряду с раздражающими ошибками.

Простой метакласс может решить эту проблему:

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

Это будет выглядеть метакласс (не используя __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') 

Примечание . Этот пример достаточно прост, он также может быть выполнен с помощью декоратора класса, но, по-видимому, фактический метакласс будет делать гораздо больше.

Класс "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 
61
ответ дан Ethan Furman 01 марта '16 в 22:48 2016-03-01 22:48

Роль метода метакласса __call__() при создании экземпляра класса

Если вы занимались программированием на Python более нескольких месяцев, вы в конечном итоге натолкнетесь на код, который выглядит следующим образом:

 # define a class class SomeClass(object): # ... # some definition here ... # ... # create an instance of it instance = SomeClass() # then call the object as if it a function result = instance('foo', 'bar') 

Последнее возможно, когда вы реализуете магический метод __call__() в классе.

 class SomeClass(object): # ... # some definition here ... # ... def __call__(self, foo, bar): return bar + foo 

Метод __call__() вызывается, когда экземпляр класса используется как вызываемый. Но, как мы видели из предыдущих @, сам класс является экземпляром метакласса, поэтому, когда мы используем класс в качестве вызываемого (т.е. когда мы создаем его экземпляр), мы фактически вызываем его метакласс __call__() , В этот момент большинство программистов на Python немного запутались, потому что им сказали, что при создании экземпляра, подобного этому instance = SomeClass() вы вызываете его метод __init__() . Некоторые, кто копнул немного глубже, знают, что до __init__() существует __new__() . Что ж, сегодня раскрывается еще один слой правды, перед __new__() появился метакласс ' __call__() .

Давайте изучим цепочку вызовов метода с точки зрения создания экземпляра класса.

Это метакласс, который регистрирует ровно тот момент, когда экземпляр создан, и момент, когда он собирается его вернуть.

 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 

Это класс, который использует этот метакласс

 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 является родительским метаклассом по умолчанию) и, учитывая последовательность упорядочения вышеприведенных выходных данных, мы теперь имеем представление о том, что будет являться псевдо-реализацией 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 

Мы можем видеть, что метод метакласса __call__() - это тот, который вызывается первым. Затем он делегирует создание экземпляра методу класса __new__() и инициализацию экземпляру __init__() . Это также тот, который в конечном итоге возвращает экземпляр.

Из вышесказанного вытекает, что метаклассу __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 
49
ответ дан Michael Ekoka 13 окт. '16 в 12:21 2016-10-13 12:21

Метакласс - это класс, который сообщает, как (какой-то) должен быть создан другой класс.

Это случай, когда я видел метакласс как решение моей проблемы: У меня была действительно сложная проблема, которая, вероятно, могла быть решена по-другому, но я решил решить ее с помощью метакласса. Из-за сложности это один из немногих модулей, которые я написал, когда комментарии в модуле превосходят количество написанного кода. Вот оно...

 #!/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 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 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 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() 
45
ответ дан Craig 25 февр. '14 в 0:20 2014-02-25 00:20

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 .

31
ответ дан Mushahid Khan 09 авг. '16 в 21:49 2016-08-09 21:49

Версия tl; dr

Функция type(obj) предоставляет вам тип объекта.

type() класса - это его метакласс.

Чтобы использовать метакласс:

 class Foo(object): __metaclass__ = MyMetaClass 
27
ответ дан noɥʇʎԀʎzɐɹƆ 27 дек. '17 в 5:21 2016-12-27 05:21

Классы Python сами являются объектами, как, например, их метакласса.

Метаклас по умолчанию, который применяется, когда вы определяете классы как:

 class foo: ... 

meta class используются для применения некоторого правила ко всему набору классов. Например, предположим, что вы создаете ORM для доступа к базе данных и хотите, чтобы записи из каждой таблицы были отображены в эту таблицу (на основе полей, бизнес-правил и т.д.), Возможное использование метакласса это, например, логика пула соединений, которая является общим для всех классов записей из всех таблиц. Другое использование - это логика поддержки внешних ключей, которая включает в себя несколько классов записей.

когда вы определяете метакласс, тип подкласса и можете переопределить следующие магические методы для вставки вашей логики.

 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 

так или иначе, эти два являются наиболее часто используемыми крючками. метаклассирование является мощным, а выше - нигде и исчерпывающий список применений для метаклассификации.

17
ответ дан Xingzhou Liu 13 июля '17 в 10:58 2017-07-13 10:58

Функция type() может возвращать тип объекта или создавать новый тип,

например, мы можем создать класс Hi с помощью функции type() и не использовать этот способ с классом Hi (object):

 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 

В дополнение к типу() для динамического создания классов вы можете контролировать поведение поведения класса и использовать метакласс.

В соответствии с объектной моделью Python класс является объектом, поэтому класс должен быть экземпляром другого определенного класса. По умолчанию класс Python является экземпляром класса type. То есть, тип - это метакласс большинства встроенных классов и метакласс пользовательских классов.

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

Магия вступает в силу, когда мы передаем аргументы ключевого слова в метаклассе, это означает, что интерпретатор Python создает CustomList через ListMetaclass. new (), на данный момент мы можем изменить определение класса, например, и добавить новый метод, а затем вернуть исправленное определение.

11
ответ дан binbjz 12 янв. '18 в 12:16 2018-01-12 12:16

В дополнение к опубликованным ответам я могу сказать, что metaclass определяет поведение класса. Таким образом, вы можете явно установить свой метакласс. Всякий раз, когда Python получает class ключевого слова, он начинает поиск metaclass . Если он не найден - тип метакласса по умолчанию используется для создания объекта класса. Используя атрибут __metaclass__ , вы можете установить metaclass вашего класса:

 class MyClass: __metaclass__ = type # write here other method # write here one more method print(MyClass.__metaclass__) 

Он выдаст вывод примерно так:

 class 'type' 

И, конечно же, вы можете создать свой собственный metaclass чтобы определить поведение любого класса, созданного с использованием вашего класса.

Для этого ваш класс metaclass умолчанию должен быть унаследован, так как это основной metaclass :

 class MyMetaClass(type): __metaclass__ = type # you can write here any behaviour you want class MyTestClass: __metaclass__ = MyMetaClass Obj = MyTestClass() print(Obj.__metaclass__) print(MyMetaClass.__metaclass__) 

Çıxış olacaq:

 class '__main__.MyMetaClass' class 'type' 
4
ответ дан ARGeo 15 сент. '18 в 15:41 2018-09-15 15:41

метаклассов. Я могу это проиллюстрировать. Метакласс - это класс, экземпляры которого являются классами. Подобно "обычным" классам, определяется поведение экземпляров класса, метакласс определяет поведение классов и их экземпляров.

Метаклассы не поддерживаются каждым объектно-ориентированным языком программирования. Те языки программирования, которые поддерживают метаклассы, значительно различаются в зависимости от их реализации. Python поддерживает их.

Некоторые программисты видят метаклассы в Python как "решения, ожидающие или ищущие проблему".

Существует множество вариантов использования метаклассов. Просто чтобы назвать несколько:

 logging and profiling interface checking registering classes at creation time automatically adding new methods automatic property creation proxies automatic resource locking/synchronization. 

Метаклассы определяются как любой другой класс Python, но они являются классами, которые наследуются от типа. Другое отличие состоит в том, что метаклас вызывается автоматически, когда инструкция класса, использующая метакласс, заканчивается. Другими словами: Если ключевое слово "метакласса" не будет передано после базовых классов (также не может быть базовых классов) заголовка класса, будет вызван тип() (т.е. вызов ). Если ключевое слово metaclass используется, с другой стороны, назначенный ему класс будет вызываться вместо типа.

Позвольте мне показать вам, создав очень простой метакласс. Это ничего не значит, за исключением того, что он будет печатать содержимое своих аргументов в новом методе и возвращает результаты этого типа. Новый вызов:

класс LittleMeta (тип): def new (cls, clsname, суперклассы, атрибут): print ( "clsname:", clsname) print ( "суперклассы:", суперклассы) print ( "attributedict:", attributedict) new (cls, clsname, суперклассы, атрибут)

Я буду использовать метакласс "LittleMeta" в следующем примере:

класс S: проходить класс A (S, metaclass= LittleMeta):

clsname: A суперклассы: (,) attributedict: {' module ': ' main ', ' qualname ': 'A'} проходить a = A()

3
ответ дан Sandeep Anand 23 нояб. '17 в 9:46 2017-11-23 09:46

Метакласс определяется как "класс класса". Любой класс, экземпляры которого сами являются классами, является метаклассом. Итак, в соответствии с тем, что мы видели выше, это делает тип метаклассом - на самом деле, наиболее часто используемым метаклассом в Python, поскольку он является метаклассом по умолчанию для всех классов.

Так как метакласс является классом класса, он используется для построения классов (так же, как класс используется для построения объектов). Но подождите секунду, не создаем классы со стандартным определением класса? Определенно, но то, что Python делает под капотом, следующее:

  • Когда он видит определение класса, Python выполняет его для сбора атрибуты (включая методы) в словарь
  • Когда определение класса закончено, Python определяет метакласс из класс. Позвольте назвать Meta​​li >
  • В конце концов, Python выполняет Meta (имя, базы, dct), где:

    -Meta - это метакласс, поэтому этот вызов создает его.

    -name - это имя вновь созданного класса

    -bases - это набор базовых классов класса

    -dct сопоставляет имена атрибутов объектам, перечисляя все атрибуты класса

Как определить метакласс класса? Проще говоря, если какой-либо класс или одна из его баз имеет атрибут метакласс , он воспринимается как метакласс. В противном случае введите метакласс.

Итак, что происходит, когда мы определяем:

 class MyKlass(object): foo = 2 

Это: MyKlass не имеет атрибута метакласса , поэтому вместо него используется тип, а создание класса выполняется как:

 MyKlass = type(name, bases, dct) 

Это соответствует тому, что мы видели в начале статьи. Если, с другой стороны, у MyKlass есть определенный метакласс:

 class MyKlass(object): __metaclass__ = MyMeta foo = 2 

Затем создание класса выполняется как:

 MyKlass = MyMeta(name, bases, dct) 

Итак, MyMeta должен быть соответствующим образом реализован для поддержки такой формы вызова и возврата нового класса. Это фактически похоже на запись нормального класса с предопределенной сигнатурой конструктора.

o управлять созданием и инициализацией класса в метаклассе, вы можете реализовать метод metaclass __new__ и/или конструктор __init__ . Большинство реальных метаклассов, вероятно, переопределит только один из них. __new__ должен быть реализован, если вы хотите управлять созданием нового объекта (класса в нашем случае), а __init__ должен быть реализован, когда вы хотите контролировать инициализацию нового объекта после его создания.

Итак, когда вызов MyMeta выполняется выше, что происходит под капотом, это:

 MyKlass = MyMeta.__new__(MyMeta, name, bases, dct) MyMeta.__init__(MyKlass, name, bases, dct) 

Вот более конкретный пример, который должен продемонстрировать, что происходит. Пусть записано это определение для метакласса

 class MyMeta(type): def __new__(meta, name, bases, dct): print '-----------------------------------' print "Allocating memory for class", name print meta print bases print dct return super(MyMeta, meta).__new__(meta, name, bases, dct) def __init__(cls, name, bases, dct): print '-----------------------------------' print "Initializing class", name print cls print bases print dct super(MyMeta, cls).__init__(name, bases, dct) 

Выполняется со следующим определением класса:

 class MyKlass(object): __metaclass__ = MyMeta def foo(self, param): pass barattr = 2 
1
ответ дан Umer Farooq 11 февр. '18 в 15:10 2018-02-11 15:10

Метакласс - это класс, экземплярами которого являются классы. Подобно тому, как "обычный" класс определяет поведение экземпляров класса, метакласс определяет поведение классов и их экземпляров.

Метаклассы поддерживаются не всеми объектно-ориентированными языками программирования. Те языки программирования, которые поддерживают метаклассы, значительно различаются по способу их реализации. Python поддерживает их.

Существует множество вариантов использования метаклассов. Просто назвать несколько:

  • регистрация и профилирование
  • проверка интерфейса
  • регистрация классов во время создания
  • автоматическое добавление новых методов
  • автоматическое создание свойств
  • автоматическая блокировка/синхронизация ресурсов.

В принципе, метаклассы определяются как любой другой класс Python, но они являются классами, которые наследуются от "типа". Другое отличие состоит в том, что метакласс вызывается автоматически, когда оператор класса, использующий метакласс, заканчивается. Другими словами: если ключевое слово "metaclass" не передано после базовых классов (может быть и без базовых классов) заголовка класса, будет вызван type() (то есть вызов типа). Если ключевое слово metaclass используется с другой стороны, назначенный ему класс будет вызываться вместо типа.

Теперь мы создаем очень простой метакласс. Это бесполезно, за исключением того, что он напечатает содержимое своих аргументов в новом методе и вернет результаты типа. новый звонок:

 class LittleMeta(type): def __new__(cls, clsname, superclasses, attributedict): print("clsname: ", clsname) print("superclasses: ", superclasses) print("attributedict: ", attributedict) return type.__new__(cls, clsname, superclasses, attributedict) 

Мы будем использовать метакласс LittleMeta в следующем примере:

 class S: pass class A(S, metaclass=LittleMeta): pass a = A() clsname: A superclasses: (<class '__main__.S'>,) attributedict: {'__module__': '__main__', '__qualname__': 'A'} 

Мы можем видеть LittleMeta. новый был назван, а не тип. новый

0
ответ дан Codemaker 20 дек. '18 в 11:47 2018-12-20 11:47

Метакласс - это, по сути, абстрактный базовый класс - концепция, преподаваемая в большинстве курсов по компьютерному программированию. Класс связан с метаклассом.

-6
ответ дан JKL 01 июня '17 в 11:01 2017-06-01 11:01

Другие вопросы по меткам или Задайте вопрос