Welcome, guest | Sign In | My Account | Store | Cart

A metaclass that allows the runtime creation of parent references, without needing to pass them on the command line. For example:

a.b -> (points to) b b.creator -> (points to) a

Python, 77 lines
 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
""" Metaclass for making linked attributes

	The LinkedAttribute class allows you to (at run time) have objects
	that have references back to the object that created them.

	Any class that has LinkedAttribute as it's metaclass will have a
	MakedLinkedAttribute() method to make linked attributes.

	To accomplish this hackery, we use the metaclass to hijack the __init__
	method, replacing it with our own.  The new __init__ method (defined as
	Meta__init__) sets up the _creator attribute (if it was passed in) and then
	calls the original __init__ function.  The original __init__ function gets
	renamed to <class name>_old__init__.  The reason for including the class name
	in the first part of the redefinition is so that sub classes that call
	super(...).__init__ won't enter an infinite loop.
	

	The restriction to using this metaclass is that the classes that you call 
	MakeLinkedAttribute on must also have LinkedAttribute as a metaclass.

	If you use this metaclass, make sure to give credit where credit is due :)
	(e.g. in a comment or docstring)

	There are other ways of solving the same problem (including passing the
	parent to __init__ explicitly).


	The software is provided AS IS, use this software at your own risk.  There
	is no warranty.  By using this software you agree to hold the author(s)
	harmless of any damage whether direct, incidental, consequently and 
	otherwise.  In no event shall the author(s) of this code be liable for any
	damages whatsoever incurred by this code.

	(c) 2006 Michael Murr [mmurr at code-x d0t net]

"""

class LinkedAttribute(type):
	def __new__(klass, klassName, klassBases, klassDict):
		# Method to make a new linked child
		def MakeLinkedAttribute(self, childClass, *args, **kwds):
			return(childClass(_creator=self, *args, **kwds))
		# MakeLinkedAttribute(self, childClass, *args, **kwds):

		# Run time hijacking of __init__ so we can make
		# a _creator BEFORE the original __init__ is called
		#
		# We use klassName + "_hijacked__init__" so sub classes
		# who call super(...).__init__ won't enter infinite
		# loops
		#
		# Note: All your __init__ are belong to us
		def Meta__init__(self, *args, **kwds):
			# If we don't have _creator keyword do nothing
			if kwds.has_key("_creator"):
				self._creator = kwds["_creator"]
				del kwds["_creator"]
			# kwds.has_key("_creator"):

			# If we don't have an old init, do nothing
			if hasattr(self, klassName + "_hijacked__init__"):
				attr = getattr(self, klassName + "_hijacked__init__")
				attr(*args, **kwds)
			# hasattr(self, klassName + "_hijacked__init__"):
		# Meta__init__(self, *args, **kwds):

		# If we have an init, we need to save it
		if klassDict.has_key("__init__"): klassDict[klassName + "_hijacked__init__"] = klassDict["__init__"]

		# Hijack (redirect) __init__ for our [evil] purposes :)
		klassDict["__init__"] = Meta__init__
		instance = super(LinkedAttribute, klass).__new__(klass, klassName, klassBases, klassDict)
		instance.MakeLinkedAttribute = MakeLinkedAttribute

		return(instance)
	# __new__(klass, klassName, klassBases, klassDict):
# LinkedAttribute(type):

This code can be used in situations where you have two objects, a and b, where they need to access each other. For instance:

from LinkedAttribute import LinkedAttribute

class a(object): def __init__(self): self.linked_b = self.MakeLinkedAttribute(b, "this is a linked b") self.plain_b = b("this is a plain b")

__metaclass__ = LinkedAttribute

class b(object): def __init__(self, text): self.text = text if hasattr(self, "_creator"): print "I have a creator!" else: print "I am a standalone!"

__metaclass__ = LinkedAttribute

if __name__ == "__main__": a = a();

Yields a link-diagram: a.linked_b -> b a.linked_b._creator -> a

a.plain_b -> b a.plain_b._creator doesn't exist

Traditionally the task of creating a reference to a parent/creator object is done by passing a reference to the parent/creator to __init__. This metaclass takes care of that behind the scenes, by use of the MakeLinkedAttribute method.

To handle the processing in the class that gets the creator/parent object, the __init__ function is redirected when the class object gets created. So when the class is created, the Meta__init__ function is called (instead of the original __init__) and handles the _creator parameter. The original __init__ is then called.

I chose this partly to learn more about metaclasses, and partly because it avoids having to modify the __init__ function to accept an additional parameter.

The known issue with this metaclass is that both the parent/creator and the attribute-to-be-linked classes must have LinkedAttribute as their metaclass.

An alternative to using a metaclass is to modify the __init__ method of each object to accept a reference to a parent/creator object.

Created by Michael Murr on Sun, 28 May 2006 (PSF)
Python recipes (4591)
Michael Murr's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks