18

I don't know if I have a good design here, but I have a class that is derived from unittest.TestCase and the way I have it set up, my code will dynamically inject a bunch of test_* methods into the class before invoking unittest to run through it. I use setattr for this. This has been working well, but now I have a situation in which I want to remove the methods I previously injected and inject a new set of methods. How can I remove all the methods in a class whose names match the pattern test_*?

tadasajon
  • 14,276
  • 29
  • 92
  • 144

4 Answers4

29

It's called delattr and is documented here.

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • @JasonSperske: Not if you have to delete an attribute whose name is computed programmatically. You can only use `del` to delete a name you can literally type in the source code. – BrenBarn May 22 '13 at 20:26
  • @JasonSperske -- the title says "opposite of setattr" implying that OP knows the method name as a string... – mgilson May 22 '13 at 20:26
  • The common usage for me is `delattr(type(obj), 'test_foo')` (for testing purposes only). A method cannot be unbound from a Class object, but only from the entire class(which affects all the objects) – Bryce Guinta Jun 18 '16 at 00:50
  • @BryceGuinta however, isn't it strange, that I can do st. like `foo.quack = lambda: "Quack"; print (foo.quack())` where *quack* was not defined so far for *foo*? – lef Oct 15 '17 at 22:22
  • @lef A method defined in a class is bound to the instance upon creation. You're setting a new attribute on an object which happens to be a function where the object happens to be an instance of a class. A bound method (which is defined in the class) implicitly passes the instance to the class's method (usually called self). Your function however does not do that – Bryce Guinta Oct 15 '17 at 22:59
  • But delattr delete attributes not methods... Trying to delete mathods will give you an attribute error – G M Jun 18 '21 at 14:49
  • @GM: Methods are attributes. – BrenBarn Jun 19 '21 at 18:57
6
>>> class Foo:
    def func(self):
        pass
...     
>>> dir(Foo)
['__doc__', '__module__', 'func']
>>> del Foo.func
>>> dir(Foo)
['__doc__', '__module__']
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
2

As an addition to the previous answers: If the method to be deleted is re-implemented, you'd have to delete the subclass and parent class methods, both:

class A:
    def method(self): 
        print("TestA")

class B(A):
    def method(self):
        A.method(self)
        print("TestB")

instance = B()
instance.method() # will call B.method()
del B.method
instance.method() # will now call A.method()
del A.method
instance.method() # will now raise AttributeError

(Or use delattr of @BrenBarn's answer)

DomTomCat
  • 8,189
  • 1
  • 49
  • 64
1

delattr() is what you want. Loop through vars() of the class and test for attribute names starting with "test_". E.g.,

@classmethod
def remove_test_methods(cls):
    for name in list(vars(cls)):
        if name.startswith("test_") and callable(getattr(cls, name)):
            delattr(cls, name)

I'd advise against using dir(), as this will show you names from your parent classes as well, so not all the names you get from dir() may defined on the class you're targeting.

kindall
  • 178,883
  • 35
  • 278
  • 309