Name conflict in class hierarchy

J

Jeffrey Barish

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.
 
L

Larry Bates

Jeffrey said:
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.

When you subclass an object it is your responsibility to check to make sure
you don't override existing methods in that class unintentionally. Of course
you may want to intentionally override the methods so as to replace them with
your own methods. You are correct, renaming A.func wouldn't be a good idea.
A good editor should make changing all the calls to B.func not be all that
hard.

You can easily get information about the methods by doing an import and dir().

Example:
['ConfigParser', 'DEFAULTSECT', 'DuplicateSectionError', 'Error',
'InterpolationDepthError', 'InterpolationError',
'InterpolationMissingOptionError', 'InterpolationSyntaxError',
'MAX_INTERPOLATION_DEPTH', 'MissingSectionHeaderError', 'NoOptionError',
'NoSectionError', 'ParsingError', 'RawConfigParser', 'SafeConfigParser',
'__all__', '__builtins__', '__doc__', '__file__', '__name__', 're']


Depending on the author, you may also be able to get extensive help
on all the methods with help().
Help on module ConfigParser:

NAME
ConfigParser - Configuration file parser.

FILE
c:\python24\lib\configparser.py

DESCRIPTION
A setup file consists of sections, lead by a "[section]" header,
and followed by "name: value" entries, with continuations and such in
the style of RFC 822.


-Larry Bates
 
R

Ralf Muschall

Jeffrey Barish wrote:

[overriding of base class member functions by subclass]
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.

In C++, one has virtual member functions (which behave like python's),
and their purpose is to be overriden (o.g. a print method, whose subclass
version will print more attributes than the base knows about).

Such functions usually are documented - otherwise their being virtual
would make no sense.

In Python, a function not intended to be overriden should be either
have a name starting with an underscore or be documented. So the person
who should change something is the author of the library, not you.

In your current situation, the following options seem to remain:

1. Use funny names ("foobar239847562384756" instead of "func")
and hope that the library update will not use them

2. Call dir(classname) at hacking time and avoid names listed
therein. This breaks if the library update may happen at the
user's site without you being able to change your code.

3. Call dir(classname) at runtime and raise an exception
if it contains a name used by you.

Ralf
 
S

Scott David Daniels

Jeffrey said:
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.

If you insist on this (I find silly) version, I'd suggest you want
to use "has-a" rather than "is-a" relationship between B and A

class A(object):
def f1(self):
print 'In A.f1, calling func'
self.func()

def func(self):
print 'In A.func'

class B(object):
def __init__(self, *args, **kwargs):
self._a = A(*args, **kwargs)

def func(self):
print 'In B.func, calling A.f1'
self._a.f1()


But, you could use this (I think ill-advised) technique if you
need a special-case work-around:

class Bb(A):
def __init__(self, *args, **kwargs):
super(Bb, self).__init__(*args, **kwargs)
self._recursed_func = 0

def f1(self):
self._recursed_func += 1
try:
return super(Bb, self).f1()
finally:
self._recursed_func -= 1

def func(self):
if self._recursed_func:
return super(Bb, self).func()
print 'In B.func, calling A.f1'
self.f1()


--Scott David Daniels
(e-mail address removed)
 
B

bruno at modulix

Ralf said:
Jeffrey Barish wrote:

[overriding of base class member functions by subclass]
(snip)

In Python, a function not intended to be overriden should be either
have a name starting with an underscore

actually with *two* underscores. The single-leading-underscore naming
scheme is the convention for 'protected' (read: 'implementation, not
API') attributes.
or be documented.

s/or/and/
 
S

Scott David Daniels

bruno said:
Ralf said:
Jeffrey Barish wrote:

[overriding of base class member functions by subclass] ....
In Python, a function not intended to be overriden should be either
have a name starting with an underscore

actually with *two* underscores. The single-leading-underscore naming
scheme is the convention for 'protected' (read: 'implementation, not
API') attributes.

The double-underscore is there to generate names that are unlikely to
accidentally conflict. Use for anything else simply annoys those of
us who may need to get access to those instances and methods.
 
R

Ralf Muschall

actually with *two* underscores. The single-leading-underscore naming
scheme is the convention for 'protected' (read: 'implementation, not
API') attributes.

That's what I meant. "A.func" looked like an internal implementation
detail.

Having the implementation details documented too would be nice, but
hardly enforceable in real life.

Ralf
 
M

Michele Simionato

Jeffrey said:
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
 
B

bruno at modulix

Scott said:
bruno said:
Ralf said:
Jeffrey Barish wrote:

[overriding of base class member functions by subclass] ....
In Python, a function not intended to be overriden should be either
have a name starting with an underscore


actually with *two* underscores. The single-leading-underscore naming
scheme is the convention for 'protected' (read: 'implementation, not
API') attributes.


The double-underscore is there to generate names that are unlikely to
accidentally conflict.

Yes, I do know this, and uses it when appropriate (that is : almost never)

I was just correcting Ralf's wrong statement about the single leading
underscore convention meant to denote "functions not intended to be
overriden".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,139
Latest member
JamaalCald
Top