Jakie są niektóre (konkretne) przypadki użycia metaklasy?

Mam znajomego, który lubi używać metaklasy i regularnie oferuje je jako rozwiązanie.

Jestem przekonany, że prawie nigdy nie musisz używać metaklasy. Dlaczego? ponieważ pomyślałem, że jeśli robisz coś takiego klasie, prawdopodobnie powinieneś to robić obiektowi. I mały przeprojektowanie / refaktor jest w porządku.

Możliwość używania metaklasy spowodowała, że wiele osób w wielu miejscach używało klas jako jakiegoś obiektu drugiej klasy, co wydaje się katastrofalne dla mnie. Czy programowanie ma zostać zastąpione przez meta-programowanie? Dodanie dekoratorów klasy sprawiło niestety, że jest on jeszcze bardziej akceptowalny.

Więc proszę, jestem zdesperowany, aby poznać Twoje ważne (konkretne) przypadki użycia metaklas w Pythonie. Albo być oświeconym, dlaczego mutowanie klas jest lepsze niż mutowanie przedmiotów, czasami.

Zacznę:

Czasami, gdy korzystasz z usług osób trzecich biblioteka przydatna jest możliwość zmutować klasę w pewnym sposób.

(to jedyny przypadek, który przychodzi mi do głowy i nie jest konkretny)

Author: martineau, 2008-12-24

17 answers

Mam klasę, która zajmuje się nieinteraktywnym kreśleniem, jako frontend do Matplotlib. Jednak czasami chce się robić interaktywne kreślenie. Z tylko kilka funkcji okazało się, że byłem w stanie zwiększyć liczbę figure, wywołanie draw ręcznie, itp, ale musiałem to zrobić przed i po każdym wykreślanie połączenia. Aby stworzyć zarówno interaktywną owijkę do wykreślania, jak i owijkę do wykreślania poza ekranem, stwierdziłem, że bardziej wydajne jest robienie tego za pomocą metaklasy, owijając odpowiednie metody, niż zrobić coś takiego:

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

Ta metoda nie nadąża za zmianami API i tak dalej, ale taka, która iteruje nad atrybutami klasy w __init__ przed ponownym ustawieniem atrybutów klasy, jest bardziej wydajna i utrzymuje aktualność:

class _Interactify(type):
    def __init__(cls, name, bases, d):
        super(_Interactify, cls).__init__(name, bases, d)
        for base in bases:
            for attrname in dir(base):
                if attrname in d: continue # If overridden, don't reset
                attr = getattr(cls, attrname)
                if type(attr) == types.MethodType:
                    if attrname.startswith("add_"):
                        setattr(cls, attrname, wrap_pylab_newplot(attr))
                    elif attrname.startswith("set_"):
                        setattr(cls, attrname, wrap_pylab_show(attr))

Oczywiście, mogą być lepsze sposoby, aby to zrobić, ale odkryłem, że to jest skuteczne. Oczywiście można to zrobić również w __new__ lub __init__, ale to było rozwiązanie, które znalazłem najbardziej proste.

 20
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-26 18:42:03

Ostatnio zadano mi to samo pytanie i znalazłem kilka odpowiedzi. Mam nadzieję, że ożywić ten wątek, ponieważ chciałem rozwinąć kilka z wymienionych przypadków użycia i dodać kilka nowych.

Większość metaklasy jakie widziałem robi jedną z dwóch rzeczy:

  1. Rejestracja (dodawanie klasy do struktury danych):

    models = {}
    
    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            models[name] = cls = type.__new__(meta, name, bases, attrs)
            return cls
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Ilekroć podklasa Model, twoja klasa jest zarejestrowana w słowniku models:

    >>> class A(Model):
    ...     pass
    ...
    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...>,
     'B': <__main__.B class at 0x...>}
    

    Można to również zrobić za pomocą Klasa dekoratorów:

    models = {}
    
    def model(cls):
        models[cls.__name__] = cls
        return cls
    
    @model
    class A(object):
        pass
    

    Lub z jawną funkcją rejestracji:

    models = {}
    
    def register_model(cls):
        models[cls.__name__] = cls
    
    class A(object):
        pass
    
    register_model(A)
    

    Właściwie to jest prawie to samo: wspominasz o dekoratorach klas nieprzychylnie, ale to naprawdę nic więcej niż cukier składniowy dla wywołania funkcji na klasie, więc nie ma w tym magii.

    W każdym razie, zaletą metaklasy w tym przypadku jest dziedziczenie, ponieważ działają one dla dowolnych podklas, podczas gdy inne rozwiązania działają tylko dla podklas wyraźnie dekorowanych lub zarejestrowany.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
    
  2. Refaktoryzacja (modyfikowanie atrybutów klasy lub dodawanie nowych):

    class ModelMetaclass(type):
        def __new__(meta, name, bases, attrs):
            fields = {}
            for key, value in attrs.items():
                if isinstance(value, Field):
                    value.name = '%s.%s' % (name, key)
                    fields[key] = value
            for base in bases:
                if hasattr(base, '_fields'):
                    fields.update(base._fields)
            attrs['_fields'] = fields
            return type.__new__(meta, name, bases, attrs)
    
    class Model(object):
        __metaclass__ = ModelMetaclass
    

    Ilekroć podklasujesz Model i zdefiniujesz niektóre atrybuty Field, są one wstrzykiwane z ich nazwami (na przykład dla bardziej informacyjnych komunikatów o błędach) i zgrupowane w Słownik _fields (dla łatwej iteracji, bez konieczności przeglądania wszystkich atrybutów klasy i wszystkich atrybutów klas bazowych za każdym razem):{[29]]}

    >>> class A(Model):
    ...     foo = Integer()
    ...
    >>> class B(A):
    ...     bar = String()
    ...
    >>> B._fields
    {'foo': Integer('A.foo'), 'bar': String('B.bar')}
    

    Znowu można to zrobić (bez dziedziczenie) z klasą dekoratorską:

    def model(cls):
        fields = {}
        for key, value in vars(cls).items():
            if isinstance(value, Field):
                value.name = '%s.%s' % (cls.__name__, key)
                fields[key] = value
        for base in cls.__bases__:
            if hasattr(base, '_fields'):
                fields.update(base._fields)
        cls._fields = fields
        return cls
    
    @model
    class A(object):
        foo = Integer()
    
    class B(A):
        bar = String()
    
    # B.bar has no name :(
    # B._fields is {'foo': Integer('A.foo')} :(
    

    Lub wprost:

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!
    
    Pomimo tego, że w przeciwieństwie do twojego poparcia dla czytelnego i łatwego do utrzymania nie-meta programowania, jest to znacznie bardziej uciążliwe, zbędne i podatne na błędy:]}
    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}
    

Po rozważeniu najczęstszych i konkretnych przypadków użycia, jedynymi przypadkami, w których absolutnie musisz użyć metaklasy, są przypadki, gdy chcesz zmodyfikować nazwę klasy lub listę klas bazowych, ponieważ po zdefiniowaniu te parametry są wypiekane do klasy i żaden dekorator ani funkcja nie może ich odłączyć.

class Metaclass(type):
    def __new__(meta, name, bases, attrs):
        return type.__new__(meta, 'foo', (int,), attrs)

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

print A.__name__ # foo
print B.__name__ # foo
print issubclass(B, A)   # False
print issubclass(B, int) # True

Może to być przydatne w frameworkach do wydawania ostrzeżeń, gdy zdefiniowane są klasy o podobnych nazwach lub niekompletnych drzewach dziedziczenia, ale nie mogę wymyślić powodu poza trollingiem, aby zmienić te wartości. Może David Beazley może.

W każdym razie, w Pythonie 3 metaklasy mają również metodę __prepare__, która pozwala ocenić ciało klasy do mapowania innego niż dict, więc obsługa uporządkowanych atrybutów, przeciążonych atrybutów i innych fajnych rzeczy: {]}

import collections

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return collections.OrderedDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(list(attrs))
        # Do more stuff...

class A(metaclass=Metaclass):
    x = 1
    y = 2

# prints ['x', 'y'] rather than ['y', 'x']

 

class ListDict(dict):
    def __setitem__(self, key, value):
        self.setdefault(key, []).append(value)

class Metaclass(type):

    @classmethod
    def __prepare__(meta, name, bases, **kwds):
        return ListDict()

    def __new__(meta, name, bases, attrs, **kwds):
        print(attrs['foo'])
        # Do more stuff...

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

# prints [<function foo at 0x...>, <function foo at 0x...>] rather than <function foo at 0x...>

Można argumentować, że uporządkowane atrybuty można uzyskać za pomocą liczników tworzenia, a przeciążenie można symulować za pomocą domyślnych argumentów:

import itertools

class Attribute(object):
    _counter = itertools.count()
    def __init__(self):
        self._count = Attribute._counter.next()

class A(object):
    x = Attribute()
    y = Attribute()

A._order = sorted([(k, v) for k, v in vars(A).items() if isinstance(v, Attribute)],
                  key = lambda (k, v): v._count)

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=None):
        if x is None:
            return self._foo0()
        else:
            return self._foo1(x)

Poza tym, że jest znacznie brzydszy, jest również mniej elastyczny: co jeśli chcesz uporządkować dosłowne atrybuty, takie jak liczby całkowite i ciągi znaków? Co jeśli None jest poprawną wartością dla x?

Oto twórczy sposób rozwiązania pierwszego problemu:

import sys

class Builder(object):
    def __call__(self, cls):
        cls._order = self.frame.f_code.co_names
        return cls

def ordered():
    builder = Builder()
    def trace(frame, event, arg):
        builder.frame = frame
        sys.settrace(None)
    sys.settrace(trace)
    return builder

@ordered()
class A(object):
    x = 1
    y = 'foo'

print A._order # ['x', 'y']
A oto kreatywny sposób na rozwiązanie drugiego:]}
_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

    def foo(self, x=_undefined):
        if x is _undefined:
            return self._foo0()
        else:
            return self._foo1(x)

Ale to jest dużo, dużo voodoo-er niż zwykła metaklasa(zwłaszcza pierwsza, która naprawdę topi twój mózg). Chodzi mi o to, że patrzycie na metaklasy jako nieznane i sprzeczne z intuicją, ale możecie też patrzeć na nie jako na kolejny krok ewolucji w językach programowania: po prostu musicie dostosować swój sposób myślenia. W końcu można by zrobić wszystko w C, w tym definiowanie struktury ze wskaźnikami funkcji i przekazywanie jej jako pierwszego argumentu do jej funkcji. Osoba, która widzi C++ po raz pierwszy, może powiedzieć: "co to za Magia? Dlaczego kompilator domyślnie przekazuje this do metod, a nie do funkcji regularnych i statycznych? Lepiej być wyraźnym i gadatliwym o swoich argumentach". Ale programowanie zorientowane obiektowo jest znacznie potężniejsze, gdy je zdobędziesz; tak samo jak to, uh... programowanie quasi-aspektowe, jak sądzę. I raz ty zrozumieć metaklasy, są one rzeczywiście bardzo proste, więc dlaczego nie używać ich, gdy wygodne?

I wreszcie, metaklasy są rad, a programowanie powinno być zabawne. Używanie standardowych konstrukcji programistycznych i wzorców projektowych przez cały czas jest nudne i nie inspirujące i utrudnia wyobraźnię. Żyj trochę! Oto metametaklass, specjalnie dla Ciebie.

class MetaMetaclass(type):
    def __new__(meta, name, bases, attrs):
        def __new__(meta, name, bases, attrs):
            cls = type.__new__(meta, name, bases, attrs)
            cls._label = 'Made in %s' % meta.__name__
            return cls 
        attrs['__new__'] = __new__
        return type.__new__(meta, name, bases, attrs)

class China(type):
    __metaclass__ = MetaMetaclass

class Taiwan(type):
    __metaclass__ = MetaMetaclass

class A(object):
    __metaclass__ = China

class B(object):
    __metaclass__ = Taiwan

print A._label # Made in China
print B._label # Made in Taiwan
 50
Author: Dan Gittik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-04 14:41:01

Celem metaclasses nie jest zastąpienie rozróżnienia klasy / obiektu metaclass / class - jest to zmiana zachowania definicji klas (a tym samym ich instancji) w jakiś sposób. W praktyce jest to zmiana zachowania instrukcji class w sposób, który może być bardziej użyteczny dla danej domeny niż domyślny. Rzeczy, do których ich używałem to:

  • Śledzenie podklas, Zwykle do obsługi rejestru. Jest to przydatne podczas korzystania z konfiguracji stylu wtyczki, gdzie chcesz zarejestrować handler dla konkretnej rzeczy po prostu przez podklasowanie i ustawienie kilku atrybutów klasy. np. Załóżmy, że piszesz obsługę różnych formatów muzycznych, gdzie każda klasa implementuje odpowiednie metody (play / get tags itp.) dla swojego typu. Dodanie obsługi dla nowego typu staje się:

    class Mp3File(MusicFile):
        extensions = ['.mp3']  # Register this type as a handler for mp3 files
        ...
        # Implementation of mp3 methods go here
    

    Metaclass następnie utrzymuje słownik {'.mp3' : MP3File, ... } itd. i konstruuje obiekt odpowiedniego typu, gdy żądasz obsługi przez fabrykę funkcja.

  • Zmiana zachowania. Do pewnych atrybutów można przypisać specjalne znaczenie, co skutkuje zmianą zachowania, gdy są obecne. Na przykład, możesz szukać metod o nazwach _get_foo i _set_foo i przejrzyście przekonwertować je na właściwości. Jako przykład z prawdziwego świata, Oto przepis, który napisałem, aby podać więcej definicji struktur podobnych do C. Metaclass służy do konwersji zadeklarowanych elementów na łańcuch w formacie struct, obsługujący dziedziczenie itp.i stworzyć klasę zdolną do radzenia sobie z tym.

    Dla innych rzeczywistych przykładów, spójrz na różne ORM, jak sqlalchemy ORM lub sqlobject. Ponownie, celem jest interpretacja definicji (tutaj definicje kolumn SQL) z określonym znaczeniem.

 34
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-25 06:06:27

Zacznijmy od klasycznego cytatu Tima Petera:

Metaklasy to głębsza Magia niż 99% użytkownicy powinni się kiedykolwiek martwić. Jeśli zastanawiasz się, czy ich potrzebujesz, Ty nie (ludzie, którzy faktycznie potrzebują wiedzą z całą pewnością, że potrzebujesz ich, a nie potrzebujesz wyjaśnienie dlaczego). Tim Peters (c.l. P post 2002-12-22)

Powiedziawszy to, napotkałem (okresowo) prawdziwe zastosowania metaklas. Ten, który przychodzi na myśl jest w Django, gdzie wszystkie modele dziedziczą po modelach.Model. modelki.Z kolei Model robi jakąś poważną magię, aby owijać twoje modele DB dobrocią ORM Django. Ta magia dzieje się przez metaklasy. Tworzy wszelkiego rodzaju klasy WYJĄTKÓW, klasy menedżera itp. itd.

Zobacz django/db/models/base.py, class ModelBase() na początek historii.

 16
Author: Peter Rowell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-25 02:23:47

Metaklasy mogą być przydatne do budowy języków specyficznych dla domeny w Pythonie. Konkretnymi przykładami są deklaratywna składnia Django, sqlobject schematu bazy danych.

Podstawowy przykład z Konserwatywnej Metaklasy autorstwa Iana Bickinga:

Metaklasy, których używałem były przede wszystkim do wspierania pewnego rodzaju deklaratywny styl programowania. Na przykład, rozważ walidację schemat:

class Registration(schema.Schema):
    first_name = validators.String(notEmpty=True)
    last_name = validators.String(notEmpty=True)
    mi = validators.MaxLength(1)
    class Numbers(foreach.ForEach):
        class Number(schema.Schema):
            type = validators.OneOf(['home', 'work'])
            phone_number = validators.PhoneNumber()

Niektóre inne techniki: składniki na Budowa DSL w Pythonie (pdf).

Edit (by Ali): przykład użycia kolekcji I instancji jest tym, co wolałbym. Ważnym faktem są instancje, które dają więcej mocy i eliminują powód do korzystania z metaklasy. Ponadto warto zauważyć, że twój przykład wykorzystuje mieszankę klas I instancji, co z pewnością wskazuje, że nie możesz po prostu zrobić tego wszystkiego z metaklasami. I tworzy prawdziwie niejednorodny sposób działania.

number_validator = [
    v.OneOf('type', ['home', 'work']),
    v.PhoneNumber('phone_number'),
]

validators = [
    v.String('first_name', notEmpty=True),
    v.String('last_name', notEmpty=True),
    v.MaxLength('mi', 1),
    v.ForEach([number_validator,])
]
Nie jest idealny, ale już nie ma prawie zero magii, nie ma potrzeby metaklasy, i poprawiona jednolitość.
 6
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-09 18:26:10

Wczoraj myślałem o tym samym i całkowicie się Zgadzam. Komplikacje w kodzie spowodowane próbami uczynienia go bardziej deklaratywnym generalnie sprawiają, że kod jest trudniejszy do utrzymania, trudniejszy do odczytania i mniej pythoniczny moim zdaniem. Zwykle wymaga również dużej ilości kopii.copy()ing (aby zachować dziedziczenie i skopiować z klasy do instancji) i oznacza, że musisz szukać w wielu miejscach, aby zobaczyć, co się dzieje (zawsze patrząc od metaclass w górę), co idzie w parze z ziarnem Pythona również. Przeglądałem formencode i Kod sqlalchemy, aby sprawdzić, czy taki deklaratywny styl był tego wart, a jego najwyraźniej nie. Taki styl należy pozostawić deskryptorom (takim jak właściwości i metody) oraz niezmiennym danym. Ruby ma lepsze wsparcie dla takich deklaratywnych stylów i cieszę się, że rdzenny język python nie idzie tą drogą.

Widzę ich użycie do debugowania, dodaj metaklasę do wszystkich klas bazowych, aby uzyskać bogatsze informacje. Widzę też ich zastosowanie tylko w (bardzo) dużych projekty, aby pozbyć się jakiegoś kodu kotła (ale z utratą jasności). SQLAlchemy dla przykład używa ich gdzie indziej, aby dodać konkretną metodę niestandardową do wszystkich podklas na podstawie wartości atrybutu w ich definicji klasy np. przykład zabawki

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

Może mieć metaklasę, która wygenerowała metodę w tej klasie o specjalnych właściwościach opartych na "hello" (powiedzmy metodę, która dodała "hello" na końcu łańcucha). Może to być dobre dla konserwacji, aby upewnić się, że nie musisz napisać metodę w każdej podklasie, którą tworzysz zamiast tego wszystko, co musisz zdefiniować, to method_maker_value.

Potrzeba tego jest jednak tak rzadka i ogranicza tylko trochę pisania, że nie jest to naprawdę warte rozważenia, chyba że masz wystarczająco duży kod.

 5
Author: David Raznick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-28 03:32:00

Rozsądnym wzorcem użycia metaklasy jest zrobienie czegoś raz, gdy klasa jest zdefiniowana, a nie wielokrotnie, gdy ta sama klasa jest tworzona.

Gdy wiele klas ma to samo specjalne zachowanie, powtarzanie __metaclass__=X jest oczywiście lepsze niż powtarzanie kodu specjalnego przeznaczenia i/lub wprowadzanie doraźnie udostępnionych superklas.

Ale nawet z tylko jedną specjalną klasą i bez przewidywalnego rozszerzenia, __new__ i __init__ metaklasy są czystszym sposobem inicjalizacji klasy zmienne lub inne dane globalne niż mieszanie kodu specjalnego i zwykłych instrukcji def i class w ciele definicji klasy.

 5
Author: user412090,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-15 03:15:29

Nigdy absolutnie nie musisz używać metaklasy, ponieważ zawsze możesz zbudować klasę, która robi to, co chcesz, używając dziedziczenia lub agregacji klasy, którą chcesz zmodyfikować.

To powiedziawszy, może być bardzo przydatne w Smalltalk i Ruby, aby móc modyfikować istniejącą klasę, ale Python nie lubi tego robić bezpośrednio.

Jest doskonały Artykuł DeveloperWorks na temat metaklasowania w Pythonie, który może pomóc. Artykuł w Wikipedii {[8] } jest również ładny dobrze.

 4
Author: Charlie Martin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 21:17:25

Jedyny raz używałem metaklasy w Pythonie podczas pisania wrappera dla API Flickr.

Moim celem było zeskrobanie witryny API Flickra i dynamiczne generowanie pełnej hierarchii klas, aby umożliwić dostęp do API za pomocą obiektów Pythona:

# Both the photo type and the flickr.photos.search API method 
# are generated at "run-time"
for photo in flickr.photos.search(text=balloons):
    print photo.description

Więc w tym przykładzie, ponieważ wygenerowałem całe Python Flickr API ze strony internetowej, naprawdę nie znam definicji klas w czasie wykonywania. Możliwość dynamicznego generowania typów była bardzo przydatna.

 4
Author: Triptych,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 21:19:39

Metaklasy nie zastępują programowania! To tylko sztuczka, która może zautomatyzować lub uczynić bardziej eleganckimi niektóre zadania. Dobrym tego przykładem jest Pigmenty biblioteka podświetlania składni. Posiada klasę o nazwie RegexLexer, która pozwala użytkownikowi zdefiniować zbiór reguł lexingu jako wyrażenia regularne w klasie. Metaklasa jest używana do przekształcania definicji w użyteczny parser.

Są jak sól, zbyt łatwo się jej używa.
 3
Author: Benjamin Peterson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-24 22:15:22

Sposób, w jaki używałem metaklasy, polegał na dostarczeniu pewnych atrybutów klasom. Weźmy na przykład:

class NameClass(type):
    def __init__(cls, *args, **kwargs):
       type.__init__(cls, *args, **kwargs)
       cls.name = cls.__name__

Umieści atrybut name na każdej klasie, która będzie miała metaclass ustawiony tak, aby wskazywał na NameClass.

 3
Author: hyperboreean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-25 11:47:43

Niektóre biblioteki GUI mają problemy, gdy wiele wątków próbuje z nimi współdziałać. tkinter jest jednym z takich przykładów; i chociaż można jawnie rozwiązać problem ze zdarzeniami i kolejkami, może być znacznie prostsze Użycie biblioteki w sposób, który całkowicie ignoruje problem. Oto Magia metaklas.

Możliwość płynnego dynamicznego przepisywania całej biblioteki tak, aby działała prawidłowo zgodnie z oczekiwaniami w aplikacji wielowątkowej może być niezwykle pomocna w niektórych okoliczności. Moduł safetkinter robi to za pomocą metaklasy dostarczonej przez moduł threadbox -- zdarzenia i kolejki nie są potrzebne.

Jednym z aspektów threadbox jest to, że nie obchodzi go, jaką klasę klonuje. Jest to przykład tego, jak wszystkie klasy bazowe mogą być dotykane przez metaklasę w razie potrzeby. Kolejną korzyścią, która przychodzi z metaklasami, jest to, że działają one również na klasach dziedziczących. Programy, które same piszą-dlaczego nie?

 3
Author: Noctis Skytower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-04 19:29:42

Jedynym uzasadnionym przypadkiem użycia metaklasy jest powstrzymanie innych wścibskich programistów przed dotykaniem Twojego kodu. Gdy wścibski deweloper opanuje metaklasy i zacznie grzebać w Twoich, dorzuć kolejny lub dwa poziomy, aby ich nie dopuścić. Jeśli to nie zadziała, zacznij używać type.__new__ lub jakiegoś schematu używającego metaklasy rekurencyjnej.

(pisane językiem w policzku, ale widziałem takie zaciemnienie. Django jest doskonałym przykładem)

 3
Author: Mike A,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-20 03:18:02

To jest małe zastosowanie, ale... jedną z rzeczy, do których znalazłem przydatne metaklasy, jest wywołanie funkcji za każdym razem, gdy tworzona jest podklasa. Skodyfikowałem to w metaklasę, która szuka atrybutu __initsubclass__: ilekroć tworzona jest podklasa, wszystkie klasy nadrzędne, które definiują tę metodę, są wywoływane z __initsubclass__(cls, subcls). Pozwala to na utworzenie klasy nadrzędnej, która następnie rejestruje wszystkie podklasy z jakimś globalnym rejestrem, uruchamia niezmienne kontrole podklas, gdy są zdefiniowane, wykonuje późne wiązanie operacje itp... wszystko to bez konieczności ręcznego wywoływania funkcji lub w celu tworzenia niestandardowych metaklasy, które wykonują każde z tych oddzielnych zadań.

Powoli zdaję sobie sprawę, że ukryta magiczność tego zachowania jest w pewnym sensie niepożądana, ponieważ jest to nieoczekiwane, gdy patrzymy na definicję klasy z kontekstu... dlatego zrezygnowałem z używania tego rozwiązania do niczego poważnego poza inicjalizacją atrybutu __super dla każdej klasy I instancji.
 2
Author: Eli Collins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-14 16:44:08

Ostatnio musiałem użyć metaklasy, aby pomóc deklaratywnie zdefiniować model SQLAlchemy wokół tabeli bazy danych wypełnionej danymi spisu powszechnego z http://census.ire.org/data/bulkdata.html

IRE dostarcza powłoki bazy danych dla tabel danych spisu powszechnego, które tworzą kolumny całkowite zgodnie z konwencją nazewnictwa z biura Spisu Powszechnego z p012015, p012016, p012017, itd.

Chciałem a) być w stanie uzyskać dostęp do tych kolumn za pomocą składni model_instance.p012017, B) być dość wyraźny o tym, co robiłem i c) nie musiałem jawnie definiować dziesiątek pól w modelu, więc podklasowałem DeclarativeMeta sqlalchemy, aby iteracji przez zakres kolumn i automatycznie tworzyć pola Modelu odpowiadające kolumnom:

from sqlalchemy.ext.declarative.api import DeclarativeMeta

class CensusTableMeta(DeclarativeMeta):
    def __init__(cls, classname, bases, dict_):
        table = 'p012'
        for i in range(1, 49):
            fname = "%s%03d" % (table, i)
            dict_[fname] = Column(Integer)
            setattr(cls, fname, dict_[fname])

        super(CensusTableMeta, cls).__init__(classname, bases, dict_)

Mógłbym następnie użyć tej metaklasy do definicji modelu i uzyskać dostęp do automatycznie wyliczanych pól w modelu:

CensusTableBase = declarative_base(metaclass=CensusTableMeta)

class P12Tract(CensusTableBase):
    __tablename__ = 'ire_p12'

    geoid = Column(String(12), primary_key=True)

    @property
    def male_under_5(self):
        return self.p012003

    ...
 1
Author: Geoffrey Hing,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-08 17:59:56

Wydaje się, że istnieje legalne użycie opisane tutaj - przepisywanie dokumentów Pythona za pomocą Metaklasy.

 1
Author: mistermarko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-25 08:44:58

Musiałem użyć ich raz dla parsera binarnego, aby ułatwić korzystanie z niego. Definiujesz klasę wiadomości z atrybutami pól obecnych na drucie. Należało je uporządkować w sposób, w jaki zostały zadeklarowane, aby skonstruować z niego ostateczny format drutu. Możesz to zrobić z metaklasami, jeśli używasz uporządkowanej przestrzeni nazw dict. W rzeczywistości jego w przykładach dla Metaklasy:

Https://docs.python.org/3/reference/datamodel.html#metaclass-example

Ale ogólnie: bardzo dokładnie oceń, jeśli naprawdę potrzebujesz dodatkowej złożoności metaklasy.

 0
Author: GeeF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-02 12:47:37