]> granicus.if.org Git - python/commitdiff
metaclass(): add some more examples of metaclasses, including one
authorGuido van Rossum <guido@python.org>
Thu, 30 Aug 2001 20:52:40 +0000 (20:52 +0000)
committerGuido van Rossum <guido@python.org>
Thu, 30 Aug 2001 20:52:40 +0000 (20:52 +0000)
using cooperative multiple inheritance.

inherits(): add a test for subclassing the unicode type.

Lib/test/test_descr.py

index ec966a13d53f0fe919e1385352527f1010b758ba..f79af4d8c0537be95a1bb82ed18d6d9b126a5792 100644 (file)
@@ -392,6 +392,7 @@ def metaclass():
     verify(C.__spam__ == 1)
     c = C()
     verify(c.__spam__ == 1)
+
     class _instance(object):
         pass
     class M2(object):
@@ -420,6 +421,96 @@ def metaclass():
     c = C()
     verify(c.spam() == 42)
 
+    # More metaclass examples
+
+    class autosuper(type):
+        # Automatically add __super to the class
+        # This trick only works for dynamic classes
+        # so we force __dynamic__ = 1
+        def __new__(metaclass, name, bases, dict):
+            # XXX Should check that name isn't already a base class name
+            dict["__dynamic__"] = 1
+            cls = super(autosuper, metaclass).__new__(metaclass,
+                                                      name, bases, dict)
+            while name[:1] == "_":
+                name = name[1:]
+            while name[-1:] == "_":
+                name = name[:-1]
+            if name:
+                name = "_%s__super" % name
+            else:
+                name = "__super"
+            setattr(cls, name, super(cls))
+            return cls
+    class A:
+        __metaclass__ = autosuper
+        def meth(self):
+            return "A"
+    class B(A):
+        def meth(self):
+            return "B" + self.__super.meth()
+    class C(A):
+        def meth(self):
+            return "C" + self.__super.meth()
+    class D(C, B):
+        def meth(self):
+            return "D" + self.__super.meth()
+    verify(D().meth() == "DCBA")
+    class E(B, C):
+        def meth(self):
+            return "E" + self.__super.meth()
+    verify(E().meth() == "EBCA")
+
+    class autogetset(type):
+        # Automatically create getset attributes when methods
+        # named _get_x and/or _set_x are found
+        def __new__(metaclass, name, bases, dict):
+            hits = {}
+            for key, val in dict.iteritems():
+                if key.startswith("_get_"):
+                    key = key[5:]
+                    get, set = hits.get(key, (None, None))
+                    get = val
+                    hits[key] = get, set
+                elif key.startswith("_set_"):
+                    key = key[5:]
+                    get, set = hits.get(key, (None, None))
+                    set = val
+                    hits[key] = get, set
+            for key, (get, set) in hits.iteritems():
+                dict[key] = getset(get, set)
+            return super(autogetset, metaclass).__new__(metaclass,
+                                                        name, bases, dict)
+    class A:
+        __metaclass__ = autogetset
+        def _get_x(self):
+            return -self.__x
+        def _set_x(self, x):
+            self.__x = -x
+    a = A()
+    verify(not hasattr(a, "x"))
+    a.x = 12
+    verify(a.x == 12)
+    verify(a._A__x == -12)
+
+    class multimetaclass(autogetset, autosuper):
+        # Merge of multiple cooperating metaclasses
+        pass
+    class A:
+        __metaclass__ = multimetaclass
+        def _get_x(self):
+            return "A"
+    class B(A):
+        def _get_x(self):
+            return "B" + self.__super._get_x()
+    class C(A):
+        def _get_x(self):
+            return "C" + self.__super._get_x()
+    class D(C, B):
+        def _get_x(self):
+            return "D" + self.__super._get_x()
+    verify(D().x == "DCBA")
+
 def pymods():
     if verbose: print "Testing Python subclass of module..."
     log = []
@@ -1193,6 +1284,19 @@ def inherits():
         u = t.rev()
         verify(u == s)
 
+    class madunicode(unicode):
+        _rev = None
+        def rev(self):
+            if self._rev is not None:
+                return self._rev
+            L = list(self)
+            L.reverse()
+            self._rev = self.__class__(u"".join(L))
+            return self._rev
+    u = madunicode("ABCDEF")
+    verify(u.rev() == madunicode(u"FEDCBA"))
+    verify(u.rev().rev() == madunicode(u"ABCDEF"))
+
 def all():
     lists()
     dicts()