= denominator def __mul__(self, other): return Fraction(self.numerator * other.numerator, self.denominator * other.denominator) def print_fraction(self): print '{}/{}'.format(self.numerator, self.denominator) Why did we inherit object? • Inheritance syntax is not just for inheritance! • Some inheritance is for metaclass propagation.
• A metaclass is just like any other callable except that you usually call the metaclass using a class statement. • Metaclasses let you make things that aren’t classes using the class statement. >>> class BooleanEnum(enum.Enum): ... true = 1 ... false = 0 ... >>> type(BooleanEnum) <class 'enum.EnumMeta'> >>> class BooleanClass(object): ... true = 1 ... false = 0 ... >>> type(BooleanClass) <class 'type'>
• A metaclass is just like any other callable except that you usually call the metaclass using a class statement. • Metaclasses let you make things that aren’t classes using the class statement. • Metaclasses can be specified explicitly class MyABC: __metaclass__ = abc.ABCMeta def some_method(self): 2.x
• A metaclass is just like any other callable except that you usually call the metaclass using a class statement. • Metaclasses let you make things that aren’t classes using the class statement. • Metaclasses can be specified explicitly __metaclass__ = abc.ABCMeta class MyABC: def some_method(self): 2.x
• A metaclass is just like any other callable except that you usually call the metaclass using a class statement. • Metaclasses let you make things that aren’t classes using the class statement. • Metaclasses can be specified explicitly class MyABC: __metaclass__ = abc.ABCMeta def some_method(self): 2.x class MyABC(metaclass = abc.ABCMeta): def some_method(self): data = [] 3.x
• A metaclass is just like any other callable except that you usually call the metaclass using a class statement. • Metaclasses let you make things that aren’t classes using the class statement. • Metaclasses can be specified explicitly, but are usually taken from the parent class. Examples: Django models, SQLAlchemy models, new-style classes, the stdlib enum module, (sometimes) zope.interface interfaces.
a class? • The most common metaclass code: ◦ In Python 2, there are two object systems: classic classes (class Foo:) and new-style classes (class Foo(object):) ◦ The difference is their metaclass (classobj vs. type) • For your own code, prefer class decorators to metaclasses Metaclasses - best practices and takeaways
instance is just calling a metaclass instance. ◦ half = Fraction(1, 2) • If it is a class (not an instance of another metaclass) ◦ __new__ is called ▪ __new__ returns an instance (or something else) ◦ if __new__ returns an instance of the class ▪ (e.g. Fraction.__new__ returns a Fraction object, not some other object), __init__ gets called ▪ __init__ receives an instance ▪ __new__ does not call __init__, the type calls __init__
metaclasses when debugging tricky code • When __new__ is involved, you have to remember when __init__ will be called and not • If you’re defining __new__, write a function instead of a class
is called ▪ First, the instance dictionary (or equivalent) is checked • This finds self.numerator ▪ Then, the class (and all the parent classes) are checked • This finds Fraction.print_fraction • The descriptor protocol is invoked ▪ Then, __getattr__ is called • For syntax that uses double-underscore attributes (like *→__mul__), the method is looked up directly on the class Attribute lookup
are looked up as a class attribute (or set or deleted) • Most common example: functions ◦ Descriptors are how functions become methods • Many language features are actually just descriptors: ◦ functions/methods ◦ properties ◦ classmethods ◦ staticmethods
as instances class C(object): @decorator_class def decorated_method(self): . . . • In Python 2, Classic Classes do not fully support the descriptor protocol when used as descriptors
__dict__ lookup → class __dict__ lookup → __getattr__ ◦ __setattr__, __delattr__ are a bit simpler • Descriptors cause things to happen at lookup ◦ Methods, properties, etc. ◦ Callable classes (such as decorators) are not automatically method-like descriptors--self is not passed ◦ Define new descriptors sparingly • No code is special enough for custom __getattribute__
have a __dict__, it is using __slots__ ◦ Also possible for objects defined in C • __slots__ is used to save memory, not speed • If the time has come to use __slots__, the time has probably come to write a C extension
an object . . . MAYBE • Python does not promise to call __del__ • __del__ especially might not be called if your instance ◦ is part of a reference cycle ◦ survives until the Python interpreter shuts down • If __del__ causes a new reference to be made to an object, it won’t be garbage collected (and might be called again) • __del__ writes to stderr if there is an exception All good things must come to an end
• Use __del__ only as a backup • The main ways to make sure something gets done: ◦ with open(path) as f: data = parse_file(f) ◦ try: x.do_work() finally: x.cleanup()
works, we see that things that look simple can be very complex • Python provides hooks for almost everything, which are useful for ◦ Understanding and using others’ code which uses them ◦ Machete debugging: getting temporary debugging code to run