Europython 2013
Radomir Dopieralski
Allegro Group
The techniques presented in this talk are often quite advanced and not immediately obvious. They can be useful in implementing a library, a framework or a domain-specific language with a particular convenient API, but they can be very confusing when used haphazardly in everyday code.
Please code responsibly!
__len__
, __contains__
__getitem__
, __setitem__
, __delitem__
__iter__
len(thing) == int(thing.__len__())
needle in hay == bool(hay.__contains__(needle))
needle not in hay == not needle in hay
thing[key]
thing.__getitem__(key)
thing[key] = value
thing.__setitem__(key, value)
del thing[key]
thing.__delitem__(key)
thing[start:end:step]
def get_slice(thing, start=None, end=None, step=None):
if step is None and hasattr(thing.__class__, '__getslice__'):
# XXX deprecated
if start is None:
start = 0
elif start < 0:
start += len(thing)
if end is None:
end = sys.maxint
elif end < 0:
end += len(thing)
return thing.__getslice__(start, end)
return thing[slice(start, end, step)]
for item in sequence:
...
_iterator = sequence.__iter__()
while True:
try:
item = _iterator.next()
except StopIteration:
break
...
import UserDict
class MyDict(UserDict.DictMixin):
def __getitem__(self, key):
...
def __setitem__(self, key, value):
...
def __delitem__(self, key):
...
__dict__
, __slots__
__getattr__
, __setattr__
, __delattr__
__getattribute__
__get__
, __set__
, __delete__
thing.name
thing.__getattribute__(name)
thing.name = value
def set_attr(thing, name, value):
if hasattr(thing.__class__, '__setattr__'):
thing.__setattr__(name, value)
return
self.__dict__[name] = value
del thing.name
def del_attr(thing, name):
if hasattr(thing.__class__, '__delattr__'):
thing.__delattr__(name)
return
del self.__dict__[name]
class object:
def __getattribute__(self, name):
if name in self.__dict__:
return self.__dict__[name]
for cls in self.__class__.mro():
if name in cls.__dict__:
return cls.__dict__[name]
return self.__getattr__(name)
def __getattribute__(self, name):
if name in self.__dict__:
return self.__dict__[name]
for cls in self.__class__.mro():
if name in cls.__dict__:
attr = cls.__dict__[name]
if hasattr(attr.__class__, '__get__'):
return attr.__get__(self, cls)
return attr
return self.__getattr__(name)
def set_attr(thing, name, value):
if hasattr(thing.__class__, '__setattr__'):
thing.__setattr__(name, value)
return
for cls in self.__class__.mro():
if (name in cls.__dict__ and
hasattr(cls.__dict__[name].__class__, '__set__')):
cls.__dict__[name].__set__(self, name, value)
return
self.__dict__[name] = value
def del_attr(thing, name):
if hasattr(thing.__class__, '__delattr__'):
thing.__delattr__(name)
return
for cls in self.__class__.mro():
if (name in cls.__dict__ and
hasattr(cls.__dict__[name].__class__, '__delete__')):
cls.__dict__[name].__delete__(self, name)
return
del self.__dict__[name]
class Property(object):
def __init__(self, getter):
self.getter = getter
def __get__(self, instance, owner):
return self.getter(instance)
class MyClass(object):
@Property
def something(self):
return 42
MyClass().something == 42
__name__
, __module__
, __doc__
__call__
__defaults__
, __closure__
, __code__
, __globals__
__get__
thing(1, 2, foo=3) == thing.__call__(1, 2, foo=3)
import functools
def decorator(thing):
@functools.wraps(thing)
def wrapped(*args, **kwargs):
return thing(*args, **kwargs)
return wrapped
def function_factory():
variable = []
def function():
variable.append(1)
return variable
return function
function1 = function_factory()
function2 = function_factory()
print function1()
print function1()
print function2()
[1] [1, 1] [1]
class UnboundMethod(object):
def __init__(self, func):
self.__func__ = func
def __call__(self, *args, **kwargs):
return self.__func__(*args, **kwargs)
def __get__(self, instance, owner):
return BoundMethod(instance, self.__func__)
class BoundMethod(object):
def __init__(self, instance, func):
self.__self__, self.__func__ = instance, func
def __call__(self, *args, **kwargs):
return self.__func__(self.__self__, *args, **kwargs)
__new__
__init__
__del__
class type:
@classmethod
def __call__(cls, *args, **kwargs):
instance = cls.__dict__['__new__'](cls, *args, **kwargs)
if isinstance(instance, cls) and hasattr(cls, '__init__'):
instance.__init__(*args, **kwargs)
return instance
__metaclass__
__subclasses__
__instancecheck__
, __subclasscheck__
class MyClass(object):
foo = 3
MyClass = type('MyClass', (object,), {'foo': 3})
def classy_dict_meta(name, bases, d):
del d['__metaclass__']
return d
class ClassyDict():
__metaclass__ = classy_dict_meta
foo = 42
bar = 4
ClassyDict == {'foo': 42, 'bar': 4}
def isinstance(thing, classinfo):
if hasattr(thing.__class__, '__instancecheck__'):
return thing.__instancecheck__(thing, classinfo)
return issubclass(thing.__class__, classinfo)
__add__ | + |
__sub__ | - |
__mul__ | * |
__floordiv__ | // |
__div__ __truediv__ | / |
__mod__ | % |
__divmod__ | divmod(a, b) |
__pow__ | ** pow(a, power, modulo=None) |
__lshift__ | << |
__rshift__ | >> |
__and__ | & |
__xor__ | ^ |
__or__ | | |
__neg__ | - |
__pos__ | + |
__abs__ | abs(a) |
__invert__ | ~ |
class Expr(object):
def __init__(self, a, op=None, b=None):
self.a, self.op, self.b = a, op, b
def __repr__(self):
if self.op:
if self.b:
return '(%r %s %r)' % (self.a, self.op, self.b)
else:
return '(%s%r)' % (self.op, self.a)
return self.a
def __add__(self, other):
return Expr(self, '+', other)
def __sub__(self, other):
return Expr(self, '-', other)
def __mul__(self, other):
return Expr(self, '*', other)
def __div__(self, other):
return Expr(self, '/', other)
def __neg__(self, other):
return Expr(self, '-')
def __pos__(self, other):
return Expr(self, '+')
>>> a = Expr('a')
>>> b = Expr('b')
>>> (a + b) * b
((a + b) * b)
__hash__
__eq__
, __ne__
, __lt__
, __le__
, __gt__
, __ge__
@functools.total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
if a == b:
a.hash() == b.hash()
class object:
def __hash__(self):
return id(self)