Verbesserung der Code-"Qualität" durch statische Code-Analyse
Metaprogrammierung, praktisch
1. 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
2. Andi Albrecht – @andialbrecht
Erster Python-Kontakt vor etwa 10 Jahren als
Studentische Hilfskraft bei der DFG
Aktuell: Anwendungsentwickler für Webapplikationen bei
ProUnix in Bonn
Entwicklung und Pflege mittlerer und großer Systeme
OpenSource: Rietveld Code Review Tool,
python-sqlparse, CrunchyFrog, hgsvn, ...
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
3. Ü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
4. Hintergrund
Was ist Metaprogrammierung?
"Programmierung der Programmierung"
"Programmcode erzeugt|untersucht|… Programmcode"
"Programmcode wird zu Objekten von anderem
(Meta-)Programmcode"
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
5. Hintergrund
Was 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
6. Hintergrund
Was 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
7. Hintergrund
Metaklassen in Python
Wohl bekannteste Form der Metaprogrammierung in
Python
Möglichkeiten zur Modifikation von Klassen schon in 1995
als C-Extension möglich (Don Beaudry Hook)
Ab Python 1.5 auch ohne C-Extension∗
Echter Support für Metaklassen mit New-Style-Classes in
Python 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
8. Hintergrund
Metaklassen in Python
Python 1995: C-Extension Python 2.2
Python 1.5
class Foo(object):
class Tracing:
__metaclass__ = MyMetaclass
def __init__(self,
name, bases, namespace):
def __call__(self): Python 3
class Instance:
def __init__(self, klass):
def __getattr__(self, name):
class Foo(metaclass=MyMetaclass):
pass
class BoundMethod:
def __init__(self,
function, instance):
def __call__(self, *args):
Trace = Tracing(’Trace’, (), )
class MyTracedClass(Trace):
def method1(self, a):
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
9. Konzepte
Metaprogrammierung in Python
Types
Metaklassen
Dekoratoren
Code-Generierung zur Laufzeit (synthetische
Funktionen/Klassen)
...
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
10. Konzepte
type()
type(object) → liefert Typ des Objekts
type(name, bases, namespace) → erzeugt einen
neuen 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
11. Konzepte
types-Modul
import types
# Modul erzeugen
my_mod = types.ModuleType(’foo’, ’doc string’)
my_mod.foo = my_func
sys.modules[’heyho’] = my_mod
import heyho
heyho.foo()
# Methode erzeugen
def answer(self):
return 42
class Foo(object):
pass
foo1 = Foo()
foo2 = Foo()
foo1.answer = types.MethodType(answer, foo1)
foo1.answer() # gibt 42 zurück
foo2.answer() # wirft einen AttributeError
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
12. Konzepte
Metaklassen
Metaklassen modifizieren Klassen
Metaklassen können Meta-Funktionen haben
Beispiel für Einsatzzwecke:
neue Typen von Klassen
neues Verhalten von Klassen
Automatisierungen
Registrierung von Klassen
Frameworks, ORMs
Introspektion, QA
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
13. Konzepte
Metaklassen, 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)
@classmethod
def register(mcls, cls):
mcls.registry[cls.__name__] = cls
@classmethod
def get_all(cls):
return cls.registry
class Foo(metaclass=PluginRegistry): pass
class Bar(metaclass=PluginRegistry): pass
PluginRegistry.get_all() # –> gibt {’Foo’: <class Foo>, ’Bar’: <class Bar>} zurück
Foo.get_all() # –> gibt {’Foo’: <class Foo>, ’Bar’: <class Bar>} zurück
Foo().get_all() # –> AttributeError
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
14. Konzepte
Klassendekoratoren, Definition zur Laufzeit, ...
Klassendekoratoren Monkey-Patching
import amodule
@my_classdecorator
class Foo(object): def better_func():
pass print(’This is better!’)
return None
Code-Generierung zur
Laufzeit amodule.func = better_func
namespace = {} ...
exec(”””def answer():
return 42”””, namespace)
namespace[’answer’]() # 42
aber exec lieber vermeiden!
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
15. 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
16. Use-Cases
Enums
def enum(**enums):
return type(’Enum’, (), enums)
> Numbers = enum(ONE=1, TWO=2, THREE=’three’)
> Numbers.ONE
1
> Numbers.TWO
2
> 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.RED
0
Siehe http://stackoverflow.com/a/1695250/97167
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
17. Use-Cases
Django-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
18. Use-Cases
Django-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 Objekt
fügt neue DoesNotExist/MultipleObjectsReturned-Attribute hinzu
ergänzt einen Record-Manager als "objects"-Attribut
wertet die gegebenen Attribute (Namespace) aus und macht sie fit zur
Verwendung mit einer DB
...
Hintergrund Ein paar Konzepte Use-Cases Sinn oder Unsinn?
... ..... .....
Metaprogrammierung, praktisch / Andi Albrecht / PyCon DE 2012
19. Use-Cases
Test-Matrix
class ApiTestCase(unittest.TestCase, metaclass=TestMatrixMeta):
apicalls = (
(’/user/1’, ’GET’, {’name’: ’foo’}),
(’/user/1/locations’, ’GET’, [’Leipzig’,]),
)
python3 testmatrix.py -v
test__user_1_json (__main__.test__user_1_locations_xml) ... ok
test__user_1_locations_json (__main__.test__user_1_locations_xml) ... ok
test__user_1_locations_xml (__main__.test__user_1_locations_xml) ... FAIL
test__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_wrapper
self.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
20. Use-Cases
Test-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
@classmethod
def 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__ = name
return 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
21. Sinn oder Unsinn?
Für (fast) alles gibt es auch einen andere Lösung
Debugging wird teils schwieriger, Fehlverhalten nicht
immer transparent
Verwendung des Codes wird für End-Entwickler eleganter
Leichte Erweiterung der Sprache um spezielle
Paradigmen, 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
22. Danke!
E-Mail albrecht@prounix.de
albrecht.andi@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