two metaclass puzzles

2013.Apr.19

Before reading the following two puzzles, please look at the address bar in your browser and say the current domain name outloud at least three times.

Seriously, don’t use this code. (dot COM!)

What follows are two “puzzles” involving metaclasses and type construction in Python 2 and Python 3.

These are puzzles in the sense that they are dense, textured pieces of code with dubious applicability to real problems that should be pleasurable to tease apart and understand.

The first puzzle is in Python 2, and it involves function-based metaclasses at the global scope and the difference between old- and new-style classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from sys import version_info
assert version_info.major == 2, 'this puzzle is Python2 only'

def __metaclass__(name, bases, body):
    print '__metaclass__({}, {}, {})'.format(name, bases, body)
    assert name in ('Foo', 'Bar') # why aren't Baz and Quux affected?
    return type(name, bases, body)

class Foo: # old-style?
    pass

class Bar: # old-style?
    pass

class Baz(object): # new-style
    pass

class Quux: # new or old-style?
	def __metaclass__(name, bases, body):
		print 'Quux.__metaclass__({}, {}, {})'.format(name, bases, body)
		return type(name, bases, body)

print Foo, type(Foo), Foo(), type(Foo())
print Bar, type(Bar), Bar(), type(Bar())
print Baz, type(Baz), Baz(), type(Baz())
print Quux, type(Quux), Quux(), type(Quux())

assert type(Foo) == type(object) # Foo has become new-style: why?
assert type(Bar) == type(object) # Bar has become new-style: why?
assert type(Baz) == type(object) # Bar is new-style
assert type(Quux) == type(object) # Quux is new-style? why?

# how do we keep Foo and Bar as old-style classes?

Here’s the output, in case you’re afraid to run it on your own machine! (Who knows what it might do?)

# output
# notice that there are no AssertionErrors raised,
#   so all of the predicates in the above evaluate True
__metaclass__(Foo, (), {'__module__': '__main__'})

__metaclass__(Bar, (), {'__module__': '__main__'})

Quux.__metaclass__(Quux, (), {'__module__': '__main__', '__metaclass__': <function __metaclass__ at 0x7f785360aaa0>})

<class '__main__.Foo'> <type 'type'> <__main__.Foo object at 0x0> <class '__main__.Foo'>
<class '__main__.Bar'> <type 'type'> <__main__.Bar object at 0x0> <class '__main__.Bar'>
<class '__main__.Baz'> <type 'type'> <__main__.Baz object at 0x0> <class '__main__.Baz'>
<class '__main__.Quux'> <type 'type'> <__main__.Quux object at 0x0> <class '__main__.Quux'>

The second puzzle is in Python 3 and is considerably less useful.

I’ve noted some sample questions, but I think the puzzle in the below is mostly in determing what it does and how it works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from sys import version_info
assert version_info.major == 3, 'works only in Python3'

# what does this do?
from functools import partial
@partial(lambda y,x:x(y), __builtins__.__build_class__) # ?? 
def rebind(build_class):
	@partial(setattr, __builtins__, '__build_class__') # this is just gratuitous
	def __build_class__(name, code, *bases, **kwargs):
		if 'metaclass' in kwargs:
			pass # for reference: this is where a user-defined metaclass goes
		print('__build_class__({}, {}, {}, {})'.format(name, code, bases, kwargs))
		return build_class(name, code, *bases, **kwargs)

class Foo:
	pass

class Bar(Foo):
	pass

class Baz_meta(type(Foo)):
	def __new__(meta, name, bases, body, quux=None):
		print('Baz_meta.__new__({}, {}, {}, {}, {})'.format(meta, name, bases, body, quux))
		return type(Foo).__new__(meta, name, bases, body)

	# is the __init__ necessary?
	@staticmethod # is this legal? what about on the __new__?
	def __init__(name, bases, body, quux=None):
		print('Baz_meta.__init__({}, {}, {}, {})'.format(name, bases, body, quux))
		return type(Foo).__init__(type, name, bases, body)

class Baz(Foo, metaclass=Baz_meta, quux='fhtagn'):
	pass
	
print(Foo, type(Foo), Foo(), type(Foo()))
print(Bar, type(Bar), Bar(), type(Bar()))
print(Baz, type(Baz), Baz(), type(Baz()))

Here’s the output.

# output
__build_class__(<function Foo at 0x7f542ed35ea8>, Foo, (), {})

__build_class__(<function Bar at 0x7f542ed35ea8>, Bar, (<class '__main__.Foo'>,), {})

__build_class__(<function Baz_meta at 0x7f542ed35ea8>, Baz_meta, (<class 'type'>,), {})
__build_class__(<function Baz at 0x7f542ed35ea8>, Baz, (<class '__main__.Foo'>,), {'quux': 'fhtagn', 'metaclass': <class '__main__.Baz_meta'>})
Baz_meta.__new__(<class '__main__.Baz_meta'>, Baz, (<class '__main__.Foo'>,), {'__module__': '__main__'}, fhtagn)
Baz_meta.__init__(Baz, (<class '__main__.Foo'>,), {'__module__': '__main__'}, fhtagn)

<class '__main__.Foo'> <class 'type'> <__main__.Foo object at 0x0> <class '__main__.Foo'>
<class '__main__.Bar'> <class 'type'> <__main__.Bar object at 0x0> <class '__main__.Bar'>
<class '__main__.Baz'> <class '__main__.Baz_meta'> <__main__.Baz object at 0x0> <class '__main__.Baz'>

Next time: * the answers to these puzzles! * how can I use these for actual, real work?