| From | Sent On | Attachments |
|---|---|---|
| Fabio Corneti | Jun 11, 2002 4:35 am | |
| holger krekel | Jun 11, 2002 4:47 am | |
| Fabio Corneti | Jun 11, 2002 5:44 am | |
| Jerzy Karczmarczuk | Jun 11, 2002 5:54 am | |
| Emile van Sebille | Jun 11, 2002 6:01 am | |
| holger krekel | Jun 11, 2002 6:32 am | |
| Jerzy Karczmarczuk | Jun 11, 2002 6:42 am | |
| David LeBlanc | Jun 11, 2002 6:52 am | |
| David LeBlanc | Jun 11, 2002 7:19 am | |
| Emile van Sebille | Jun 11, 2002 7:21 am | |
| Eric Brunel | Jun 11, 2002 8:35 am | |
| Jeff Epler | Jun 11, 2002 9:20 am | |
| Richard Barrett | Jun 11, 2002 10:09 am |
| Subject: | Defining a method final | |
|---|---|---|
| From: | Jeff Epler (jep...@unpythonic.net) | |
| Date: | Jun 11, 2002 9:20:18 am | |
| List: | org.python.python-list | |
The following code uses a metaclass to attempt to enforce "final" methods. Warning: Use of metaclasses has exploded brains. Accessing directly through __dict__ or object.__setattr__ can bypass these protections.
def finalmethod(f): """Marks a method as a final method. Use like classmethod(), etc. Only works in subclasses of FinalMethods: class C(FinalMethods): def f(x): ... f = finalmethod(f)""" f.__final__ = 1 return f
class FinalMethodsMeta(type): """Metatype for FinalMethods""" def __init__(cls, name, bases, dict): super(FinalMethodsMeta, cls).__init__(name, bases, dict) for base in cls.__mro__: if base is cls: continue for attrname in base.__dict__: attr = base.__dict__[attrname] if hasattr(attr, "__final__") and attr.__final__: func = getattr(cls, attrname).im_func print attrname, attr, func if func is not attr: raise ValueError, \ "Subclasses of %s may not override the method %s" \ % (base.__name__, attrname)
def __setattr__(cls, name, attr):
oldval = getattr(cls, name, None)
if hasattr(oldval, "__final__") and oldval.__final__:
raise ValueError, "final methods may not be overridden by assignment to
class"
super(FinalMethodsMeta, cls).__setattr__(name, attr)
class FinalMethods: """Subclasses of FinalMethods may define a method to be a finalmethod(). This probably only works with instancemethods, not classmethods or staticmethods. A subclass may not override a finalmethod"""
__metaclass__ = FinalMethodsMeta
def __setattr__(self, name, attr):
oldval = getattr(self, name, None)
if hasattr(oldval, "__final__") and oldval.__final__:
raise ValueError, "final methods may not be overridden by assignment to
instance"
super(FinalMethods, self).__setattr__(name, attr)
def _test(): class c1(FinalMethods): def f(x): pass f = finalmethod(f)
def g(x): pass
class c2(FinalMethods): def g(x): pass g = finalmethod(g)
# Test that FinalMethods classes can be subclassed when they don't # interfere with each others' final methods class c3(c2, c1): pass assert c3.f.im_func is c1.f.im_func
# Test that a FinalMethods class can be subclassed when the added # methods do not interfere with the super's final methods class c4(c1): def g(x): pass
# Test that when a final method is concealed by a non-final method, # it blows up try: class c5(c1, c2): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" print c5.g, c1.g, c2.g raise RuntimeError
# Test that when a final method is overridden in the subclass, it # blows up try: class c5(c1): def f(x): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print c5.f, c1.f print "Did not catch expected error" raise RuntimeError
# Test that when a final method is overridden in the sub-subclass, # it blows up try: class c6(c3): def f(x): pass except ValueError, detail: print "Caught expected ValueError:", detail else: print c6.f, c3.f print "Did not catch expected error" raise RuntimeError
# Test that assigning to non-final attributes works (class) class c7(c1): pass c7.g = lambda: None
# Test that assigning to final attributes fails (class) try: class c7(c3): pass c7.f = None except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" raise RuntimeError
# Test that assigning to non-final attributes works (instance) i = c1() i.g = None
# Test that assigning to final attributes fails (instance) try: i = c1(); i.f = None except ValueError, detail: print "Caught expected ValueError:", detail else: print "Did not catch expected error" raise RuntimeError
print "seems ta work"
if __name__ == '__main__': _test()





