Yanıtlar:
Metasınıf bir sınıfın sınıfıdır. Sınıf, sınıfın bir örneğinin (yani bir nesnenin) nasıl davrandığını, bir metasınıf ise bir sınıfın nasıl davrandığını tanımlar. Sınıf bir metasınıfın örneğidir.
Python'da metasınıflar için ( Jerub'un gösterdiği gibi ) keyfi callables kullanabiliyorsanız , daha iyi yaklaşım onu gerçek bir sınıf haline getirmektir. type
Python'daki olağan metasınıftır. type
kendisi bir sınıftır ve kendi tipidir. type
Tamamen Python'da olduğu gibi bir şey yaratamazsınız , ancak Python biraz hile yapar. Python'da kendi metasınıfınızı oluşturmak için gerçekten sadece alt sınıf yapmak istiyorsunuz type
.
Metasınıf en yaygın olarak sınıf fabrikası olarak kullanılır. Sınıfı çağırarak bir nesne oluşturduğunuzda, Python metasınıfı çağırarak yeni bir sınıf oluşturur ('class' ifadesini çalıştırdığında). Normal __init__
ve __new__
yöntemlerle birleştiğinde, metasınıflar bir sınıf oluştururken yeni sınıfı bir kayıt defterine kaydettirmek veya sınıfı tamamen başka bir şeyle değiştirmek gibi 'ekstra şeyler' yapmanıza izin verir.
İfade class
yürütüldüğünde, Python önce class
ifadenin gövdesini normal bir kod bloğu olarak yürütür . Sonuçta elde edilen ad alanı (bir diksiyon), alınacak sınıfın niteliklerini içerir. Metasınıf, olması gereken sınıfın (metasınıflar devralınır) __metaclass__
temel sınıflarına, olması gereken sınıfın (varsa) özelliğine veya __metaclass__
global değişkene bakarak belirlenir . Daha sonra metasınıfı, sınıfın adı, tabanı ve öznitelikleri ile somutlaştırmak için çağrılır.
Bununla birlikte, metasınıflar aslında sadece bir fabrika değil, bir sınıfın türünü tanımlar , böylece onlarla daha fazlasını yapabilirsiniz. Örneğin, metasınıfta normal yöntemleri tanımlayabilirsiniz. Bu metasınıf yöntemleri, sınıf olmadan örnek üzerinde çağrılabildikleri için sınıf yöntemleri gibidir, ancak sınıf örneğinde çağrılamayacakları gibi sınıf yöntemleri gibi de değildirler. metasınıfta type.__subclasses__()
bir yöntem örneğidir type
. Ayrıca, gibi normal 'Sihirli' yöntemleri tanımlayabilir __add__
, __iter__
ve __getattr__
uygulamak veya değiştirmek nasıl sınıf davranacağını için.
İşte bitlerin ve parçaların toplu bir örneği:
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__
__metaclass__
Python 3. ayında Python 3 kullanımda desteklenmez class MyObject(metaclass=MyType)
, bkz python.org/dev/peps/pep-3115 ve aşağıda cevabını.
Metasınıfları anlamadan önce Python'da derslerde ustalaşmanız gerekir. Ve Python, Smalltalk dilinden ödünç alınan sınıfların ne olduğu konusunda çok tuhaf bir fikre sahiptir.
Çoğu dilde, sınıflar yalnızca bir nesnenin nasıl üretileceğini açıklayan kod parçalarıdır. Bu Python'da da doğrudur:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
Ama sınıflar Python'dakinden daha fazla. Sınıflar da nesnedir.
Evet, nesneler.
Anahtar kelimeyi kullandığınız anda class
Python anahtar kelimeyi yürütür ve bir NESNE oluşturur. Talimat
>>> class ObjectCreator(object):
... pass
...
bellekte "ObjectCreator" adlı bir nesne oluşturur.
Bu nesnenin (sınıf) kendisi, nesneler (örnekler) oluşturabilmektedir ve bu yüzden bir sınıftır .
Ama yine de, bu bir nesne ve bu nedenle:
Örneğin:
>>> 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>
Sınıflar nesne olduğundan, bunları herhangi bir nesne gibi anında oluşturabilirsiniz.
İlk olarak, aşağıdakileri kullanarak bir işlevde sınıf oluşturabilirsiniz class
:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
Ama o kadar dinamik değil, çünkü tüm sınıfı hala kendiniz yazmak zorundasınız.
Sınıflar nesne olduğundan, bir şey tarafından üretilmeleri gerekir.
class
Anahtar kelimeyi kullandığınızda , Python bu nesneyi otomatik olarak oluşturur. Ancak Python'daki çoğu şeyde olduğu gibi, bunu manuel olarak yapmanın bir yolunu sunar.
İşlevi hatırlıyor type
musunuz? Bir nesnenin ne tür olduğunu bilmenizi sağlayan eski işlev:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
Eh, type
bu da anında sınıf oluşturabileceğini bambaşka bir yeteneği vardır. type
bir sınıfın tanımını parametre olarak alabilir ve bir sınıfı döndürebilir.
(Biliyorum, aynı işlevin, ilettiğiniz parametrelere göre tamamen farklı iki kullanıma sahip olması saçma. Python'daki geriye dönük uyumluluk nedeniyle bir sorun)
type
şu şekilde çalışır:
type(name, bases, attrs)
Nerede:
name
: sınıfın adıbases
: ana sınıfın tuple'i (kalıtım için boş olabilir)attrs
: özellik adları ve değerleri içeren sözlükÖrneğin:
>>> class MyShinyClass(object):
... pass
manuel olarak şu şekilde oluşturulabilir:
>>> 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>
Sınıfın adı ve sınıf referansını tutmak için değişken olarak "MyShinyClass" kullandığımızı fark edeceksiniz. Farklı olabilirler, ancak işleri karmaşıklaştırmak için bir neden yoktur.
type
sınıfın özelliklerini tanımlamak için bir sözlüğü kabul eder. Yani:
>>> class Foo(object):
... bar = True
Tercüme edilebilir:
>>> Foo = type('Foo', (), {'bar':True})
Ve normal bir sınıf olarak kullanılır:
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True
Ve elbette, ondan miras alabilirsiniz, yani:
>>> class FooChild(Foo):
... pass
olabilir:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True
Sonunda sınıfınıza yöntemler eklemek isteyeceksiniz. Sadece uygun imzalı bir işlev tanımlayın ve bir öznitelik olarak atayın.
>>> 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
Sınıfı dinamik olarak oluşturduktan sonra, normalde oluşturulan bir sınıf nesnesine yöntem eklemek gibi, daha da fazla yöntem ekleyebilirsiniz.
>>> def echo_bar_more(self):
... print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True
Nereye gittiğimizi görüyorsunuz: Python'da sınıflar nesnelerdir ve anında dinamik olarak bir sınıf oluşturabilirsiniz.
Anahtar kelimeyi kullandığınızda Python class
bunu yapar ve bunu bir metasınıf kullanarak yapar.
Metasınıflar sınıflar yaratan 'şeyler'dir.
Nesneleri oluşturmak için sınıfları tanımlarsınız, değil mi?
Ancak Python sınıflarının nesne olduğunu öğrendik.
Bu nesneleri yaratan metasınıflar. Bunlar sınıfların sınıflarıdır, onları şu şekilde hayal edebilirsiniz:
MyClass = MetaClass()
my_object = MyClass()
Bunun type
böyle bir şey yapmanıza izin verdiğini gördünüz :
MyClass = type('MyClass', (), {})
Çünkü fonksiyon type
aslında bir metasınıf. type
Python'un perde arkasındaki tüm sınıfları oluşturmak için kullandığı metasınıf.
Şimdi merak ediyorum neden küçük harfle yazılıyor, değil Type
mi?
Sanırım bu bir tutarlılık meselesi, str
dizeler nesneleri int
yaratan sınıf ve tamsayı nesneler yaratan sınıf. type
sadece sınıf nesneleri oluşturan sınıftır.
Bu __class__
özelliği kontrol ederek görürsünüz .
Her şey ve her şey demek istediğim Python'da bir nesne. Buna ints, stringler, fonksiyonlar ve sınıflar dahildir. Hepsi nesneler. Ve hepsi bir sınıftan yaratıldı:
>>> 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'>
Şimdi, __class__
herhangi biri __class__
nedir?
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>
Yani, bir metasınıf sadece sınıf nesneleri yaratan şeydir.
İsterseniz 'sınıf fabrikası' diyebilirsiniz.
type
Python'un kullandığı yerleşik metasınıftır, ancak elbette kendi metasınıfınızı oluşturabilirsiniz.
__metaclass__
nitelikPython 2'de, __metaclass__
bir sınıf yazarken bir öznitelik ekleyebilirsiniz (Python 3 sözdizimi için sonraki bölüme bakın):
class Foo(object):
__metaclass__ = something...
[...]
Bunu yaparsanız, Python sınıfı oluşturmak için metasınıfı kullanır Foo
.
Dikkatli, zor.
Önce yazıyorsunuz class Foo(object)
, ancak sınıf nesnesi Foo
henüz bellekte oluşturulmamış.
Python __metaclass__
sınıf tanımında arayacaktır . Bulursa, nesne sınıfını oluşturmak için kullanır Foo
. Yoksa, type
sınıfı oluşturmak için kullanılır
.
Bunu birkaç kez okuyun.
Yaptığınızda:
class Foo(Bar):
pass
Python aşağıdakileri yapar:
İçinde bir __metaclass__
özellik var Foo
mı?
Evet ise, bellekte bir sınıf nesnesi oluşturun (bir sınıf nesnesi dedim, burada benimle kal), içinde Foo
ne olduğunu kullanarak adla __metaclass__
.
Python bulamazsa __metaclass__
, __metaclass__
MODÜL seviyesinde bir arar ve aynısını yapmaya çalışır (ancak yalnızca hiçbir şey miras almayan sınıflar, temelde eski stil sınıfları için).
Sonra herhangi bulamazsa __metaclass__
hiç bu kullanacağız Bar
's (birinci ebeveyn) (varsayılan olabilir kendi metaclass type
sınıf nesnesi oluşturmak için).
Burada __metaclass__
özelliğin miras alınmayacağına, üst öğenin ( Bar.__class__
) metasınıfına dikkat edin . Eğer Bar
kullanılmış bir __metaclass__
yaratılmış olduğunu niteliği Bar
ile type()
(ve type.__new__()
), alt sınıfları davranış olduğunu devralmaz.
Şimdi asıl soru, ne koyabilirsiniz __metaclass__
?
Cevap: sınıf yaratabilecek bir şey.
Ve ne sınıf oluşturabilir? type
veya alt sınıfları kullanan veya kullanan herhangi bir şey.
Meta sınıfı ayarlamak için sözdizimi Python 3'te değiştirildi:
class Foo(object, metaclass=something):
...
yani __metaclass__
öznitelik artık temel sınıflar listesindeki bir anahtar kelime argümanı lehine kullanılmaz.
Ancak metasınıfların davranışı büyük ölçüde aynı kalır .
Python 3'teki metasınıflara eklenen bir şey, öznitelikleri anahtar kelime bağımsız değişkenleri olarak bir metasınıfa da aktarabilmenizdir:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
Python'un bunu nasıl ele aldığını öğrenmek için aşağıdaki bölümü okuyun.
Metasınıfın temel amacı sınıfı oluşturulduğunda otomatik olarak değiştirmektir.
Bunu genellikle geçerli bağlamla eşleşen sınıflar oluşturmak istediğiniz API'lar için yaparsınız.
Modülünüzdeki tüm sınıfların niteliklerinin büyük harfle yazılması gerektiğine karar verdiğiniz aptal bir örnek düşünün. Bunu yapmanın birkaç yolu vardır, ancak bir yol __metaclass__
modül düzeyinde ayarlamaktır .
Bu şekilde, bu modülün tüm sınıfları bu metasınıf kullanılarak oluşturulacak ve sadece metaslass'a tüm nitelikleri büyük harfe çevirmesini söylemeliyiz.
Neyse ki, __metaclass__
aslında herhangi bir çağrılabilir olabilir, resmi bir sınıf olması gerekmez (biliyorum, 'sınıf' olan bir şey bir sınıf olmak zorunda değildir, şekil gidin ... ama yararlıdır).
Yani basit bir örnekle, bir fonksiyon kullanarak başlayacağız.
# 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_attrs):
"""
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_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attrs)
__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'
Hadi kontrol edelim:
>>> hasattr(Foo, 'bar')
False
>>> hasattr(Foo, 'BAR')
True
>>> Foo.BAR
'bip'
Şimdi aynısını yapalım, ancak bir metasınıf için gerçek bir sınıf kullanalım:
# 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_attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in future_class_attrs.items()
}
return type(future_class_name, future_class_parents, uppercase_attrs)
Yukarıdakileri yeniden yazalım, ancak şimdi ne anlama geldiğini bildiğimiz için daha kısa ve daha gerçekçi değişken isimleriyle:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type(clsname, bases, uppercase_attrs)
Ekstra argümanı fark etmiş olabilirsiniz cls
. Bu konuda özel bir şey yok: __new__
her zaman tanımlandığı sınıfı ilk parametre olarak alır. Tıpkı self
ilk parametre olarak örneği alan sıradan yöntemlere veya sınıf yöntemleri için tanımlayıcı sınıfa sahip olduğunuz gibi.
Ancak bu uygun bir OOP değil. Biz aradığınız type
doğrudan ve biz geçersiz kılma veya ebeveynin demiyorsun __new__
. Bunun yerine yapalım:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return type.__new__(cls, clsname, bases, uppercase_attrs)
Bunu kullanarak daha da temiz hale getirebiliriz super
, bu da kalıtımı kolaylaştırır (çünkü evet, metasınıflardan miras, metasınıflardan miras, tipten miras alabilirsiniz):
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, attrs):
uppercase_attrs = {
attr if attr.startswith("__") else attr.upper(): v
for attr, v in attrs.items()
}
return super(UpperAttrMetaclass, cls).__new__(
cls, clsname, bases, uppercase_attrs)
Oh ve python 3'te bu çağrıyı aşağıdaki gibi anahtar kelime bağımsız değişkenleriyle yaparsanız:
class Foo(object, metaclass=MyMetaclass, kwarg1=value1):
...
Bunu kullanmak için metasınıfta buna çevirir:
class MyMetaclass(type):
def __new__(cls, clsname, bases, dct, kwargs1=default):
...
Bu kadar. Metaclasses hakkında daha fazla bir şey yok.
Metaclasses kullanan kodun karmaşıklığının ardındaki neden, metasınıflar nedeniyle değildir, çünkü genellikle introspeksiyona dayanan, mirası manipüle eden __dict__
, vb.
Gerçekten de, metasınıflar özellikle kara büyü ve dolayısıyla karmaşık şeyler yapmak için kullanışlıdır. Ancak kendi başlarına basittirler:
Yana __metaclass__
herhangi çağrılabilir kabul edebilir açıkça daha karmaşıktır çünkü, neden bir sınıf kullanmak istiyorsunuz?
Bunu yapmanın birkaç nedeni vardır:
UpperAttrMetaclass(type)
, neyi takip edeceğinizi biliyorsunuz__new__
, __init__
ve __call__
. Hangi farklı şeyler yapmanıza izin verecektir. Genellikle her şeyi yapabilseniz bile __new__
, bazı insanlar kullanmak daha rahattır __init__
.Şimdi büyük soru. Neden bazı hataya yatkın özellikler kullanıyorsunuz?
Genellikle şunları yapmazsınız:
Metaclasses, kullanıcıların% 99'unun asla endişelenmemesi gereken daha derin bir sihirdir. Onlara ihtiyacınız olup olmadığını merak ediyorsanız, (gerçekten ihtiyaç duyan insanlar onlara ihtiyaç duyduklarını kesin olarak bilirler ve neden hakkında bir açıklamaya ihtiyaç duymazlar).
Python Guru Tim Peters
Metasınıfın ana kullanım durumu bir API oluşturmaktır. Bunun tipik bir örneği Django ORM'dir. Bunun gibi bir şey tanımlamanızı sağlar:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
Ancak bunu yaparsanız:
person = Person(name='bob', age='35')
print(person.age)
Bir IntegerField
nesne döndürmez . Bir döndürür int
ve hatta doğrudan veritabanından alabilir.
Bu mümkündür çünkü models.Model
tanımlar __metaclass__
ve Person
basit ifadelerle tanımladığınız bir sihirbazı kullanarak karmaşık bir kancaya veritabanı alanına dönüşür.
Django, basit bir API'yi açığa çıkararak ve metasınıfları kullanarak, sahne arkasındaki gerçek işi yapmak için bu API'dan kod yeniden oluşturarak karmaşık bir şeyi basitleştirir.
İlk olarak, sınıfların örnek oluşturabilen nesneler olduğunu bilirsiniz.
Aslında, sınıfların kendileri örneklerdir. Metasınıfların.
>>> class Foo(object): pass
>>> id(Foo)
142630324
Her şey Python'daki bir nesnedir ve hepsi ya sınıfların örnekleri ya da metasınıfların örnekleridir.
Dışında type
.
type
aslında kendi metasınıfıdır. Bu, saf Python'da çoğaltabileceğiniz bir şey değildir ve uygulama düzeyinde biraz hile yaparak yapılır.
İkincisi, metasınıflar karmaşıktır. Bunları çok basit sınıf değişiklikleri için kullanmak istemeyebilirsiniz. İki farklı teknik kullanarak sınıfları değiştirebilirsiniz:
Sınıf değişimine ihtiyaç duyduğunuz zamanın% 99'u, bunları kullanmaktan daha iyidir.
Ama zamanın% 98'inde sınıf değişikliğine hiç ihtiyacınız yok.
models.Model
kullanmıyor . Harika gönderi! İşte Django kaynağı: github.com/django/django/blob/master/django/db/models/…__metaclass__
class Model(metaclass=ModelBase):
ModelBase
__metaclass__
özniteliğin devralınmayacağına, üst öğenin ( Bar.__class__
) metasınıfına dikkat edin . Eğer Bar
kullanılmış bir __metaclass__
yaratılmış olduğunu niteliği Bar
ile type()
(ve type.__new__()
. Eğer / birileri biraz daha derin bu pasaj açıklayabilir misiniz -), alt sınıfları davranış >> o miras değil mi?
Now you wonder why the heck is it written in lowercase, and not Type?
- C dilinde uygulandığı için - OrderedDict (python 2'de) normal CamelCase iken defaultdict küçük harfle aynı nedendir
Not, bu cevap 2008'de yazıldığı gibi Python 2.x içindir, metasınıflar 3.x'te biraz farklıdır.
Metaclasses, 'sınıf' iş yapan gizli sostur. Yeni bir stil nesnesinin varsayılan metasınıfına 'tip' denir.
class type(object)
| type(object) -> the object's type
| type(name, bases, dict) -> a new type
Metasınıflar 3 argüman alır. ' name ', ' base ' ve ' dict '
İşte sır burada başlıyor. Bu örnek sınıf tanımında adın, tabanların ve diktenin nereden geldiğine bakın.
class ThisIsTheName(Bases, Are, Here):
All_the_code_here
def doesIs(create, a):
dict
' Class: ' kelimesinin nasıl çağırdığını gösteren bir metasınıf tanımlayalım .
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'
Ve şimdi, aslında bir şey anlamına gelen bir örnek, bu otomatik olarak sınıfta ayarlanan "öznitelikler" listesindeki değişkenleri yapar ve Yok olarak ayarlanır.
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
Initialised
Metaclass'a sahip olarak kazanılan sihir davranışının init_attributes
bir alt sınıfına aktarılmadığını unutmayın Initialised
.
Sınıf oluşturulduğunda bir eylem gerçekleştiren bir metasınıf yapmak için 'tip' alt sınıfını nasıl yapabileceğinizi gösteren daha somut bir örnek. Bu oldukça zor:
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
Diğerleri, metasınıfların nasıl çalıştığını ve Python tipi sisteme nasıl uyduklarını açıkladı. İşte ne için kullanılabileceğine dair bir örnek. Yazdığım bir test çerçevesinde, sınıfların tanımlandığı sırayı takip etmek istedim, böylece daha sonra bunları bu sırayla başlatabilirdim. Bunu bir metasınıf kullanarak yapmanın en kolay yolunu buldum.
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
Alt sınıfı olan her şey , sınıfların tanımlandığı sırayı kaydeden MyType
bir sınıf niteliği alır _order
.
__init__(self)
diyor type(self)._order = MyBase.counter; MyBase.counter += 1
?
Metasınıflar için bir kullanım, bir örneğe otomatik olarak yeni özellikler ve yöntemler eklemektir.
Örneğin, Django modellerine bakarsanız, tanımları biraz kafa karıştırıcı görünüyor. Sadece sınıf özelliklerini tanımlıyormuşsunuz gibi görünüyor:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
Bununla birlikte, çalışma zamanında Person nesneleri her türlü yararlı yöntemle doldurulur. Bazı şaşırtıcı metasınıflar için kaynağa bakın .
Metasınıf programlamaya ONLamp girişinin iyi yazılmış olduğunu ve zaten birkaç yaşında olmasına rağmen konuya gerçekten iyi bir giriş yaptığını düşünüyorum.
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html ( https://web.archive.org/web/20080206005253/http://www.onlamp adresinde arşivlendi . com.tr / pub / a / python / 2003/04/17 / metaclasses.html )
Kısacası: Sınıf, bir örneğin oluşturulması için bir taslaktır, metasınıf ise bir sınıfın oluşturulması için bir taslaktır. Bu davranışı sağlamak için Python sınıflarında da birinci sınıf nesneler olması gerektiği kolayca görülebilir.
Kendimi hiç yazmadım, ancak bence metasınıfların en güzel kullanımlarından biri Django çerçevesinde görülebilir . Model sınıfları, yeni modeller veya form sınıfları yazma konusunda açıklayıcı bir stil sağlamak için bir metasınıf yaklaşımı kullanır. Metaclass sınıfı oluştururken, tüm üyeler sınıfın kendisini özelleştirme olanağına sahiptir.
Söylenecek olan şey şu: Eğer metasınıfların ne olduğunu bilmiyorsanız, onlara ihtiyacınız olmayacak olasılığı % 99'dur.
Metaclasses nedir? Onları ne için kullanıyorsun?
TLDR: Bir metasınıf, bir sınıfın davranışını, tıpkı bir sınıfın örneğini başlatır ve tanımlar gibi tanımlar.
pseudocode:
>>> Class(...)
instance
Yukarıdakiler tanıdık gelmelidir. Peki, nereden Class
geliyor? Bu bir metasınıf örneği (ayrıca sözde kod):
>>> Metaclass(...)
Class
Gerçek kodda, varsayılan metasınıfı geçebiliriz, type
bir sınıfı başlatmak için ihtiyacımız olan her şey ve bir sınıf alırız:
>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>
Sınıf örneğe, metasınıf ise sınıfa aittir.
Bir nesneyi başlattığımızda bir örnek alırız:
>>> object() # instantiation of class
<object object at 0x7f9069b4e0b0> # instance
Benzer şekilde, bir sınıfı varsayılan metasınıfla açıkça tanımladığımızda type
, bunu başlatırız:
>>> type('Object', (object,), {}) # instantiation of metaclass
<class '__main__.Object'> # instance
Başka bir deyişle, sınıf metasınıfın bir örneğidir:
>>> isinstance(object, type)
True
Üçüncü bir deyişle, metasınıf bir sınıftır.
>>> type(object) == type
True
>>> object.__class__
<class 'type'>
Bir sınıf tanımı yazdığınızda ve Python bunu çalıştırdığınızda, sınıf nesnesini örneklemek için bir metasınıf kullanır (bu da o sınıfın örneklerini örneklemek için kullanılır).
Özel nesne örneklerinin davranış biçimini değiştirmek için sınıf tanımlarını kullanabildiğimiz gibi, bir sınıf nesnesinin davranış biçimini değiştirmek için bir metasınıf sınıf tanımı kullanabiliriz.
Ne için kullanılabilirler? Gönderen docs :
Metasınıfların potansiyel kullanımları sınırsızdır. Keşfedilen bazı fikirler arasında günlük kaydı, arayüz denetimi, otomatik temsilci seçme, otomatik özellik oluşturma, proxy'ler, çerçeveler ve otomatik kaynak kilitleme / eşitleme bulunur.
Bununla birlikte, kullanıcıların kesinlikle gerekli olmadıkça metasınıf kullanmaktan kaçınmaları teşvik edilir.
Örneğin, bir sınıf tanımı yazdığınızda,
class Foo(object):
'demo'
Bir sınıf nesnesini başlatırsınız.
>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)
İşlevsel olarak type
uygun argümanlarla çağrı yapmak ve sonucu bu adın bir değişkenine atamakla aynıdır :
name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)
Not, bazı şeyler otomatik olarak __dict__
ad alanına eklenir :
>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__module__': '__main__', '__weakref__': <attribute '__weakref__'
of 'Foo' objects>, '__doc__': 'demo'})
Her iki durumda da yarattığımız nesnenin metasınıfıtype
.
(Sınıfın içeriğiyle ilgili bir yan not __dict__
: __module__
sınıflar nerede tanımlandıklarını bilmeleri gerektiğinden __dict__
ve __weakref__
orada tanımlanmadığımız için varlar __slots__
- eğer tanımlarsak__slots__
, örneklerde biraz yer tasarrufu yapacağız. izin vermeyebilir __dict__
ve __weakref__
hariç tutabiliriz.
>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})
... ama konuya giriyorum.)
type
Diğer sınıf tanımları gibi genişletebiliriz :__repr__
Sınıfların varsayılanı :
>>> Foo
<class '__main__.Foo'>
Bir Python nesnesi yazarken varsayılan olarak yapabileceğimiz en değerli şeylerden biri, ona bir iyi sağlamaktır __repr__
. Aradığımızda , eşitlik testi de gerektiren help(repr)
iyi bir test olduğunu öğreniyoruz . Tür sınıfımızın sınıf örneklerinin ve örneklerinin aşağıdaki basit uygulaması bize sınıfların varsayılan değerlerini artırabilecek bir gösterim sağlar :__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__)
Şimdi bu metasınıfla bir nesne oluşturduğumuzda __repr__
, komut satırındaki yankı varsayılandan çok daha az çirkin bir görüş sağlar:
>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
__repr__
Sınıf örneği için iyi tanımlanmış bir kodla, kodumuzu hata ayıklamak için daha güçlü bir yeteneğe sahibiz. Bununla birlikte, daha fazla kontrol eval(repr(Class))
yapılması olası değildir (fonksiyonların varsayılan değerlerinden değerlendirilmesi imkansız olacaktır __repr__
).
__prepare__
bir ad alanıÖrneğin, bir sınıfın yöntemlerinin hangi sırada oluşturulduğunu bilmek istiyorsak, sınıfın ad alanı olarak sıralı bir diksiyon sağlayabiliriz. Biz bu yapardın __prepare__
hangi Python 3'te uygulanırsa sınıf için ad dicti döndürür :
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
Ve kullanım:
class OrderedMethodsObject(object, metaclass=OrderedType):
def method1(self): pass
def method2(self): pass
def method3(self): pass
def method4(self): pass
Ve şimdi bu yöntemlerin (ve diğer sınıf özniteliklerinin) yaratılma sırasının bir kaydı var:
>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')
Not, bu örnek dokümantasyondan uyarlanmıştır - standart kütüphanedeki yeni numaralandırma bunu yapar.
Yani yaptığımız şey bir sınıf oluşturarak bir metasınıf başlatmaktı. Ayrıca metasınıfı başka bir sınıf gibi davranabiliriz. Bir yöntem çözümleme sırası vardır:
>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)
Ve yaklaşık olarak doğru repr
(işlevlerimizi temsil etmenin bir yolunu bulamadıkça artık değerlendiremeyiz).
>>> 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>})
Python 3 güncellemesi
Bir metasınıfta (bu noktada) iki anahtar yöntem vardır:
__prepare__
, ve__new__
__prepare__
OrderedDict
sınıf oluşturulurken ad alanı olarak kullanılacak özel bir eşleme (an gibi ) sağlamanıza olanak tanır . Seçtiğiniz ad alanının bir örneğini döndürmelisiniz. Uygulamazsanız __prepare__
normal dict
kullanılır.
__new__
final sınıfının asıl yaratılmasından / değiştirilmesinden sorumludur.
Çıplak kemikler, ekstra hiçbir şey yapmayan metasınıf:
class Meta(type):
def __prepare__(metaclass, cls, bases):
return dict()
def __new__(metacls, cls, bases, clsdict):
return super().__new__(metacls, cls, bases, clsdict)
Basit bir örnek:
Özelliklerinizde bazı basit doğrulama kodlarının çalışmasını istediğinizi varsayalım - her zaman bir int
veya a olması gerekir str
. Metaclass olmasaydı, sınıfınız şöyle görünecektir:
class Person:
weight = ValidateType('weight', int)
age = ValidateType('age', int)
name = ValidateType('name', str)
Gördüğünüz gibi, özelliğin adını iki kez tekrarlamanız gerekir. Bu, tahriş edici hatalarla birlikte yazım hatalarını mümkün kılar.
Basit bir metasınıf bu sorunu çözebilir:
class Person(metaclass=Validator):
weight = ValidateType(int)
age = ValidateType(int)
name = ValidateType(str)
Metaclass böyle görünecektir (gerekli olmadığından kullanmamak __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)
Örnek bir çalışma:
p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'
üretir:
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')
Not : Bu örnek, bir sınıf dekoratörü ile de gerçekleştirilebilecek kadar basittir, ancak muhtemelen gerçek bir metasınıf çok daha fazlasını yapacaktır.
Başvuru için 'ValidateType' sınıfı:
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
__set_name__(cls, name)
açıklayıcıda ( ValidateType
) açıklayıcıda adını ayarlamak için ( self.name
ve ayrıca bu durumda self.attr
). Bu, bu özel ortak kullanım durumu için metasınıflara dalmak zorunda kalmamak için eklenmiştir (bakınız PEP 487).
__call__()
Sınıf örneği oluştururken metasınıfın yönteminin rolüBirkaç aydan uzun bir süredir Python programlaması yaptıysanız, sonunda aşağıdaki gibi görünen kodlara rastlayacaksınız:
# define a class
class SomeClass(object):
# ...
# some definition here ...
# ...
# create an instance of it
instance = SomeClass()
# then call the object as if it's a function
result = instance('foo', 'bar')
İkincisi, __call__()
sınıfta sihirli yöntemi uyguladığınızda mümkündür .
class SomeClass(object):
# ...
# some definition here ...
# ...
def __call__(self, foo, bar):
return bar + foo
__call__()
Bir sınıfın bir örneği, bir çağrılabilir olarak kullanıldığı zaman, yöntem, çağrılır. Ancak önceki cevaplardan da gördüğümüz gibi, bir sınıfın kendisi bir metasınıfın örneğidir, bu yüzden sınıfı bir çağrılabilir olarak kullandığımızda (yani bir örneğini oluşturduğumuzda) aslında onun metasınıfı __call__()
yöntemini çağırıyoruz . Bu noktada, Python programcılarının çoğu biraz karışıktır, çünkü böyle bir örnek oluştururken yöntemini instance = SomeClass()
çağırdığınızı söylediler __init__()
. Biraz daha derine kazılmış ettik kim Bazı önce biliyoruz __init__()
var __new__()
. Peki, bugün __new__()
metasınıf olmadan önce başka bir gerçeklik katmanı ortaya çıkıyor __call__()
.
Yöntem çağırma zincirini, özellikle bir sınıf örneği oluşturma perspektifinden inceleyelim.
Bu, bir örnek oluşturulmadan hemen önce ve onu döndürmek üzere olan anı günlüğe kaydeden bir metasınıftır.
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
Bu metasınıfı kullanan bir sınıf
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__()."
Şimdi bir örnek oluşturalım 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.
Yukarıdaki kodun aslında görevleri günlüğe kaydetmekten başka bir şey yapmadığını gözlemleyin. Her yöntem asıl çalışmayı ebeveyninin uygulamasına devreder ve böylece varsayılan davranışı korur. Yana type
olduğunu Meta_1
'ın üst sınıf ( type
varsayılan ebeveyn metaclass olmak üzere) ve yukarıdaki çıkışın sipariş dizisini göz önüne alındığında artık sözde uygulaması ne olacağını bir ipucu var type.__call__()
:
class type:
def __call__(cls, *args, **kwarg):
# ... maybe a few things done to cls here
# then we call __new__() on the class to create an instance
instance = cls.__new__(cls, *args, **kwargs)
# ... maybe a few things done to the instance here
# then we initialize the instance with its __init__() method
instance.__init__(*args, **kwargs)
# ... maybe a few more things done to instance here
# then we return it
return instance
Metaclass ' __call__()
yönteminin ilk denilen yöntem olduğunu görebiliriz . Daha sonra, örneğin oluşturulmasını sınıfın __new__()
yöntemine ve başlatmayı örneğe uygular __init__()
. Nihayetinde örneği döndüren de odur.
Üstündeki Metaclass' olduğunu kaynaklanıyor itibaren __call__()
de edip etmeme çağrısı karar için fırsat verilir Class_1.__new__()
veya Class_1.__init__()
sonunda yapılacaktır. Yürütülmesi sırasında, bu yöntemlerden herhangi biri tarafından dokunulmamış bir nesneyi döndürebilir. Örneğin, singleton modeline bu yaklaşımı ele alalım:
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__()."
Tekrar tekrar bir tür nesne yaratmaya çalışırken neler olduğunu gözlemleyelim 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
Metasınıf, başka bir sınıfın nasıl (bazı) yaratılması gerektiğini anlatan bir sınıftır.
Bu, metasınıfı problemime bir çözüm olarak gördüğüm bir durum: Muhtemelen farklı bir şekilde çözülebilecek gerçekten karmaşık bir sorunum vardı, ancak bir metasınıf kullanarak çözmeyi seçtim. Karmaşıklık nedeniyle, modüldeki yorumların yazılan kod miktarını aştığı birkaç modülden biridir. İşte burada...
#!/usr/bin/env python
# Copyright (C) 2013-2014 Craig Phillips. All rights reserved.
# This requires some explaining. The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried. I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to. See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType. This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient. The complicated bit
# comes from requiring the GsyncOptions class to be static. By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace. Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet. The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method. This is the first and only time the class will actually have its
# dictionary statically populated. The docopt module is invoked to parse the
# usage document and generate command line options from it. These are then
# paired with their defaults and what's in sys.argv. After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored. This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times. The __getattr__ call hides this by default, returning the
# last item in a property's list. However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
class GsyncListOptions(object):
__initialised = False
class GsyncOptionsType(type):
def __initialiseClass(cls):
if GsyncListOptions._GsyncListOptions__initialised: return
from docopt import docopt
from libgsync.options import doc
from libgsync import __version__
options = docopt(
doc.__doc__ % __version__,
version = __version__,
options_first = True
)
paths = options.pop('<path>', None)
setattr(cls, "destination_path", paths.pop() if paths else None)
setattr(cls, "source_paths", paths)
setattr(cls, "options", options)
for k, v in options.iteritems():
setattr(cls, k, v)
GsyncListOptions._GsyncListOptions__initialised = True
def list(cls):
return GsyncListOptions
def __getattr__(cls, name):
cls.__initialiseClass()
return getattr(GsyncListOptions, name)[-1]
def __setattr__(cls, name, value):
# Substitut option names: --an-option-name for an_option_name
import re
name = re.sub(r'^__', "", re.sub(r'-', "_", name))
listvalue = []
# Ensure value is converted to a list type for GsyncListOptions
if isinstance(value, list):
if value:
listvalue = [] + value
else:
listvalue = [ None ]
else:
listvalue = [ value ]
type.__setattr__(GsyncListOptions, name, listvalue)
# Cleanup this module to prevent tinkering.
import sys
module = sys.modules[__name__]
del module.__dict__['GetGsyncOptionsType']
return GsyncOptionsType
# Our singlton abstract proxy class.
class GsyncOptions(object):
__metaclass__ = GetGsyncOptionsType()
type(obj)
Fonksiyon size bir nesnenin türünü alır.
type()
Bir sınıfın onun olduğu metaclass .
Metasınıf kullanmak için:
class Foo(object):
__metaclass__ = MyMetaClass
type
kendi metasınıfıdır. Bir sınıfın sınıfı bir metasınıftır - bir sınıfın gövdesi, sınıfı oluşturmak için kullanılan metaclass'a iletilen argümanlardır.
Burada sınıf yapımını özelleştirmek için metasınıfların nasıl kullanılacağı hakkında bilgi edinebilirsiniz.
type
aslında metaclass
başka bir sınıf oluşturan bir sınıftır. Çoğu metaclass
alt sınıflarıdır type
. metaclass
Aldığında new
ilk argüman olarak sınıf ve aşağıda belirtilen ayrıntıları ile sınıf nesnesi erişim sağlar:
>>> 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:
Sınıfın hiçbir zaman somutlaştırılmadığına dikkat edin; sınıf yaratmanın basit eylemi metaclass
.
Python sınıflarının kendileri - örneğin, meta sınıflarının nesneleridir.
Sınıfları şu şekilde belirlediğinizde uygulanan varsayılan metasınıf:
class foo:
...
meta sınıfı, sınıfların tümüne bir kural uygulamak için kullanılır. Örneğin, bir veritabanına erişmek için bir ORM oluşturduğunuzu ve her tablodaki kayıtların o tabloyla eşleştirilmiş bir sınıfta olmasını istediğinizi varsayalım (alanlar, iş kuralları vb. Temel alınarak), metasınıfın olası kullanımı örneğin, tüm tablolardan tüm kayıt sınıfları tarafından paylaşılan bağlantı havuzu mantığıdır. Başka bir kullanım, birden fazla kayıt sınıfını içeren yabancı anahtarları desteklemek için mantıktır.
metasınıfı tanımladığınızda, alt sınıf türünü kullanırsınız ve mantığınızı eklemek için aşağıdaki sihirli yöntemleri geçersiz kılabilirsiniz.
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
her neyse, bu ikisi en sık kullanılan kancalardır. metaclassing güçlüdür ve yukarıda metaclassing için hiçbir yere yakın ve kapsamlı bir liste yoktur.
Type () işlevi bir nesnenin türünü döndürebilir veya yeni bir tür oluşturabilir,
örneğin, type () işleviyle bir Hi sınıfı oluşturabiliriz ve Hi (object) sınıfıyla bu yolu kullanmamız gerekmez:
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
Sınıfları dinamik olarak oluşturmak için type () işlevine ek olarak, sınıfın oluşturma davranışını denetleyebilir ve metaclass kullanabilirsiniz.
Python nesne modeline göre, sınıf nesnedir, bu nedenle sınıf başka bir sınıfın örneği olmalıdır. Varsayılan olarak, bir Python sınıfı type sınıfının bir örneğidir. Yani, tür yerleşik sınıfların çoğunun metasınıfı ve kullanıcı tanımlı sınıfların metasınıfıdır.
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']
Metasınıfta anahtar kelime argümanlarını geçtiğimizde sihir etkili olacak, Python yorumlayıcısını ListMetaclass aracılığıyla CustomList oluşturduğunu gösteriyor. new (), bu noktada, örneğin sınıf tanımını değiştirebilir ve yeni bir yöntem ekleyebilir ve sonra düzeltilmiş tanımı döndürebiliriz.
Yayınlanan cevaplara ek olarak, metaclass
bir sınıfın davranışını tanımladığını söyleyebilirim . Böylece, metasınıfınızı açıkça ayarlayabilirsiniz. Ne zaman Python bir anahtar kelime alırsa class
o zaman metaclass
. Bulunmazsa - sınıfın nesnesini oluşturmak için varsayılan metasınıf türü kullanılır. __metaclass__
Özniteliği kullanarak metaclass
sınıfınızı ayarlayabilirsiniz :
class MyClass:
__metaclass__ = type
# write here other method
# write here one more method
print(MyClass.__metaclass__)
Çıkışı şöyle üretecektir:
class 'type'
Ve elbette, metaclass
sınıfınızı kullanarak oluşturulan herhangi bir sınıfın davranışını tanımlamak için kendinizinkini oluşturabilirsiniz .
Bunu yapmak için, varsayılan metaclass
yazım sınıfı, bu ana sınıf olduğu için miras alınmalıdır 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__)
Çıktı şöyle olacaktır:
class '__main__.MyMetaClass'
class 'type'
Nesne yönelimli programlamada, metasınıf, örnekleri sınıf olan bir sınıftır. Tıpkı sıradan bir sınıfın belirli nesnelerin davranışını tanımlaması gibi, bir metasınıf da belirli sınıfın davranışlarını ve örneklerini tanımlar Metaclass terimi basitçe sınıflar oluşturmak için kullanılan bir şey anlamına gelir. Başka bir deyişle, bir sınıfın sınıfıdır. Metaclass, nesneyi bir sınıf örneği gibi sınıf oluşturmak için kullanılır, sınıf bir metaclass örneğidir. Python sınıflarında da nesneler olarak kabul edilir.
İşte bunun için kullanılabilecek başka bir örnek:
metaclass
Örneğinin (sınıf) işlevini değiştirmek için tuşunu kullanabilirsiniz .class MetaMemberControl(type):
__slots__ = ()
@classmethod
def __prepare__(mcs, f_cls_name, f_cls_parents, # f_cls means: future class
meta_args=None, meta_options=None): # meta_args and meta_options is not necessarily needed, just so you know.
f_cls_attr = dict()
if not "do something or if you want to define your cool stuff of dict...":
return dict(make_your_special_dict=None)
else:
return f_cls_attr
def __new__(mcs, f_cls_name, f_cls_parents, f_cls_attr,
meta_args=None, meta_options=None):
original_getattr = f_cls_attr.get('__getattribute__')
original_setattr = f_cls_attr.get('__setattr__')
def init_getattr(self, item):
if not item.startswith('_'): # you can set break points at here
alias_name = '_' + item
if alias_name in f_cls_attr['__slots__']:
item = alias_name
if original_getattr is not None:
return original_getattr(self, item)
else:
return super(eval(f_cls_name), self).__getattribute__(item)
def init_setattr(self, key, value):
if not key.startswith('_') and ('_' + key) in f_cls_attr['__slots__']:
raise AttributeError(f"you can't modify private members:_{key}")
if original_setattr is not None:
original_setattr(self, key, value)
else:
super(eval(f_cls_name), self).__setattr__(key, value)
f_cls_attr['__getattribute__'] = init_getattr
f_cls_attr['__setattr__'] = init_setattr
cls = super().__new__(mcs, f_cls_name, f_cls_parents, f_cls_attr)
return cls
class Human(metaclass=MetaMemberControl):
__slots__ = ('_age', '_name')
def __init__(self, name, age):
self._name = name
self._age = age
def __getattribute__(self, item):
"""
is just for IDE recognize.
"""
return super().__getattribute__(item)
""" with MetaMemberControl then you don't have to write as following
@property
def name(self):
return self._name
@property
def age(self):
return self._age
"""
def test_demo():
human = Human('Carson', 27)
# human.age = 18 # you can't modify private members:_age <-- this is defined by yourself.
# human.k = 18 # 'Human' object has no attribute 'k' <-- system error.
age1 = human._age # It's OK, although the IDE will show some warnings. (Access to a protected member _age of a class)
age2 = human.age # It's OK! see below:
"""
if you do not define `__getattribute__` at the class of Human,
the IDE will show you: Unresolved attribute reference 'age' for class 'Human'
but it's ok on running since the MetaMemberControl will help you.
"""
if __name__ == '__main__':
test_demo()
metaclass
Onunla, ama bu yalnızca size biliniyor olabilir dikkatli olun (örneğin maymun sihirli gibi) pek çok şey vardır, güçlüdür.
Python'daki bir sınıf bir nesnedir ve diğer tüm nesneler gibi "şey" in bir örneğidir. Bu "bir şey" Metaclass olarak adlandırılan şeydir. Bu metasınıf, diğer sınıfın nesnelerini oluşturan özel bir sınıf türüdür. Bu nedenle, metasınıf yeni sınıflar yapmaktan sorumludur. Bu, programcının sınıfların oluşturulma şeklini özelleştirmesini sağlar.
Bir metasınıf oluşturmak için genellikle new () ve init () yöntemlerinin geçersiz kılınması yapılır. new () nesnelerin oluşturulma şeklini değiştirmek için geçersiz kılınırken, init () ise nesnenin başlatılma şeklini değiştirmek için geçersiz kılınabilir. Metaclass birkaç yolla oluşturulabilir. Yollardan biri type () işlevini kullanmaktır. type () işlevi, 3 parametre ile çağrıldığında bir metasınıf oluşturur. Parametreler: -
Metasınıf oluşturmanın başka bir yolu da 'metaclass' anahtar kelimesinden oluşur. Metasınıfı basit bir sınıf olarak tanımlayın. Devralınan sınıfın parametrelerinde, metaclass = metaclass_name iletin
Metaclass özellikle aşağıdaki durumlarda kullanılabilir: -
Python 3.6'da, metasınıflar __init_subclass__(cls, **kwargs)
için birçok yaygın kullanım durumunun yerini almak üzere yeni bir dunder yönteminin sunulduğunu unutmayın. Tanımlayıcı sınıfın bir alt sınıfı oluşturulduğunda Is çağrılır. Bkz. Python belgeleri .
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b