Магия метаклассов

  • View
    213

  • Download
    0

Embed Size (px)

Transcript

  • >>> foo = Foo() >>> foo.bar 'bar' >>> foo.baz() 'baz' >>> print foo

  • class Foo(object): bar = 'bar' def baz(self): return 'baz'

  • >>> print Foo >>> print hasattr(Foo, 'new_attribute') False >>> Foo.new_attribute = 'foo' >>> print hasattr(Foo, 'new_attribute') True >>> print Foo.new_attribute 'foo'

  • >>> def choose_class(name): ... if name == 'foo': ... class Foo(object): ... pass ... return Foo ... else: ... class Bar(object): ... pass ... return Bar ... >>> MyClass = choose_class('foo') >>> print MyClass >>> print MyClass()

  • ?

  • type

    >>> print type(1) >>> print type('1') >>> print type(Foo) >>> print type(Foo())

  • type

    type(, , # , )

  • typeclass MyShinyClass(object): pass

    >>> MyShinyClass = type('MyShinyClass', (), {}) >>> print MyShinyClass >>> print MyShinyClass()

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

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

    >>> print Foo >>> print Foo.bar True >>> f = Foo() >>> print f >>> print f.bar True

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

    >>> FooChild = type('FooChild', (Foo,), {})

    >>> print FooChild >>> print FooChild.bar True

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

  • MyClass = MetaClass() MyObject = MyClass()

  • type

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

  • >>> 42.__class__ >>> 'Hello world'.__class__ >>> def foo(): pass >>> foo.__class__ >>> class Bar(object): pass >>> b = Bar() >>> b.__class__

  • >>> 42.__class__.__class__ >>> 'Hello world'.__class__.__class__ >>> foo.__class__.__class__ >>> b.__class__.__class__

  • __metaclass__

    __metaclass__:

    1. 2. 3. ( ) 4. type

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

  • -

    def upper_attr(future_cls_name, future_cls_parents, future_cls_attr): attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

    return type(future_cls_name, future_cls_parents, attrs)

  • -

    class Foo(object): __metaclass__ = upper_attr bar = 'bar'

    foo = Foo() print hasattr(foo, 'bar') # False print hasattr(foo, 'BAR') # True print foo.BAR # bar

  • class UpperAttrMetaclass(type): def __new__(up_metacls, future_cls_name, future_cls_parents, future_cls_attr):

    attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

    return type(future_cls_name, future_cls_parents, attrs)

  • class UpperAttrMetaclass(type): def __new__(up_metacls, future_cls_name, future_cls_parents, future_cls_attr):

    attrs = {} for name, val in future_cls_attr.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

    return type.__new__(up_metacls, future_cls_name, future_cls_parents, attrs)

  • class UpperAttrMetaclass(type): def __new__(mcs, name, bases, dct):

    attrs = {} for name, val in dct.items(): if not name.startswith('__'): attrs[name.upper()] = val else: attrs[name] = val

    return super(UpperAttrMetaclass, mcs).\ __new__(mcs, name, bases, attrs)

  • :

    __new__(cls, *args, **kwargs)

    __init__(self, *args, **kwargs)

    __call__(self, *args, **kwargs)

  • :

    __new__(mms, name, bases, dct) -

    __init__(cls, name, bases, dct) -

    __call__(cls, *args, **kwargs)

  • __call__

    instance = constructor()

    instance = MyClass()

  • __call__class MetaSingleton(type): instance = None def __call__(cls, *args, **kwargs): if cls.instance is None: cls.instance = super(MetaSingleton,cls).\ __call__(*args, **kwargs) return cls.instance

    class Foo(object): __metaclass__ = MetaSingleton

    a = Foo() b = Foo() assert a is b

  • python3?

    1.

    2.

    3.

    4. -

  • class Meta(type): pass

    class MyClass(metaclass=Meta): pass

    ,

  • def __prepare__(mcs, cls, bases, **kwargs): return dict()

    dict-like , .

    class MyClass(metaclass=Meta, **kwargs):

  • exec(body, globals(), namespace)

    namespace , __prepare__. , dict.

  • def __new__(cls, name, bases, namespace, **kwargs): return type(name, bases, namespace)

    dict-like , __prepare__.

    , __prepare__.

  • ?

  • ORM

  • ORM

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

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

  • , ?

    Monkey-patching