Author: | Beni Cherniavsky |
---|---|
Presented: | Python-IL, 2007-03-28 |
Agenda
instance.__class__ is type(instance) is Class.
A new-style instance is a puppet — and the Class pulls the strings.
Class.__class__ is type(Class) is type.
A new-style Class is basically just a puppet factory.
A property returns self on class access:
>>> import time >>> time_prop = property(lambda self: time.time()) >>> class Class(object): ... time = time_prop ... >>> Class.time <property object at 0x831c2d4>
but calls the getter function on instance access:
>>> instance = Class() >>> instance.time 1175029167.593503 >>> instance.time 1175029168.477463
A class statement simply instantiates type:
We can do it ourselves:
>>> Class = type('Class', (object,), dict(time=time_prop)) >>> Class().time 1175029698.4495051
This allows us to write a "class decorator":
def class_decorator(C): return type(C.__name__, C.__bases__, dict(C.__dict__, time=time_prop))
>>> class Class(object): ... "will magically tells the time" >>> Class = class_decorator(Class) >>> Class().time 1175029821.2147801
This gives some power, but Class.__class__ is still type.
If we instantiate a subclass of type, it becomes the metaclass:
>>> class TimedType(type): ... time = time_prop ... >>> TimedClass = TimedType('TimedClass', (object,), {'x': 1}) >>> TimedClass.__class__ <class '__main__.TimedType'> >>> TimedType.time <property object at 0x831c374> >>> TimedClass.time 1175030904.9948061 >>> TimedClass().time Traceback (most recent call last): TimedClass().time AttributeError: 'TimedClass' object has no attribute 'time'
The member __metaclass__ (if given) is called to construct the class:
class TimedClass(object): def __metaclass__(name, bases, members): return type(C.__name__, C.__bases__, dict(C.__dict__, time=time_prop))
But usually it will be a class:
- class TimedClass(object):
- __metaclass__ = TimedType
or directly:
- class TimedClass(object):
- class __metaclass__(type):
- time = time_prop
class Class(object): instantiates type because object.__class__ is type.
If TimedClass.__class__ is TimedType:
>>> class SubClass(TimedClass): ... pass ... >>> SubClass.__class__ <class '__main__.TimedType'> >>> SubClass.time 1175057391.6200049
Multiple inheritance works but with limitations but leads to the dark side...
Usually, the most convenient interface for a metaclass is to inherit from a base class using it.
Use in moderation!
"A day without objects is a day without job security."
class Enum(int): class __metaclass__(type): def __new__(metaclass, name, bases, members): Class = type.__new__(metaclass, name, bases, members) Class._names = {} for k, v in members.items(): if isinstance(v, (int, long)): setattr(Class, k, Class(v)) Class._names[v] = k return Class def __str__(self): return '%s.%s' % (self.__class__.__name__, self._names.get(self, int(self)))
>>> class FooBar(Enum): ... FOO = 0 ... BAR = 1 >>> print FooBar(0), FooBar.BAR FooBar.FOO FooBar.BAR
class Dict(object): class __metaclass__(type): # , UserDict.DictMixin __getitem__ = type.__getattribute__ __setitem__ = type.__setattr__
>>> class A(Dict): ... foo = 1 >>> class B(A): ... bar = 2 >>> B['bar'] 2 >>> B['foo'] 1 >>> A['foo'] = 0 >>> B['foo'] 0
The main use case is a locals_dict that remembers the order of assignments.
"You are very clever, young man, very clever. But it is turtles all the way down!"
—An old lady unconvinced by Bertrand Russell's astronomy lecture.
We will focus on Jython in this lecture.
"It's a turtle, for heaven's sake. It swims. That's what turtles are for."
—Small Gods, Terry Pratchett
instance_PyObject.getType() is Class_PyType.