Name conflict in class hierarchy

Michele Simionato michele.simionato at gmail.com
Tue May 23 12:27:54 CEST 2006


Jeffrey Barish wrote:
> I believe that the answer to my question is no, but I want to be sure that I
> understand this issue correctly:  Suppose that there are two classes
> defined as follows:
>
> class A(object):
>     def f1(self):
>         print 'In A.f1, calling func'
>         self.func()
>
>     def func(self):
>         print 'In A.func'
>
> class B(A):
>     def func(self):
>         print 'In B.func, calling A.f1'
>         A.f1(self)
>
> Class A was defined by someone else or it comes from a library, so I have no
> prior information about what is in it.  I subclass A to add some new
> functionality, and I call the new function "func".  The function B.func
> uses A.f1, but unbeknownst to me, A.f1 uses A.func.  Unfortunately, class B
> overrides func, so the call in A.f1 to self.func actually invokes B.func,
> resulting in this case in an infinite loop.  Is there a way from B to
> specify that A should use its own version of func and ignore the version in
> B?  I know that I could rename A.func to avoid the name clash, but since A
> is actually in a library, I will lose that change when I upgrade the
> library.  I could rename B.func, but there is already a bunch of code that
> calls it so I would have to update all the calls.  That seems like the
> correct solution, though.  The other possibility is to use composition
> rather than subclassing:
>
> class B:
>     def func(self):
>         print 'In B.func, calling A.f1'
>         a = A()
>         a.f1()
>
> but then B does not inherit other functions of A that I would like to use.
> It struck me that this must be a common problem in OOP, so I'm wondering
> whether there is a simple solution that I am missing.

I was bitten by the same issue the first time I used Zope :-(
This is my solution, a metaclass that warns you if you try to override
an attribute defined in some ancestor:

class Base(object):
    func = 1

class check_if_we_are_overriding_names(type):
    def __new__(mcl, name, bases, dic):
        for name, val in dic.iteritems():
            if name.endswith("__"): continue
            if sum(hasattr(base, name) for base in bases):
                print "AlreadyDefinedNameWarning:", name
        return super(check_if_we_are_overriding_names, mcl).__new__(
            mcl, name, bases, dic)

class MyClass(Base):
    __metaclass__ = check_if_we_are_overriding_names
    func = 2 # you will get a warning at this point


                Michele Simionato




More information about the Python-list mailing list