메타 클래스의 (구체적인) 사용 사례는 무엇입니까?
메타 클래스를 사용하는 것을 좋아하고 정기적으로 솔루션으로 제공하는 친구가 있습니다.
저는 여러분이 메타 클래스를 거의 사용할 필요가 없다는 것을 알고 있습니다. 왜? 왜냐하면 당신이 클래스에 그런 일을하고 있다면 아마도 객체에 대해해야 할 것입니다. 그리고 작은 재 설계 / 리팩터링이 필요합니다.
메타 클래스를 사용할 수있게 됨으로써 많은 장소의 많은 사람들이 클래스를 일종의 2 등급 객체로 사용하게되는데, 이는 나에게 비참 해 보입니다. 프로그래밍이 메타 프로그래밍으로 대체됩니까? 클래스 데코레이터를 추가하면 안타깝게도 더 수용 할 수있게되었습니다.
그러니 제발, 파이썬에서 메타 클래스에 대한 유효한 (구체적인) 사용 사례를 알고 싶습니다. 또는 때때로 클래스를 변경하는 것이 객체를 변경하는 것보다 나은 이유에 대해 깨달음.
나는 시작할 것이다:
때로는 타사 라이브러리를 사용할 때 특정 방식으로 클래스를 변경할 수 있으면 유용합니다.
(이것은 내가 생각할 수있는 유일한 경우이며 구체적이지 않습니다)
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__
있지만 이것이 내가 가장 간단하게 찾은 해결책이었습니다.
나는 최근에 같은 질문을 받았고 몇 가지 대답을 내놓았습니다. 언급 한 몇 가지 사용 사례에 대해 자세히 설명하고 몇 가지 새로운 사례를 추가하고 싶었으므로이 스레드를 되살려도 괜찮기를 바랍니다.
내가 본 대부분의 메타 클래스는 다음 두 가지 중 하나를 수행합니다.
등록 (데이터 구조에 클래스 추가) :
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 :(
리팩토링 (클래스 속성 수정 또는 새 속성 추가) :
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이며 프로그래밍은 재미 있어야합니다. 항상 표준 프로그래밍 구조와 디자인 패턴을 사용하는 것은 지루하고 영감을주지 않으며 상상력을 방해합니다. 조금 살다! 여기 당신만을위한 메타 메타 클래스가 있습니다.
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
메타 클래스의 목적은 클래스 / 객체 구분을 메타 클래스 / 클래스로 대체하는 것이 아니라 어떤 방식 으로든 클래스 정의 (및 인스턴스)의 동작을 변경하는 것입니다. 실제로는 기본값보다 특정 도메인에 더 유용 할 수있는 방식으로 클래스 문의 동작을 변경하는 것입니다. 내가 그것들을 사용한 것은 다음과 같습니다.
일반적으로 핸들러를 등록하기 위해 서브 클래스를 추적합니다. 이것은 플러그인 스타일 설정을 사용할 때 편리합니다. 여기서 몇 가지 클래스 속성을 서브 클래 싱하고 설정하기 만하면 특정 항목에 대한 핸들러를 등록 할 수 있습니다. 예. 다양한 음악 형식에 대한 핸들러를 작성한다고 가정 해 보겠습니다. 여기서 각 클래스는 해당 유형에 적합한 메서드 (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 열 정의)를 해석하는 것입니다.
Tim Peter의 고전적인 인용문부터 시작하겠습니다.
메타 클래스는 99 %의 사용자가 걱정해야하는 것보다 더 깊은 마법입니다. 당신이 그것들이 필요한지 궁금하다면, 당신은 그럴 필요가 없습니다 (실제로 그것들을 필요로하는 사람들은 그것들이 필요하다는 것을 확실히 알고 있고 이유에 대한 설명이 필요하지 않습니다). 팀 피터스 (CLP post 2002-12-22)
그래도 메타 클래스의 진정한 사용을 (주기적으로) 실행했습니다. 떠오르는 것은 모든 모델이 models.Model에서 상속하는 Django에 있습니다. model.Model은 DB 모델을 Django의 ORM 장점으로 감싸기 위해 심각한 마술을합니다. 그 마법은 메타 클래스를 통해 발생합니다. 모든 종류의 예외 클래스, 관리자 클래스 등을 생성합니다.
이야기의 시작은 django / db / models / base.py, ModelBase () 클래스를 참조하십시오.
메타 클래스는 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,])
]
완벽하지는 않지만 이미 마법이 거의없고 메타 클래스가 필요 없으며 균일 성이 향상되었습니다.
메타 클래스 사용의 합리적인 패턴은 동일한 클래스가 인스턴스화 될 때마다 반복적으로 수행되는 것이 아니라 클래스가 정의 될 때 한 번 수행하는 것입니다.
여러 클래스가 동일한 특수 동작을 공유 할 때 __metaclass__=X
반복은 특수 목적 코드를 반복하거나 임시 공유 슈퍼 클래스를 도입하는 것보다 분명히 낫습니다.
그러나 심지어 단 하나의 특별한 클래스와 예측 가능한 확장과 __new__
및 __init__
메타 클래스의 특수 목적 코드와 정상 혼용보다 클래스 변수 또는 다른 글로벌 데이터를 초기화하는 청소기 방법 def
및 class
클래스 정의의 본문에 문을.
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를 생성했기 때문에 런타임에 클래스 정의를 알지 못합니다. 유형을 동적으로 생성 할 수 있다는 것은 매우 유용했습니다.
어제 똑같은 생각을했는데 완전히 동의합니다. 코드를 더 선언적으로 만들려는 시도로 인해 발생하는 코드의 복잡성은 일반적으로 코드베이스를 유지 관리하기 어렵게 만들고 읽기 어렵게 만들고 제 생각에는 비단뱀 적입니다. 또한 일반적으로 많은 copy.copy () ing (상속을 유지하고 클래스에서 인스턴스로 복사)이 필요하며, (항상 메타 클래스에서 위로 찾고) 무슨 일이 일어나고 있는지 확인하기 위해 여러 곳에서 조사해야 함을 의미합니다. 파이썬 곡물도. 나는 formencode와 sqlalchemy 코드를 통해 그러한 선언적 스타일이 그만한 가치가 있는지 그리고 분명히 그렇지 않은지 확인했습니다. 이러한 스타일은 설명자 (예 : 속성 및 메서드)와 변경 불가능한 데이터에 맡겨야합니다. Ruby는 이러한 선언적 스타일을 더 잘 지원하며 핵심 파이썬 언어가 그 경로를 따르지 않는다는 점에 기쁩니다.
디버깅 용도를 확인하고 모든 기본 클래스에 메타 클래스를 추가하여 더 풍부한 정보를 얻을 수 있습니다. 나는 또한 일부 상용구 코드를 제거하기 위해 (매우) 큰 프로젝트에서만 사용되는 것을 봅니다 (그러나 명확성을 잃는 경우). 예 를 들어 sqlalchemy는 클래스 정의의 속성 값 (예 : 장난감 예제)을 기반으로 모든 하위 클래스에 특정 사용자 지정 메서드를 추가하기 위해 다른 곳에서 사용합니다.
class test(baseclass_with_metaclass):
method_maker_value = "hello"
"hello"(문자열 끝에 "hello"를 추가 한 메서드)를 기반으로하는 특수 속성을 사용하여 해당 클래스에서 메서드를 생성하는 메타 클래스를 가질 수 있습니다. 만드는 모든 하위 클래스에 메서드를 작성할 필요가 없는지 확인하는 것이 유지 관리에 좋을 수 있습니다. 대신 정의해야하는 것은 method_maker_value뿐입니다.
이것에 대한 필요성은 매우 드물며 충분한 코드베이스가 없으면 고려할 가치가없는 약간의 타이핑 만 줄입니다.
수정하려는 클래스의 상속 또는 집계를 사용하여 원하는 작업을 수행하는 클래스를 항상 구성 할 수 있으므로 메타 클래스를 절대로 사용할 필요 가 없습니다 .
즉, 스몰 토크와 루비에서는 기존 클래스를 수정하는 것이 매우 편리 할 수 있지만, 파이썬은 직접 그렇게하는 것을 좋아하지 않습니다.
도움이 될 수있는 Python의 메타 클래 싱에 대한 훌륭한 DeveloperWorks 기사 가 있습니다. 위키 백과의 문서 도 꽤 좋다.
메타 클래스의 유일한 합법적 인 사용 사례는 다른 개발자가 코드를 건드리지 못하도록하는 것입니다. 코가 많은 개발자가 메타 클래스를 마스터하고 여러분의 메타 클래스를 다루기 시작하면 다른 레벨 또는 두 단계를 던져 그들을 차단하십시오. 그래도 작동하지 않으면 type.__new__
재귀 메타 클래스를 사용 하여 또는 아마도 일부 체계를 사용하십시오.
(뺨에 혀를 쓰지만 이런 종류의 난독 화를 본 적이 있습니다. Django가 완벽한 예입니다)
메타 클래스는 프로그래밍을 대체하지 않습니다! 그것들은 일부 작업을 자동화하거나 더 우아하게 만들 수있는 트릭 일뿐입니다. 이에 대한 좋은 예는 Pygments 구문 강조 라이브러리입니다. RegexLexer
사용자가 클래스의 정규 표현식으로 어휘 규칙 집합을 정의 할 수있는 라는 클래스가 있습니다. 메타 클래스는 정의를 유용한 파서로 바꾸는 데 사용됩니다.
그들은 소금과 같습니다. 너무 많이 사용하기 쉽습니다.
메타 클래스를 사용하는 방법은 클래스에 몇 가지 속성을 제공하는 것이 었습니다. 예를 들어 :
class NameClass(type):
def __init__(cls, *args, **kwargs):
type.__init__(cls, *args, **kwargs)
cls.name = cls.__name__
메타 클래스가 NameClass를 가리 키도록 설정된 모든 클래스에 name 속성을 넣습니다 .
일부 GUI 라이브러리는 여러 스레드가 상호 작용하려고 할 때 문제가 있습니다. tkinter
하나의 예입니다. 이벤트와 큐를 사용하여 문제를 명시 적으로 처리 할 수 있지만 문제를 모두 무시하는 방식으로 라이브러리를 사용하는 것이 훨씬 간단 할 수 있습니다. 보라-메타 클래스의 마법.
다중 스레드 응용 프로그램에서 예상대로 제대로 작동하도록 전체 라이브러리를 원활하게 동적으로 다시 작성할 수 있다는 것은 일부 상황에서 매우 유용 할 수 있습니다. safetkinter의 모듈 않는다는 제공하는 메타 클래스의 도움으로 threadbox의 모듈 - 이벤트 및 큐 필요하지 않습니다.
한 가지 깔끔한 측면은 threadbox
복제하는 클래스를 신경 쓰지 않는다는 것입니다. 필요한 경우 메타 클래스가 모든 기본 클래스를 터치 할 수있는 방법에 대한 예제를 제공합니다. 메타 클래스와 함께 제공되는 또 다른 이점은 상속 클래스에서도 실행된다는 것입니다. 스스로 작성하는 프로그램-왜 안 되죠?
이것은 사소한 사용이지만 ... 메타 클래스가 유용하다는 것을 발견 한 한 가지는 서브 클래스가 생성 될 때마다 함수를 호출하는 것입니다. 나는 이것을 __initsubclass__
속성 을 찾는 메타 클래스로 코드화했다 : 서브 클래스가 생성 될 때마다, 그 메서드를 정의하는 모든 부모 클래스는 __initsubclass__(cls, subcls)
. 이를 통해 모든 하위 클래스를 전역 레지스트리에 등록하고, 정의 될 때마다 하위 클래스에 대한 불변 검사를 실행하고, 지연 바인딩 작업을 수행하는 등 부모 클래스를 만들 수 있습니다.이 모든 작업은 수동으로 함수를 호출 하거나 사용자 지정 메타 클래스를 만들 필요없이 이러한 개별 업무를 각각 수행합니다.
이 동작의 암묵적인 마법이 다소 바람직하지 않다는 것을 천천히 깨닫게되었습니다. 컨텍스트에서 클래스 정의를 보면 예상치 못한 일이기 때문입니다. 그래서 저는 그 외에 심각한 문제에 대해 그 솔루션을 사용하지 않았습니다. __super
각 클래스 및 인스턴스에 대한 속성 초기화 .
최근에 http://census.ire.org/data/bulkdata.html의 미국 인구 조사 데이터로 채워진 데이터베이스 테이블을 중심으로 SQLAlchemy 모델을 선언적으로 정의하는 데 메타 클래스를 사용해야했습니다 .
IRE는 인구 조사 데이터 테이블에 대한 데이터베이스 쉘 을 제공하며 , 인구 조사국의 p012015, p012016, p012017 등의 명명 규칙에 따라 정수 열을 생성합니다.
나는 model_instance.p012017
a) 구문을 사용하여 이러한 열에 액세스 할 수 있고 , 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
...
여기에 설명 된 합법적 인 사용이있는 것 같습니다 . 메타 클래스로 Python Docstrings 다시 작성.
사용하기 쉽도록 이진 파서에 한 번 사용해야했습니다. 와이어에있는 필드의 속성으로 메시지 클래스를 정의합니다. 최종 와이어 형식을 구성하기 위해 선언 된 방식으로 주문해야했습니다. 순서가 지정된 네임 스페이스 사전을 사용하는 경우 메타 클래스로이를 수행 할 수 있습니다. 사실, 메타 클래스에 대한 예제에서 :
https://docs.python.org/3/reference/datamodel.html#metaclass-example
그러나 일반적으로 : 메타 클래스의 추가 된 복잡성이 정말로 필요한지 매우 신중하게 평가하십시오.
@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
- 모든 것이 객체이므로 클래스는 객체입니다.
- 클래스 객체는 메타 클래스에 의해 생성됩니다.
- 유형에서 상속 된 모든 클래스는 메타 클래스입니다.
- 메타 클래스가 클래스 생성을 제어 할 수 있음
- 메타 클래스는 메타 클래스 생성도 제어 할 수 있습니다 (영원히 반복 할 수 있음)
- 이것은 메타 프로그래밍입니다 ... 런타임에 타입 시스템을 제어 할 수 있습니다.
- 다시 말하지만, 모든 것이 객체입니다. 이것은 균일 한 시스템입니다. 유형 생성을 입력하고 인스턴스 생성을 입력합니다.
또 다른 사용 사례는 클래스 수준 속성을 수정할 수 있고 현재 개체에만 영향을 미치기를 원할 때입니다. 실제로 이것은 메타 클래스와 클래스 인스턴스화 단계를 "병합"하는 것을 의미하므로 고유 한 (고유 한) 종류의 클래스 인스턴스 만 처리하게됩니다.
나는 또한 ( 가독성 과 다형성에 대한 우려를 위해 ) 우리가 클래스 수준에서만 수행 할 수 있는 (종종 변경되는) 인스턴스 수준 속성을 기반으로 계산에서 결과를 반환 할 수있는 값 을 동적으로 정의하고 싶을 때 이를 수행해야했습니다. , 즉 메타 클래스 인스턴스화 후 및 클래스 인스턴스화 전.property
참고 URL : https://stackoverflow.com/questions/392160/what-are-some-concrete-use-cases-for-metaclasses
'code' 카테고리의 다른 글
msbuild를 사용하여 솔루션의 프로젝트 파일 지정 (0) | 2020.08.16 |
---|---|
튜플이 목록보다 메모리 공간을 덜 차지하는 이유는 무엇입니까? (0) | 2020.08.16 |
EL에서 상수를 참조하는 방법은 무엇입니까? (0) | 2020.08.16 |
변경 유형별로 git diff 필터링 (0) | 2020.08.16 |
javax.annotation. *에서 @Nullable을 찾을 수 없습니다. (0) | 2020.08.16 |