Metaprogrammierung, praktisch

22
Metaprogrammierung, praktisch Andi Albrecht PyCon DE 2012 30. Oktober 2012 . . . Hintergrund . . . . . Ein paar Konzepte . . . . . Use-Cases Sinn oder Unsinn? Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

description

Vortrag auf der PyConDE 2012, 30. Oktober 2012

Transcript of Metaprogrammierung, praktisch

Page 1: Metaprogrammierung, praktisch

Metaprogrammierung, praktisch

Andi Albrecht

PyCon DE 2012

30. Oktober 2012

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 2: Metaprogrammierung, praktisch

Andi Albrecht – @andialbrecht

Erster Python-Kontakt vor etwa 10 Jahren alsStudentische Hilfskraft bei der DFGAktuell: Anwendungsentwickler für Webapplikationen beiProUnix in BonnEntwicklung und Pflege mittlerer und großer SystemeOpenSource: Rietveld Code Review Tool,python-sqlparse, CrunchyFrog, hgsvn, ...

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 3: Metaprogrammierung, praktisch

Übersicht

Hintergrund

Ein paar Konzepte

Use-Cases

Sinn oder Unsinn?

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 4: Metaprogrammierung, praktisch

HintergrundWas ist Metaprogrammierung?

"Programmierung der Programmierung""Programmcode erzeugt|untersucht|… Programmcode""Programmcode wird zu Objekten von anderem(Meta-)Programmcode"

mysterious, strange, weird, mind-blowing, …

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 5: Metaprogrammierung, praktisch

HintergrundWas ist Metaprogrammierung?

"Programmierung der Programmierung""Programmcode erzeugt|untersucht|… Programmcode""Programmcode wird zu Objekten von anderem(Meta-)Programmcode"mysterious, strange, weird, mind-blowing, …

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 6: Metaprogrammierung, praktisch

HintergrundWas ist Metaprogrammierung?

"Programmierung der Programmierung""Programmcode erzeugt|untersucht|… Programmcode""Programmcode wird zu Objekten von anderem(Meta-)Programmcode"mysterious, strange, weird, mind-blowing, …

Meta-: jenseits, dahinter liegend, …

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 7: Metaprogrammierung, praktisch

HintergrundMetaklassen in Python

Wohl bekannteste Form der Metaprogrammierung inPythonMöglichkeiten zur Modifikation von Klassen schon in 1995als C-Extension möglich (Don Beaudry Hook)Ab Python 1.5 auch ohne C-Extension∗

Echter Support für Metaklassen mit New-Style-Classes inPython 2.2

* dazu: Essay von GvR "Metaclasses in Python 1.5 (a.k.a. The Killer Joke)"http://www.python.org/doc/essays/metaclasses/

http://python-history.blogspot.de/2009/04/metaclasses-and-extension-classes-aka.html

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 8: Metaprogrammierung, praktisch

HintergrundMetaklassen in Python

Python 1995: C-Extension

Python 1.5

class Tracing:def __init__(self,name, bases, namespace):

def __call__(self):

class Instance:def __init__(self, klass):def __getattr__(self, name):

class BoundMethod:def __init__(self,function, instance):

def __call__(self, *args):

Trace = Tracing(’Trace’, (), )

class MyTracedClass(Trace):def method1(self, a):

Python 2.2

class Foo(object):__metaclass__ = MyMetaclass

Python 3

class Foo(metaclass=MyMetaclass):pass

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 9: Metaprogrammierung, praktisch

KonzepteMetaprogrammierung in Python

TypesMetaklassenDekoratorenCode-Generierung zur Laufzeit (synthetischeFunktionen/Klassen)...

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 10: Metaprogrammierung, praktisch

Konzeptetype()

type(object) → liefert Typ des Objektstype(name, bases, namespace) → erzeugt einenneuen Typ

> def my_func(self):. return 42

> Question = type(’Question’, (), {’answer’: my_func})> Question().answer()< 42

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 11: Metaprogrammierung, praktisch

Konzeptetypes-Modul

import types

# Modul erzeugenmy_mod = types.ModuleType(’foo’, ’doc string’)my_mod.foo = my_funcsys.modules[’heyho’] = my_modimport heyhoheyho.foo()

# Methode erzeugendef answer(self):

return 42class Foo(object):

passfoo1 = Foo()foo2 = Foo()foo1.answer = types.MethodType(answer, foo1)foo1.answer() # gibt 42 zurückfoo2.answer() # wirft einen AttributeError

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 12: Metaprogrammierung, praktisch

KonzepteMetaklassen

Metaklassen modifizieren KlassenMetaklassen können Meta-Funktionen habenBeispiel für Einsatzzwecke:

neue Typen von Klassenneues Verhalten von KlassenAutomatisierungenRegistrierung von KlassenFrameworks, ORMsIntrospektion, QA

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 13: Metaprogrammierung, praktisch

KonzepteMetaklassen, Beispiel

class PluginRegistry(type):

registry = {}

def __new__(mcls, name, bases, classdict):return type.__new__(mcls, name, bases, classdict)

def __init__(cls, name, bases, namespace):PluginRegistry.register(cls)return super(PluginRegistery, cls).__init__(name, bases, namespace)

@classmethoddef register(mcls, cls):

mcls.registry[cls.__name__] = cls

@classmethoddef get_all(cls):

return cls.registry

class Foo(metaclass=PluginRegistry): passclass Bar(metaclass=PluginRegistry): pass

PluginRegistry.get_all() # –> gibt {’Foo’: <class Foo>, ’Bar’: <class Bar>} zurückFoo.get_all() # –> gibt {’Foo’: <class Foo>, ’Bar’: <class Bar>} zurückFoo().get_all() # –> AttributeError

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 14: Metaprogrammierung, praktisch

KonzepteKlassendekoratoren, Definition zur Laufzeit, ...

Klassendekoratoren

@my_classdecoratorclass Foo(object):pass

Code-Generierung zurLaufzeit

namespace = {}exec(”””def answer():return 42”””, namespace)

namespace[’answer’]() # 42

aber exec lieber vermeiden!

Monkey-Patching

import amodule

def better_func():print(’This is better!’)return None

amodule.func = better_func

...

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 15: Metaprogrammierung, praktisch

Use-Cases

Enum-Implementierung mit type (Erstellung neuer Typen)ORM-API mit Metaklassen (Framework / ORM)Aufbau einer Testmatrix (Automatisierung)

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 16: Metaprogrammierung, praktisch

Use-CasesEnums

def enum(**enums):return type(’Enum’, (), enums)

> Numbers = enum(ONE=1, TWO=2, THREE=’three’)> Numbers.ONE1> Numbers.TWO2> Numbers.THREE’three’

def enum(name, *sequential):enums = dict(x[::-1] for x in enumerate(sequential))return type(name, (), enums)

> Colors = enum(’Colors’, ’RED’, ’GREEN’, ’BLUE’)> Colors.RED0

Siehe http://stackoverflow.com/a/1695250/97167

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 17: Metaprogrammierung, praktisch

Use-CasesDjango-Models

from django.db import model

class Book(model.Model):author = models.CharField(max_length=200)title = models.CharField(max_length=200)year = models.IntegerField()

class Meta:verbose_name_plural = u’Books’

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 18: Metaprogrammierung, praktisch

Use-CasesDjango-Models

from django.db import model

class Book(model.Model):author = models.CharField(max_length=200)title = models.CharField(max_length=200)year = models.IntegerField()

class Meta:verbose_name_plural = u’Books’

...und was Django's Metaklasse macht (unter anderem):verschiebt "Meta" nach "_meta" und erstellt eigenes Objektfügt neue DoesNotExist/MultipleObjectsReturned-Attribute hinzuergänzt einen Record-Manager als "objects"-Attributwertet die gegebenen Attribute (Namespace) aus und macht sie fit zurVerwendung mit einer DB...

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 19: Metaprogrammierung, praktisch

Use-CasesTest-Matrix

class ApiTestCase(unittest.TestCase, metaclass=TestMatrixMeta):apicalls = (

(’/user/1’, ’GET’, {’name’: ’foo’}),(’/user/1/locations’, ’GET’, [’Leipzig’,]),

)

python3 testmatrix.py -vtest__user_1_json (__main__.test__user_1_locations_xml) ... oktest__user_1_locations_json (__main__.test__user_1_locations_xml) ... oktest__user_1_locations_xml (__main__.test__user_1_locations_xml) ... FAILtest__user_1_xml (__main__.test__user_1_locations_xml) ... ok

======================================================================FAIL: test__user_1_locations_xml (__main__.test__user_1_locations_xml)———————————————————————-Traceback (most recent call last):File ”testmatrix.py”, line 17, in test_wrapperself.assertFalse(u==’/user/1/locations’ and c == ’xml’)

AssertionError: True is not false

———————————————————————-Ran 4 tests in 0.001s

FAILED (failures=1)

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 20: Metaprogrammierung, praktisch

Use-CasesTest-Matrix

class TestMatrixMeta(type):

def __new__(mcls, name, bases, attrs):apicalls = attrs.pop(’apicalls’, [])for url, method, expected in apicalls:

for ctype in (’json’, ’xml’):name = ’test_%s_%s’ % (url.replace(’/’, ’_’), ctype)attrs[name] = mcls.build_test(name, url, method, expected, ctype)

new_cls = type.__new__(mcls, name, bases, attrs)return new_cls

@classmethoddef build_test(cls, name, url, method, expected, ctype):

def test_wrapper(self, u=url, m=method, e=expected, c=ctype):self.assertFalse(u==’/user/1/locations’ and c == ’xml’)

test_wrapper.__name__ = namereturn test_wrapper

class ApiTestCase(unittest.TestCase, metaclass=TestMatrixMeta):apicalls = (

(’/user/1’, ’GET’, {’name’: ’foo’}),(’/user/1/locations’, ’GET’, [’Leipzig’,]),

)

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 21: Metaprogrammierung, praktisch

Sinn oder Unsinn?

Für (fast) alles gibt es auch einen andere LösungDebugging wird teils schwieriger, Fehlverhalten nichtimmer transparent

Verwendung des Codes wird für End-Entwickler eleganterLeichte Erweiterung der Sprache um spezielleParadigmen, Funktionen, Verhalten, Qualitätsrichtlinien, …

→ Metaprogrammierung gezielt einsetzen→ Einsatz transparent machen

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012

Page 22: Metaprogrammierung, praktisch

Danke!

E-Mail [email protected]@gmail.com

Twitter @andialbrecht

Homepage http://andialbrecht.de

http://www.prounix.de/unternehmen/jobs/python/

prounix.de

. . .Hintergrund

. . . . .Ein paar Konzepte

. . . . .Use-Cases Sinn oder Unsinn?

Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012