메타 클래스의 (구체적인) 사용 사례는 무엇입니까?


118

메타 클래스 사용을 좋아하고 정기적으로 솔루션으로 제공하는 친구가 있습니다.

메타 클래스를 거의 사용할 필요가 없다는 것을 알고 있습니다. 왜? 왜냐하면 당신이 클래스에 그런 일을하고 있다면 아마도 객체에 대해해야 할 것입니다. 그리고 작은 재 설계 / 리팩터링이 필요합니다.

메타 클래스를 사용할 수있게 됨으로써 많은 장소에서 많은 사람들이 클래스를 일종의 2 등급 객체로 사용하게되는데, 이는 나에게 비참 해 보입니다. 프로그래밍이 메타 프로그래밍으로 대체됩니까? 클래스 데코레이터를 추가하면 안타깝게도 더 수용 할 수있게되었습니다.

그러니 제발, 파이썬에서 메타 클래스에 대한 유효한 (구체적인) 사용 사례를 알고 싶습니다. 또는 때때로 클래스를 변경하는 것이 객체를 변경하는 것보다 나은 이유에 대해 깨달을 수 있습니다.

나는 시작할 것이다:

때로는 타사 라이브러리를 사용할 때 특정 방식으로 클래스를 변경할 수 있으면 유용합니다.

(이것은 내가 생각할 수있는 유일한 경우이며 구체적이지 않습니다)


3
이것은 좋은 질문입니다. 아래 답변을 보면 메타 클래스에 대한 구체적인 사용과 같은 것이 없다는 것이 분명합니다.
Marcus Ottosson 2014

답변:


25

Matplotlib의 프런트 엔드로서 비대화 형 플로팅을 처리하는 클래스가 있습니다. 그러나 때로는 대화 형 플로팅을 원합니다. 몇 가지 기능만으로 그림 수를 늘리고, 그리기를 수동으로 호출하는 등의 작업을 수행 할 수 있다는 것을 알았지 만 모든 플로팅 호출 전후에 이러한 작업을 수행해야했습니다. 따라서 대화 형 플로팅 래퍼와 오프 스크린 플로팅 래퍼를 모두 만들려면 다음과 같은 작업을 수행하는 것보다 메타 클래스를 통해 적절한 메서드를 래핑하는 것이 더 효율적이라는 것을 알았습니다.

class PlottingInteractive:
    add_slice = wrap_pylab_newplot(add_slice)

이 메소드는 API 변경 등을 따라 가지 못하지만 클래스 속성을 __init__재설정하기 전에 클래스 속성을 반복 하는 메소드가 더 효율적이고 최신 상태로 유지합니다.

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

물론이 작업을 수행하는 더 좋은 방법이있을 수 있지만 이것이 효과적이라는 것을 알았습니다. 물론 이것은 __new__또는 에서도 수행 할 수 __init__있지만 이것이 내가 가장 간단하게 찾은 해결책이었습니다.


103

나는 최근에 같은 질문을 받았고 몇 가지 대답을 내놓았습니다. 언급 된 몇 가지 사용 사례에 대해 자세히 설명하고 몇 가지 새로운 사례를 추가하고 싶었으므로이 스레드를 되살려도 괜찮기를 바랍니다.

내가 본 대부분의 메타 클래스는 다음 두 가지 중 하나를 수행합니다.

  1. 등록 (데이터 구조에 클래스 추가) :

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

    하위 클래스를 만들 때마다 Model클래스가 models사전에 등록됩니다 .

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

    이것은 클래스 데코레이터로도 가능합니다 :

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

    또는 명시 적 등록 기능 사용 :

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

    실제로 이것은 거의 동일합니다. 클래스 데코레이터를 불리하게 언급하지만 실제로는 클래스의 함수 호출에 대한 구문 설탕에 지나지 않으므로 마술은 없습니다.

    어쨌든,이 경우 메타 클래스의 장점은 모든 서브 클래스에서 작동하기 때문에 상속성입니다. 반면 다른 솔루션은 명시 적으로 장식되거나 등록 된 서브 클래스에서만 작동합니다.

    >>> class B(A):
    ...     pass
    ...
    >>> models
    {'A': <__main__.A class at 0x...> # No B :(
  2. 리팩토링 (클래스 속성 수정 또는 새 속성 추가) :

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

    하위 클래스를 지정 Model하고 일부 Field속성을 정의 할 때마다 이름이 삽입되고 (예 : 더 많은 정보를 제공하는 오류 메시지를 위해) _fields사전에 그룹화됩니다 (모든 클래스 속성과 모든 기본 클래스를 살펴볼 필요없이 쉽게 반복 할 수 있도록). 매번 속성) :

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

    다시 말하지만, 이것은 클래스 데코레이터로 (상속없이) 할 수 있습니다 :

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

    또는 명시 적으로 :

    class A(object):
        foo = Integer('A.foo')
        _fields = {'foo': foo} # Don't forget all the base classes' fields, too!

    읽기 쉽고 유지 관리가 가능한 비 메타 프로그래밍에 대한 귀하의 옹호와는 달리 이것은 훨씬 더 번거롭고 중복되며 오류가 발생하기 쉽습니다.

    class B(A):
        bar = String()
    
    # vs.
    
    class B(A):
        bar = String('bar')
        _fields = {'B.bar': bar, 'A.foo': A.foo}

가장 일반적이고 구체적인 사용 사례를 고려했을 때 메타 클래스를 절대적으로 사용해야하는 유일한 경우는 클래스 이름이나 기본 클래스 목록을 수정하려는 경우입니다. 일단 정의되면 이러한 매개 변수가 클래스에 구워지고 데코레이터가 없기 때문입니다. 또는 기능은 그들을 구울 수 있습니다.

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

class Baseclass(object):
    __metaclass__ = Metaclass

class A(Baseclass):
    pass

class B(A):
    pass

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

이것은 유사한 이름을 가진 클래스 나 불완전한 상속 트리가 정의 될 때마다 경고를 발행하는 프레임 워크에서 유용 할 수 있지만 실제로 이러한 값을 변경하는 트롤링 외에 이유를 생각할 수 없습니다. 아마도 David Beazley는 할 수 있습니다.

어쨌든 Python 3에서 메타 클래스에는 __prepare__메서드가있어 클래스 본문을 a 이외의 매핑으로 평가할 수 dict있으므로 순서가 지정된 속성, 오버로드 된 속성 및 기타 멋진 항목을 지원합니다.

import collections

class Metaclass(type):

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

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

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

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

 

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

class Metaclass(type):

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

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

class A(metaclass=Metaclass):

    def foo(self):
        pass

    def foo(self, x):
        pass

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

정렬 된 속성은 생성 카운터를 사용하여 얻을 수 있으며 오버로딩은 기본 인수로 시뮬레이션 할 수 있습니다.

import itertools

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

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

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

 

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

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

훨씬 더 추악 할뿐만 아니라 유연성도 떨어집니다. 정수 및 문자열과 같은 순서가 지정된 리터럴 속성을 원한다면 어떨까요? 어떤 경우에하는 것은 None유효한 값입니다 x?

다음은 첫 번째 문제를 해결하는 창의적인 방법입니다.

import sys

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

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

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

print A._order # ['x', 'y']

두 번째 문제를 해결하는 창의적인 방법은 다음과 같습니다.

_undefined = object()

class A(object):

    def _foo0(self):
        pass

    def _foo1(self, x):
        pass

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

그러나 이것은 단순한 메타 클래스 (특히 뇌를 정말로 녹이는 첫 번째 메타 클래스)보다 훨씬 더 부두교입니다. 내 요점은 메타 클래스를 생소하고 직관적이지 않은 것으로 보지만 프로그래밍 언어에서 다음 단계의 진화로 볼 수도 있다는 것입니다. 사고 방식을 조정하기 만하면됩니다. 결국 함수 포인터로 구조체를 정의하고이를 함수에 대한 첫 번째 인수로 전달하는 것을 포함하여 C에서 모든 작업을 수행 할 수 있습니다. C ++를 처음 보는 사람은 "이 마법은 무엇입니까? 컴파일러가 암시 적으로 전달하는 이유는 무엇입니까?this메서드에는 있지만 일반 및 정적 함수에는 적용되지 않습니까? 당신의 주장에 대해 명시적이고 장황하게 말하는 것이 더 낫습니다. "그러나 객체 지향 프로그래밍은 일단 이해하면 훨씬 더 강력합니다. 그리고 이것은 어 ... 준 종횡 지향 프로그래밍입니다. 메타 클래스를 이해하면 실제로 매우 간단합니다. 편리 할 때 사용하지 않겠습니까?

마지막으로, 메타 클래스는 rad이고 프로그래밍은 재미 있어야합니다. 항상 표준 프로그래밍 구조와 디자인 패턴을 사용하는 것은 지루하고 영감을주지 않으며 상상력을 방해합니다. 조금 살다! 여기 당신만을위한 metametaclass가 있습니다.

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

편집하다

이것은 꽤 오래된 질문이지만 여전히 찬성 투표를 받고 있으므로보다 포괄적 인 답변에 대한 링크를 추가 할 것이라고 생각했습니다. 메타 클래스와 그 용도에 대해 더 알고 싶다면 여기에 기사를 게시했습니다 .


5
즉, 시간에 대한 감사를 작성하고 여러 예시를 좋은 대답
첸 A.

"...이 경우 메타 클래스의 장점은 상속성입니다. 모든 서브 클래스에서 작동하므로"-Python 3에서는 그렇지 않습니다. 모든 자식 클래스가 __metaclass__속성을 상속하기 때문에 Python 2에서만 작동한다고 생각 하지만이 속성은 더 이상 Python 3에서 특별하지 않습니다.이 "하위 클래스도 부모의 메타 클래스에 의해 구성됩니다"가 Python 3에서 작동하도록 만들 수있는 방법이 있습니까? ?
ForceBru

2
메타 클래스가 M 인 A에서 상속 된 클래스 B도 M의 유형이기 때문에 Python 3에서도 마찬가지입니다. 따라서 B가 평가되면 M이 호출되어이를 생성 할 수 있습니다. "모든 하위 클래스에서 작업"(A의). 그래도 Python 3.6은 훨씬 더 간단한을 도입 init_subclass했기 때문에 이제 기본 클래스에서 하위 클래스를 조작 할 수 있으며 더 이상 해당 목적을위한 메타 클래스가 필요하지 않습니다.
Dan Gittik 2018-06-10

이것은 훌륭합니다. 저는 메타 클래스에 대한 많은 블로그 포스트를 읽었습니다. 오직 이것 만이 메타 클래스의 장단점과 대안을 알 수 있습니다.
ospider

36

메타 클래스의 목적은 클래스 / 객체 구분을 메타 클래스 / 클래스로 대체하는 것이 아니라 어떤 방식 으로든 클래스 정의 (및 인스턴스)의 동작을 변경하는 것입니다. 실제로는 기본보다 특정 도메인에 더 유용 할 수있는 방식으로 클래스 문의 동작을 변경하는 것입니다. 내가 그것들을 사용한 것은 다음과 같습니다.

  • 일반적으로 핸들러를 등록하기 위해 서브 클래스를 추적합니다. 이것은 플러그인 스타일 설정을 사용할 때 편리합니다. 여기서 몇 가지 클래스 속성을 서브 클래 싱하고 설정하기 만하면 특정 항목에 대한 핸들러를 등록 할 수 있습니다. 예. 다양한 음악 형식에 대한 핸들러를 작성한다고 가정 해 보겠습니다. 여기서 각 클래스는 해당 유형에 적합한 메서드 (play / get 태그 등)를 구현합니다. 새 유형에 대한 핸들러 추가는 다음과 같습니다.

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

    그런 다음 메타 클래스는 {'.mp3' : MP3File, ... }etc 의 사전을 유지 하고 팩토리 함수를 통해 핸들러를 요청할 때 적절한 유형의 객체를 구성합니다.

  • 행동 변화. 특정 속성에 특별한 의미를 부여하여 해당 속성이있을 때 동작이 변경되도록 할 수 있습니다. 예를 들어, 당신은 이름을 가진 방법을 찾아 할 수 있습니다 _get_foo_set_foo투명 그 속성을 변환합니다. 실제 예를 들어, 여기에 좀 더 C와 같은 구조체 정의를 제공하기 위해 쓴 조리법. 메타 클래스는 선언 된 항목을 구조체 형식 문자열로 변환하고 상속 등을 처리하는 데 사용되며이를 처리 할 수있는 클래스를 생성합니다.

    다른 실제 사례를 보려면 sqlalchemy의 ORM 또는 sqlobject 와 같은 다양한 ORM을 살펴 보십시오 . 다시 말하지만, 목적은 특정 의미로 정의 (여기서는 SQL 열 정의)를 해석하는 것입니다.


3
네, 하위 클래스를 추적합니다. 그런데 왜 그걸 원 하겠어요? 귀하의 예제는 register_music_file (Mp3File, [ '.mp3'])에 대해 암시 적이며 명시적인 방법이 더 읽기 쉽고 유지 관리하기 쉽습니다. 이것은 내가 말하는 나쁜 사례의 예입니다.
Ali Afshar

ORM의 경우, 테이블을 정의하는 클래스 기반 방법 또는 매핑 된 개체의 메타 클래스에 대해 이야기하고 있습니까? SQLAlchemy는 (올바르게) 모든 클래스에 매핑 할 수 있기 때문에 (그리고 해당 활동에 메타 클래스를 사용하지 않는다고 가정합니다).
Ali Afshar

10
모든 하위 클래스에 대해 추가 등록 방법을 요구하는 것보다 선언적인 스타일을 선호합니다. 모든 것이 단일 위치에 래핑 된 경우 더 좋습니다.
Brian

sqlalchemy의 경우 대부분 선언적 계층을 생각하고 있으므로 sqlobject가 더 나은 예일 것입니다. 그러나 내부적으로 사용되는 메타 클래스는 의미를 선언하기 위해 특정 속성을 유사한 재 해석 한 예이기도합니다.
Brian

2
SO 타임 아웃 시나리오에서 내 conmments 중 하나가 분실되어 죄송합니다. 나는 거의 혐오스러운 선언적 클래스를 찾습니다. 나는 사람들이 그것을 좋아한다는 것을 압니다. 그러나 (경험으로) UN 선언을 원하는 상황에서는 사용할 수 없다는 것을 알고 있습니다. 수업 등록을 취소하는 것은 어렵습니다 .
Ali Afshar

17

Tim Peter의 고전적인 인용구부터 시작하겠습니다.

메타 클래스는 99 %의 사용자가 걱정해야 할 것보다 더 깊은 마법입니다. 당신이 그것들이 필요한지 궁금하다면, 당신은 그럴 필요가 없습니다 (실제로 그것들을 필요로하는 사람들은 그것들이 필요하다는 것을 확실히 알고 있고, 이유에 대한 설명이 필요하지 않습니다). 팀 피터스 (CLP post 2002-12-22)

그래도 메타 클래스의 진정한 사용을 (주기적으로) 실행했습니다. 떠오르는 것은 모든 모델이 models.Model에서 상속하는 Django에 있습니다. model.Model은 DB 모델을 Django의 ORM 장점으로 감싸는 몇 가지 진지한 마법을 수행합니다. 그 마법은 메타 클래스를 통해 발생합니다. 모든 종류의 예외 클래스, 관리자 클래스 등을 생성합니다.

이야기의 시작은 django / db / models / base.py, ModelBase () 클래스를 참조하십시오.


네, 요점을 알았습니다. 메타 클래스를 "어떻게"또는 "왜"사용하는지 궁금하지 않습니다. "누가"와 "무엇"이 궁금합니다. 여기서 ORM은 일반적인 경우입니다. 불행히도 Django의 ORM은 마법이 덜한 SQLAlchemy에 비해 매우 열악합니다. 매직은 나쁘고 메타 클래스는 실제로 필요하지 않습니다.
Ali Afshar

9
과거에 Tim Peters의 인용문을 읽은 후 시간은 그의 진술이 다소 도움이되지 않음을 보여주었습니다. 여기 StackOverflow에서 Python 메타 클래스를 조사하기 전까지는 구현 방법이 분명해졌습니다. 메타 클래스를 작성하고 사용하는 방법을 배우도록 강요 한 후, 그들의 능력은 저를 놀라게했고 파이썬이 실제로 어떻게 작동하는지 훨씬 더 잘 이해하게했습니다. 클래스는 재사용 가능한 코드를 제공 할 수 있으며 메타 클래스는 이러한 클래스에 대해 재사용 가능한 개선 사항을 제공 할 수 있습니다.
Noctis Skytower 2013 년

6

메타 클래스는 Python에서 도메인 특정 언어를 구성하는 데 유용 할 수 있습니다. 구체적인 예는 Django, SQLObject의 데이터베이스 스키마 선언 구문입니다.

Ian Bicking의 A Conservative Metaclass 의 기본 예 :

내가 사용한 메타 클래스는 주로 일종의 선언적 프로그래밍 스타일을 지원하는 데 사용되었습니다. 예를 들어 유효성 검사 스키마를 고려하십시오.

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

기타 기술 : Python으로 DSL을 구축하기위한 재료 (pdf).

Edit (by Ali) : 컬렉션과 인스턴스를 사용하여이 작업을 수행하는 예는 제가 선호하는 것입니다. 중요한 사실은 더 많은 권한을 제공하고 메타 클래스를 사용할 이유를 제거하는 인스턴스입니다. 여러분의 예제는 클래스와 인스턴스의 혼합을 사용한다는 점에 주목할 가치가 있습니다. 이는 확실히 메타 클래스로 모든 것을 할 수 없다는 것을 나타냅니다. 그리고 그것을 수행하는 진정으로 불균일 한 방법을 만듭니다.

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,])
]

완벽하지는 않지만 이미 마법이 거의없고 메타 클래스가 필요 없으며 균일 성이 향상되었습니다.


감사합니다. 이것은 필요에 따라 중첩 된 컬렉션을 사용하는 단순 컬렉션 인스턴스를 기반으로 더 간단 할 수있는 불필요하고보기 흉하고 관리 할 수없는 사용 사례의 아주 좋은 예입니다.
Ali Afshar

1
@Ali A : 메타 클래스를 통한 선언적 구문과 간단한 컬렉션 인스턴스를 기반으로 한 접근 방식 간의 나란히 비교하는 구체적인 예를 제공 할 수 있습니다.
jfs

@Ali A : 컬렉션 스타일 예제를 추가하기 위해 내 답변을 편집 할 수 있습니다.
jfs

알았어. 죄송합니다. 오늘은 조금 바쁘지만 추후 / 내일 질문에 답변 해 드리겠습니다. 즐거운 휴일 보내세요!
Ali Afshar

2
두 번째 예는 유효성 검사기 인스턴스를 이름과 연결해야했기 때문에 추악합니다. 약간 더 나은 방법은 목록 대신 사전을 사용하는 것입니다. 그러나 파이썬에서 클래스는 사전에 대한 구문 설탕 일뿐입니다. 클래스를 사용하지 않는 이유는 무엇입니까? 파이썬 아가씨는 문자열이 가질 수있는 공백이나 특수 문자를 포함 할 수 없기 때문에 무료 이름 유효성 검사도받을 수 있습니다.
Lie Ryan

6

메타 클래스 사용의 합리적인 패턴은 동일한 클래스가 인스턴스화 될 때마다 반복적으로 수행되지 않고 클래스가 정의 될 때 한 번 수행하는 것입니다.

여러 클래스가 동일한 특수 동작을 공유 할 때 __metaclass__=X반복은 특수 목적 코드를 반복하거나 임시 공유 슈퍼 클래스를 도입하는 것보다 분명히 낫습니다.

그러나 심지어 단 하나의 특별한 클래스와 예측 가능한 확장과 __new____init__메타 클래스의 특수 목적 코드와 정상 혼용보다 클래스 변수 또는 다른 글로벌 데이터를 초기화하는 청소기 방법 defclass클래스 정의의 본문에 문을.


5

Python에서 메타 클래스를 사용한 유일한 시간은 Flickr API 용 래퍼를 작성할 때였습니다.

내 목표는 flickr의 API 사이트 를 긁어 내고 Python 객체를 사용하여 API 액세스를 허용하는 완전한 클래스 계층 구조를 동적으로 생성하는 것이 었습니다 .

# 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

이 예제에서는 웹 사이트에서 전체 Python Flickr API를 생성했기 때문에 런타임에 클래스 정의를 알지 못합니다. 유형을 동적으로 생성 할 수 있다는 것은 매우 유용했습니다.


2
메타 클래스를 사용하지 않고 동적으로 유형을 생성 할 수 있습니다. >>> help (type)
Ali Afshar

8
당신이 그것을 인식하지 않은 경우에도 있는 다음 메타 클래스를 사용하여. type은 실제로 가장 일반적인 메타 클래스입니다. :-)
Veky

5

어제 똑같은 생각을했는데 완전히 동의합니다. 코드를 더 선언적으로 만들려는 시도로 인해 발생하는 코드의 복잡성은 일반적으로 코드베이스를 유지 관리하기 어렵고 읽기 어렵고 비단뱀 적이라고 생각합니다. 또한 일반적으로 많은 copy.copy () ing (상속성을 유지하고 클래스에서 인스턴스로 복사하기 위해)이 필요하며 무슨 일이 일어나고 있는지 (항상 메타 클래스에서 찾고) 여러 곳에서 찾아야 함을 의미합니다. 파이썬 곡물도. 나는 formencode와 sqlalchemy 코드를 통해 그러한 선언적 스타일이 그만한 가치가 있는지 그리고 분명히 그렇지 않은지 확인했습니다. 이러한 스타일은 설명자 (예 : 속성 및 메서드)와 변경 불가능한 데이터에 맡겨야합니다. Ruby는 이러한 선언적 스타일을 더 잘 지원하며 핵심 파이썬 언어가 그 길을 따르지 않아서 기쁩니다.

디버깅 용도를 확인하고 모든 기본 클래스에 메타 클래스를 추가하여 더 풍부한 정보를 얻을 수 있습니다. 나는 또한 일부 상용구 코드를 제거하기 위해 (매우) 큰 프로젝트에서만 사용되는 것을 봅니다 (그러나 명확성을 잃는 경우). 들어 sqlalchemy는 클래스 정의의 속성 값 (예 : 장난감 예제)을 기반으로 모든 하위 클래스에 특정 사용자 지정 메서드를 추가하기 위해 다른 곳에서 사용합니다.

class test(baseclass_with_metaclass):
    method_maker_value = "hello"

"hello"(문자열 끝에 "hello"를 추가 한 메서드)를 기반으로하는 특수 속성을 사용하여 해당 클래스에서 메서드를 생성하는 메타 클래스를 가질 수 있습니다. 만드는 모든 하위 클래스에 메서드를 작성할 필요가 없는지 확인하는 것이 유지 관리에 좋을 수 있습니다. 대신 정의해야하는 것은 method_maker_value뿐입니다.

이것에 대한 필요성은 매우 드물고 충분한 코드베이스가 없다면 고려할 가치가없는 약간의 타이핑 만 줄입니다.


5

수정하려는 클래스의 상속 또는 집계를 사용하여 원하는 작업을 수행하는 클래스를 항상 구성 할 수 있으므로 메타 클래스를 절대로 사용할 필요 가 없습니다 .

즉, 스몰 토크와 루비에서는 기존 클래스를 수정하는 것이 매우 편리 할 수 ​​있지만 파이썬은 직접 그렇게하는 것을 좋아하지 않습니다.

도움이 될 수있는 Python의 메타 클래 싱에 대한 훌륭한 DeveloperWorks 기사 가 있습니다. 위키 백과의 문서 도 꽤 좋다.


1
또한 객체 지향 프로그래밍을 수행하기 위해 객체가 필요하지 않습니다. 1 급 함수로 수행 할 수 있습니다. 따라서 개체를 사용할 필요 가 없습니다 . 그러나 그들은 편의를 위해 거기에 있습니다. 그래서 나는 당신이 첫 번째 문단에서 어떤 요점을하려고하는지 잘 모르겠습니다.
Tyler Crompton

1
질문을 돌아보세요.
Charlie Martin

4

일부 GUI 라이브러리는 여러 스레드가 상호 작용하려고 할 때 문제가 있습니다. tkinter하나의 예입니다. 이벤트와 큐로 문제를 명시 적으로 처리 할 수 ​​있지만 문제를 모두 무시하는 방식으로 라이브러리를 사용하는 것이 훨씬 간단 할 수 있습니다. 보라-메타 클래스의 마법.

다중 스레드 응용 프로그램에서 예상대로 제대로 작동하도록 전체 라이브러리를 원활하게 동적으로 다시 작성할 수 있다는 것은 일부 상황에서 매우 유용 할 수 있습니다. safetkinter의 모듈 않는다는 제공하는 메타 클래스의 도움으로 threadbox의 모듈 - 이벤트 및 큐 필요하지 않습니다.

한 가지 깔끔한 측면은 threadbox복제하는 클래스를 신경 쓰지 않는다는 것입니다. 필요한 경우 메타 클래스가 모든 기본 클래스를 터치 할 수있는 방법에 대한 예제를 제공합니다. 메타 클래스와 함께 제공되는 또 다른 이점은 상속 클래스에서도 실행된다는 것입니다. 스스로 작성하는 프로그램-왜 안 되죠?


4

메타 클래스의 유일한 합법적 인 사용 사례는 다른 개발자가 코드를 만지지 못하도록하는 것입니다. 코가 많은 개발자가 메타 클래스를 마스터하고 자신의 메타 클래스를 다루기 시작하면 다른 레벨 또는 2 단계를 던져 그들을 차단하십시오. 그래도 작동하지 않으면 type.__new__재귀 메타 클래스를 사용 하여 또는 아마도 일부 체계를 사용하십시오.

(볼에 혀를 쓰지만 이런 종류의 난독 화를 본 적이 있습니다. Django가 완벽한 예입니다)


7
장고에서 동기가 똑같 았는지 모르겠습니다.
알리 Afshar

3

메타 클래스는 프로그래밍을 대체하지 않습니다! 그것들은 일부 작업을 자동화하거나 더 우아하게 만들 수있는 트릭 일뿐입니다. 이에 대한 좋은 예는 Pygments 구문 강조 라이브러리입니다. RegexLexer사용자가 클래스의 정규 표현식으로 어휘 규칙 집합을 정의 할 수있는 라는 클래스가 있습니다. 메타 클래스는 정의를 유용한 파서로 바꾸는 데 사용됩니다.

그들은 소금과 같습니다. 너무 많이 사용하기 쉽습니다.


글쎄요, 제 생각에는 Pygments 사건은 불필요합니다. 왜 딕셔너리와 같은 평범한 컬렉션을 가지고 있지 않습니까? 왜 클래스가 이것을하도록 강요합니까?
Ali Afshar

4
클래스 좋은는 렉서의 아이디어를 캡슐화 해 등 guess_filename ()와 같은 다른 유용한 방법이 있기 때문에
벤자민 피터슨

3

메타 클래스를 사용하는 방법은 클래스에 몇 가지 속성을 제공하는 것이 었습니다. 예를 들어 :

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

메타 클래스가 NameClass를 가리 키도록 설정된 모든 클래스에 name 속성을 넣습니다 .


5
네, 작동합니다. 최소한 명시 적이며 코드에서 따를 수있는 수퍼 클래스도 사용할 수 있습니다. 관심이 없어서 이것을 무엇에 사용 했습니까?
Ali Afshar

2

이것은 사소한 사용이지만 ... 메타 클래스가 유용하다는 것을 발견 한 한 가지는 서브 클래스가 생성 될 때마다 함수를 호출하는 것입니다. 저는 이것을 __initsubclass__속성 을 찾는 메타 클래스로 코드화했습니다 . 서브 클래스가 생성 될 때마다 해당 메서드를 정의하는 모든 부모 클래스가 __initsubclass__(cls, subcls). 이를 통해 모든 하위 클래스를 전역 레지스트리에 등록하고, 정의 될 때마다 하위 클래스에 대한 불변 검사를 실행하고, 지연 바인딩 작업을 수행하는 등 부모 클래스를 생성 할 수 있습니다.이 모든 작업은 수동으로 함수를 호출 하거나 과 같은 사용자 지정 메타 클래스를 만들 필요가 없습니다. 이러한 개별 업무를 각각 수행합니다.

이 동작의 암묵적인 마법이 다소 바람직하지 않다는 것을 천천히 깨닫게되었습니다. 컨텍스트에서 클래스 정의를 보면 예상치 못한 일이기 때문입니다. 그래서 저는 그 외에 심각한 문제에 대해 그 솔루션을 사용하지 않았습니다. __super각 클래스 및 인스턴스에 대한 속성 초기화 .


1

저는 최근에 http://census.ire.org/data/bulkdata.html의 미국 인구 조사 데이터로 채워진 데이터베이스 테이블을 중심으로 SQLAlchemy 모델을 선언적으로 정의하는 데 메타 클래스를 사용해야했습니다 .

IRE는 인구 조사 데이터 테이블에 대한 데이터베이스 쉘 을 제공하며 , 인구 조사국의 p012015, p012016, p012017 등의 명명 규칙에 따라 정수 열을 생성합니다.

나는 model_instance.p012017a) 구문을 사용하여 이러한 열에 액세스 할 수 있고 , b) 내가하는 일에 대해 상당히 명시 적이어야하며 c) 모델에서 수십 개의 필드를 명시 적으로 정의 할 필요가 없으므로 SQLAlchemy를 하위 클래스로 지정 DeclarativeMeta하여 범위를 반복합니다. 열에 해당하는 모델 필드를 자동으로 생성합니다.

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

그런 다음이 메타 클래스를 모델 정의에 사용하고 모델의 자동 열거 필드에 액세스 할 수 있습니다.

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

여기에 설명 된 합법적 인 사용이있는 것 같습니다 . 메타 클래스로 Python Docstrings를 다시 작성합니다.


0

사용하기 쉽도록 바이너리 파서에 한 번 사용해야했습니다. 와이어에있는 필드의 속성으로 메시지 클래스를 정의합니다. 최종 와이어 형식을 구성하기 위해 선언 된 방식으로 주문해야했습니다. 순서가 지정된 네임 스페이스 dict를 사용하는 경우 메타 클래스로이를 수행 할 수 있습니다. 사실, 메타 클래스에 대한 예제에서 :

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

그러나 일반적으로 메타 클래스의 추가 복잡성이 정말로 필요한지 매우 신중하게 평가하십시오.


0

@Dan Gittik의 대답은 멋지다

끝에있는 예제는 많은 것을 명확하게 할 수 있으며, 파이썬 3으로 변경하고 몇 가지 설명을 제공합니다.

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)

#China is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class China(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#Taiwan is metaclass and it's __new__ method would be changed by MetaMetaclass(metaclass)
class Taiwan(MetaMetaclass, metaclass=MetaMetaclass):
    __metaclass__ = MetaMetaclass

#A is a normal class and it's __new__ method would be changed by China(metaclass)
class A(metaclass=China):
    __metaclass__ = China

#B is a normal class and it's __new__ method would be changed by Taiwan(metaclass)
class B(metaclass=Taiwan):
    __metaclass__ = Taiwan


print(A._label)  # Made in China
print(B._label)  # Made in Taiwan
  • 모든 것이 객체이므로 클래스는 객체입니다.
  • 클래스 객체는 메타 클래스에 의해 생성됩니다.
  • 유형에서 상속 된 모든 클래스는 메타 클래스입니다.
  • 메타 클래스는 클래스 생성을 제어 할 수 있습니다.
  • 메타 클래스는 메타 클래스 생성도 제어 할 수 있습니다 (영원히 반복 할 수 있음)
  • 이것은 메타 프로그래밍입니다 ... 런타임에 타입 시스템을 제어 할 수 있습니다.
  • 다시 말하지만, 모든 것이 객체이고, 이것은 균일 한 시스템입니다. 유형 생성을 입력하고 인스턴스 생성을 입력합니다.

0

또 다른 사용 사례는 클래스 수준 속성을 수정할 수 있고 현재 객체에만 영향을 미치기를 원할 때입니다. 실제로 이것은 메타 클래스와 클래스 인스턴스화의 단계를 "병합"하는 것을 의미하므로 고유 한 (고유 한) 종류의 클래스 인스턴스 만 처리하게됩니다.

나는 또한 ( 가독성다형성에 대한 우려를 위해 ) 클래스 수준에서만 수행 할 수 있는 (종종 변경되는) 인스턴스 수준 속성을 기반으로 계산에서 반환 된 값을 반환 하는 property s 를 동적으로 정의 해야 할 때도 수행해야했습니다. , 메타 클래스의 인스턴스 후와 클래스 인스턴스화 전에.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.