real-life problems real-life solutions they improve the rest of the code (you might not like them) antocuni (EuroPython 2012) Python White Magic July 3 2012 3 / 47
real-life problems real-life solutions they improve the rest of the code (you might not like them) antocuni (EuroPython 2012) Python White Magic July 3 2012 3 / 47
expected simplicity is an emergent property lots of internal rules and layers (ab)use them “black magic” antocuni (EuroPython 2012) Python White Magic July 3 2012 5 / 47
expected simplicity is an emergent property lots of internal rules and layers (ab)use them “black magic” antocuni (EuroPython 2012) Python White Magic July 3 2012 5 / 47
more readable more maintainable however: lots of cons some pros solid understanding of concepts is needed use with care antocuni (EuroPython 2012) Python White Magic July 3 2012 10 / 47
keep the same API how extend a module? subclassing is not enouogh module-level functions? antocuni (EuroPython 2012) Python White Magic July 3 2012 11 / 47
(and others) Pdb() refers to pdb.Pdb() our new class is never instantiated copy&paste is not a solution :-) antocuni (EuroPython 2012) Python White Magic July 3 2012 13 / 47
related) features per class e.g. visitor pattern two-axis split: by feature by class Split the class among more files? no way in Python in Ruby: “open classes” antocuni (EuroPython 2012) Python White Magic July 3 2012 18 / 47
related) features per class e.g. visitor pattern two-axis split: by feature by class Split the class among more files? no way in Python in Ruby: “open classes” antocuni (EuroPython 2012) Python White Magic July 3 2012 18 / 47
has a type (a class) A class is an object The class of a class: metaclass class statement --> metaclass instantiation antocuni (EuroPython 2012) Python White Magic July 3 2012 21 / 47
the class body type(bases[0]) (if any) global __metaclass__ Call it! subclass of type (usually) override __new__ tweak the parameters? antocuni (EuroPython 2012) Python White Magic July 3 2012 23 / 47
the class body type(bases[0]) (if any) global __metaclass__ Call it! subclass of type (usually) override __new__ tweak the parameters? antocuni (EuroPython 2012) Python White Magic July 3 2012 23 / 47
class __extend__(A): def bar(self): print ’bar’ magic.py class extendabletype(type): def __new__(cls, name, bases, dict): if name == ’__extend__’: for cls in bases: for key, value in dict.items(): if key == ’__module__’: continue setattr(cls, key, value) return None else: return type.__new__(cls, name, bases, dict) antocuni (EuroPython 2012) Python White Magic July 3 2012 24 / 47
class __extend__(A): def bar(self): print ’bar’ magic.py class extendabletype(type): def __new__(cls, name, bases, dict): if name == ’__extend__’: for cls in bases: for key, value in dict.items(): if key == ’__module__’: continue setattr(cls, key, value) return None else: return type.__new__(cls, name, bases, dict) antocuni (EuroPython 2012) Python White Magic July 3 2012 24 / 47
as actually needed Example: Camelot admin classes Entity subclass for the data model Foo.Admin for UI stuff mixing business and presentation logic :-( model.py class User(Entity): name = Field(Unicode) age = Field(Integer) class Admin(EntityAdmin): verbose_name = ’User’ field_list = [’name’, ’age’] antocuni (EuroPython 2012) Python White Magic July 3 2012 26 / 47
as actually needed Example: Camelot admin classes Entity subclass for the data model Foo.Admin for UI stuff mixing business and presentation logic :-( model.py class User(Entity): name = Field(Unicode) age = Field(Integer) class Admin(EntityAdmin): verbose_name = ’User’ field_list = [’name’, ’age’] antocuni (EuroPython 2012) Python White Magic July 3 2012 26 / 47
extendabletype name = Field(Unicode) age = Field(Integer) admin.py from model import User class __extend__(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Entity has its own metaclass metaclass conflict there is a simpler solution anyway antocuni (EuroPython 2012) Python White Magic July 3 2012 27 / 47
extendabletype name = Field(Unicode) age = Field(Integer) admin.py from model import User class __extend__(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Entity has its own metaclass metaclass conflict there is a simpler solution anyway antocuni (EuroPython 2012) Python White Magic July 3 2012 27 / 47
extendabletype name = Field(Unicode) age = Field(Integer) admin.py from model import User class __extend__(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Entity has its own metaclass metaclass conflict there is a simpler solution anyway antocuni (EuroPython 2012) Python White Magic July 3 2012 27 / 47
= Field(Integer) admin.py @attach_to(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Pros Simple is better than complex No metaclasses Much less magic Cons None :-) antocuni (EuroPython 2012) Python White Magic July 3 2012 31 / 47
= Field(Integer) admin.py @attach_to(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Pros Simple is better than complex No metaclasses Much less magic Cons None :-) antocuni (EuroPython 2012) Python White Magic July 3 2012 31 / 47
= Field(Integer) admin.py @attach_to(User): class Admin(EntityAdmin): verbose_name = ’User detail’ field_list = [’name’, ’age’] Pros Simple is better than complex No metaclasses Much less magic Cons None :-) antocuni (EuroPython 2012) Python White Magic July 3 2012 31 / 47
steps to do every time: start gdb start the program run until a certain point try your command antocuni (EuroPython 2012) Python White Magic July 3 2012 33 / 47
in various situations Cons Fragile Confusion++ (two different classes with the same name) Does not work in more complex cases antocuni (EuroPython 2012) Python White Magic July 3 2012 36 / 47
even worse Hard to test no isolation for tests persistent side effects (e.g. DB) Goal multiple, isolated, independent DBs one DB per test (or group of tests) antocuni (EuroPython 2012) Python White Magic July 3 2012 39 / 47
even worse Hard to test no isolation for tests persistent side effects (e.g. DB) Goal multiple, isolated, independent DBs one DB per test (or group of tests) antocuni (EuroPython 2012) Python White Magic July 3 2012 39 / 47
even worse Hard to test no isolation for tests persistent side effects (e.g. DB) Goal multiple, isolated, independent DBs one DB per test (or group of tests) antocuni (EuroPython 2012) Python White Magic July 3 2012 39 / 47
turn global state into local N indepentent __metadata__ & co. model.User Multiple copies of the same module Modules are singleton Cannot rely on import antocuni (EuroPython 2012) Python White Magic July 3 2012 41 / 47
turn global state into local N indepentent __metadata__ & co. model.User Multiple copies of the same module Modules are singleton Cannot rely on import antocuni (EuroPython 2012) Python White Magic July 3 2012 41 / 47
with care only if you know what you are doing lots of comments, please :-) Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. (Brian Kernighan) antocuni (EuroPython 2012) Python White Magic July 3 2012 46 / 47
with care only if you know what you are doing lots of comments, please :-) Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. (Brian Kernighan) antocuni (EuroPython 2012) Python White Magic July 3 2012 46 / 47