Inheritance and forward references (prototypes)

  • Thread starter Lorenzo Di Gregorio
  • Start date
L

Lorenzo Di Gregorio

Hi,

I'm wondering what would be the preferred way to solve the following
forward reference problem:

---------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

# here I would have a prototype of class A which is the same as class
BaseA

class B(object):
def __init__(self):
self.obj = A()
return

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
---------------------------------------

I can figure out some ways to fix this but none seems satisfying.
Either they are too specific or too cumbersome.
A runtime redefinition of class A does not seem to work either.
What would be the most "pythonesque" solution other than sorting out
the class order?

Best Regards,
Lorenzo
 
S

Steven D'Aprano

Lorenzo said:
Hi,

I'm wondering what would be the preferred way to solve the following
forward reference problem:

You don't actually explain what is the problem. Fortunately, I'm good at
guessing, and I think I can guess what your problem is (see below):

---------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

# here I would have a prototype of class A which is the same as class
BaseA

class B(object):
def __init__(self):
self.obj = A()
return

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
---------------------------------------

Class A only gets defined if you run the module as a script. What you need
is to unconditionally define class A, outside of the if __name__ block:


class A(BaseA):
pass

# A.__base__ = DebugA ## Uncomment this line for debugging.
 
D

Dave Angel

Lorenzo said:
Hi,

I'm wondering what would be the preferred way to solve the following
forward reference problem:

---------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

# here I would have a prototype of class A which is the same as class
BaseA

class B(object):
def __init__(self):
self.obj = A()
return

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
---------------------------------------

I can figure out some ways to fix this but none seems satisfying.
Either they are too specific or too cumbersome.
A runtime redefinition of class A does not seem to work either.
What would be the most "pythonesque" solution other than sorting out
the class order?

Best Regards,
Lorenzo
You haven't shown us any problem. class B works fine with a forward
reference to A. Now if you were trying to subclass A before defining
it, that'd be a problem. Or if you were trying to make an instance of B
before defining A.

Better put some code together with enough meat to actually show a
symptom. And tell us what sys.version says. I'm testing with 2.6.2
(r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)], running
on Win XP.
 
P

Piet van Oostrum

SD> You don't actually explain what is the problem. Fortunately, I'm good at
SD> guessing, and I think I can guess what your problem is (see below):
SD> Class A only gets defined if you run the module as a script. What you need
SD> is to unconditionally define class A, outside of the if __name__ block:

SD> class A(BaseA):
SD> pass
SD> # A.__base__ = DebugA ## Uncomment this line for debugging.
TypeError: readonly attribute

Make that: A.__bases__ = DebugA,
 
L

Lorenzo Di Gregorio

Lorenzo said:
I'm wondering what would be the preferred way to solve the following
forward reference problem:
class DebugA(BaseA):
    def __init__(self):
        return
# here I would have a prototype of class A which is the same as class
BaseA
class B(object):
    def __init__(self):
        self.obj = A()
        return
if __name__ == "__main__":
#    class A(BaseA): # Uncomment this for using BaseA objects
#       pass
    class A(DebugA): # Uncomment this for using DebugA objects
        pass
---------------------------------------
I can figure out some ways to fix this but none seems satisfying.
Either they are too specific or too cumbersome.
A runtime redefinition of class A does not seem to work either.
What would be the most "pythonesque" solution other than sorting out
the class order?
Best Regards,
Lorenzo

You haven't shown us any problem.  class B works fine with a forward
reference to A.  Now if you were trying to subclass A before defining
it, that'd be a problem.  Or if you were trying to make an instance of B
before defining A.

Better put some code together with enough meat to actually show a
symptom.  And tell us what sys.version says.  I'm testing with   2.6.2
(r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)], running
on Win XP.- Hide quoted text -

- Show quoted text -

Thank you for your help: I'm working on a rather large source, but I
think I have isolated the problem now.
This listing generates an error:

-----------------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

class B(object):
def __init__(self,test=A()):
self.obj = A()
return

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
-----------------------------------------------

The error happens because Python apparently evaluates the named
arguments before running the script.
I think I have read something about this some (long) time ago but I
can't find it anymore.

Suggestions?

BTW, my Python version is 2.6.1 (with latest PyDev).

Thx!
Lorenzo
 
R

Rhodri James

Thank you for your help: I'm working on a rather large source, but I
think I have isolated the problem now.
This listing generates an error:

-----------------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

class B(object):
def __init__(self,test=A()):
self.obj = A()
return

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
-----------------------------------------------

The error happens because Python apparently evaluates the named
arguments before running the script.
I think I have read something about this some (long) time ago but I
can't find it anymore.

I could have sworn this was in the FAQ, but apparently not. You're
right, Python evaluates the default arguments when it executes the
`def` instruction. (Yes, `def` is an executable instruction. It's
got to create the function/method object after all!)

The usual fix is not to make the default an instance of A (which
will have all sorts of other side effects as well, since the same
instance of A will be shared by all the Bs that don't supply a
`test` parameter), but to use `None` as a marker.

class B(object):
def __init__(self, test=None):
if test is None:
test = A()
self.obj = A()

and so on.
 
D

Dennis Lee Bieber

The error happens because Python apparently evaluates the named
arguments before running the script.
I think I have read something about this some (long) time ago but I
can't find it anymore.

File "E:\UserData\Dennis Lee Bieber\My Documents\Python
Progs\Script1.py", line 9, in <module>
class B(object):
File "E:\UserData\Dennis Lee Bieber\My Documents\Python
Progs\Script1.py", line 10, in B
def __init__(self,test=A()):
NameError: name 'A' is not defined

Which is to be expected... "class" and "def" are EXECUTABLE
statements, not DECLARATIONS. Execution consists of "compiling" code
blocks, which includes executing default arguments (these are computed
ONCE, at the time the "def" is executed), finally binding the compiled
code block to a name...

Compare to:
-=-=-=-=-=-=-
class BaseA(object):
def __init__(self):
pass

class DebugA(BaseA):
def __init__(self):
pass

class B(object):
def __init__(self,test=None):
if not test: test = A()
#do something with test
self.obj = A()

if __name__ == "__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
print "done"
-=-=-=-=-=-=-

--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
D

Dave Angel

Lorenzo said:
Lorenzo said:
Hi,

I'm wondering what would be the preferred way to solve the following
forward reference problem:

---------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

# here I would have a prototype of class A which is the same as class
BaseA

class B(object):
def __init__(self):
self.obj =()
return

if __name__ ="__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
---------------------------------------

I can figure out some ways to fix this but none seems satisfying.
Either they are too specific or too cumbersome.
A runtime redefinition of class A does not seem to work either.
What would be the most "pythonesque" solution other than sorting out
the class order?

Best Regards,
Lorenzo
You haven't shown us any problem. class B works fine with a forward
reference to A. Now if you were trying to subclass A before defining
it, that'd be a problem. Or if you were trying to make an instance of B
before defining A.

Better put some code together with enough meat to actually show a
symptom. And tell us what sys.version says. I'm testing with 2.6.2
(r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)], running
on Win XP.- Hide quoted text -

- Show quoted text -

Thank you for your help: I'm working on a rather large source, but I
think I have isolated the problem now.
This listing generates an error:

-----------------------------------------------
class BaseA(object):
def __init__(self):
return

class DebugA(BaseA):
def __init__(self):
return

class B(object):
def __init__(self,test=A()):
self.obj =()
return

if __name__ ="__main__":
# class A(BaseA): # Uncomment this for using BaseA objects
# pass
class A(DebugA): # Uncomment this for using DebugA objects
pass
-----------------------------------------------

The error happens because Python apparently evaluates the named
arguments before running the script.
I think I have read something about this some (long) time ago but I
can't find it anymore.

Suggestions?

BTW, my Python version is 2.6.1 (with latest PyDev).

Thx!
Lorenzo
This error is caused because a default argument uses class A. Default
arguments of class methods are evaluated during the definition of the
class, and not later when the class is instantiated. Thus the problem.

To work around that specific problem, you may want to use the following:

class B(object):
def __init__(self,test=None):
if test==None:
test = A()
self.obj =()
return


This is actually different than what you had, since what you had would
have used the same A() object for all instances of B that didn't supply
their own test() parameter. Maybe that's what you wanted, and maybe
not, but default arguments set to mutable values are frequently a bug.

But I'm wondering if you're just looking for problems. Why not put the
commented switch early in the file, and test for it wherever you need to
use it?

import x, y, z
_debug = False
#_debug = True


then as soon as BaseA and DebugA are defined, do the following:

if _debug:
class A(DebugA):
pass
else:
class A(BaseA)
pass
 
L

Lorenzo Di Gregorio

LorenzoDiGregoriowrote:
LorenzoDiGregoriowrote:
Hi,
I'm wondering what would be the preferred way to solve the following
forward reference problem:
---------------------------------------
class BaseA(object):
    def __init__(self):
        return
class DebugA(BaseA):
    def __init__(self):
        return
# here I would have a prototype of class A which is the same as class
BaseA
class B(object):
    def __init__(self):
        self.obj =()
        return
if __name__ ="__main__":
#    class A(BaseA): # Uncomment this for using BaseA objects
#       pass
    class A(DebugA): # Uncomment this for using DebugA objects
        pass
---------------------------------------
I can figure out some ways to fix this but none seems satisfying.
Either they are too specific or too cumbersome.
A runtime redefinition of class A does not seem to work either.
What would be the most "pythonesque" solution other than sorting out
the class order?
Best Regards,
Lorenzo
You haven't shown us any problem.  class B works fine with a forward
reference to A.  Now if you were trying to subclass A before defining
it, that'd be a problem.  Or if you were trying to make an instance of B
before defining A.
Better put some code together with enough meat to actually show a
symptom.  And tell us what sys.version says.  I'm testing with   2.6.2
(r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)], running
on Win XP.- Hide quoted text -
- Show quoted text -
Thank you for your help: I'm working on a rather large source, but I
think I have isolated the problem now.
This listing generates an error:
class DebugA(BaseA):
    def __init__(self):
        return
class B(object):
    def __init__(self,test=A()):
        self.obj =()
        return
if __name__ ="__main__":
#    class A(BaseA): # Uncomment this for using BaseA objects
#        pass
     class A(DebugA): # Uncomment this for using DebugA objects
         pass
-----------------------------------------------
The error happens because Python apparently evaluates the named
arguments before running the script.
I think I have read something about this some (long) time ago but I
can't find it anymore.

BTW, my Python version is 2.6.1 (with latest PyDev).
Thx!
Lorenzo

This error is caused because a default argument uses class A.  Default
arguments of class methods are evaluated during the definition of the
class, and not later when the class is instantiated.  Thus the problem.

To work around that specific problem, you may want to use the following:

class B(object):
    def __init__(self,test=None):
        if test==None:
            test = A()
        self.obj =()
        return

This is actually different than what you had, since what you had would
have used the same A() object for all instances of B that didn't supply
their own test() parameter.  Maybe that's what you wanted, and maybe
not, but default arguments set to mutable values are frequently a bug.

But I'm wondering if you're just looking for problems.  Why not put the
commented switch early in the file, and test for it wherever you need to
use it?

import  x, y, z
_debug = False
#_debug = True

then as soon as  BaseA and DebugA are defined, do the following:

if _debug:
    class A(DebugA):
        pass
else:
    class A(BaseA)
        pass- Zitierten Text ausblenden -

- Zitierten Text anzeigen -

I had also thought of using "None" (or whatever else) as a marker but
I was curious to find out whether there are better ways to supply an
object with standard values as a default argument.
In this sense, I was looking for problems ;-)

Of course the observation that "def" is an instruction and no
declaration changes the situation: I would not have a new object being
constructed for every instantiation with no optional argument, because
__init__ gets executed on the instantiation but test=A() gets executed
on reading 'def'.

At this point I think there is no other way than using a marker as
suggested above multiple times, if I want to supply a new object with
default values for non-passed arguments.

Anybody with a better idea?
 
L

Lie Ryan

Lorenzo said:
I had also thought of using "None" (or whatever else) as a marker but
I was curious to find out whether there are better ways to supply an
object with standard values as a default argument.
In this sense, I was looking for problems ;-)

Of course the observation that "def" is an instruction and no
declaration changes the situation: I would not have a new object being
constructed for every instantiation with no optional argument, because
__init__ gets executed on the instantiation but test=A() gets executed
on reading 'def'.

At this point I think there is no other way than using a marker as
suggested above multiple times, if I want to supply a new object with
default values for non-passed arguments.

Using None as default for mutable default argument is the common idiom
for the problem you're having.
 
D

Dennis Lee Bieber

At this point I think there is no other way than using a marker as
suggested above multiple times, if I want to supply a new object with
default values for non-passed arguments.
Marker is needed if the default is to a mutable type (unless, now
that you know the behavior, you have an explicit need for a shared
"default").

If the default is a numeric, tuple, or string, it is safe since
nothing you do later in the code can change its "contents"
--
Wulfraed Dennis Lee Bieber KD6MOG
(e-mail address removed) (e-mail address removed)
HTTP://wlfraed.home.netcom.com/
(Bestiaria Support Staff: (e-mail address removed))
HTTP://www.bestiaria.com/
 
B

Bruno Desthuilliers

Piet van Oostrum a écrit :
TypeError: readonly attribute

Make that: A.__bases__ = DebugA,


or even better (readability-wise):

A.__bases__ = (DebugA,)

Just the same thing, but at least you wont neither miss the ',' no
wonder wether it's a typo or the author really wanted a tuple.
 
B

Bruno Desthuilliers

Dave Angel a écrit :
(snip
Default
arguments of class methods are evaluated during the definition of the
class

<pedantic>
Default arguments of functions are eval'd during the execution of the
def statement.

The fact that you use a def statement within a class statement's body is
totally orthogonal - a def statement produces a function object, period.

It's only when resolved as a class attribute that a function "becomes" a
method object (thanks to the descriptor protocol).
</pedantic>
 
L

Lorenzo Di Gregorio

LorenzoDiGregoriowrote:


If what you are worrying about is having a single default object, you
could do something like this:

     class B(object):
         _default = None

         def __init__(self, test=None):
             if test is None:
                 test = self._default
                 if test is None:
                     B._default = test = A()
             ...

--Scott David Daniels
(e-mail address removed)- Zitierten Text ausblenden -

- Zitierten Text anzeigen -

Well, I could also declare (ups, define ;-)) __init__(self,**kwargs)
and within the __init__, if kwargs['test'] exists, do test = kwargs
['test'], if it does not exist, do test = A().

The point is that it would have been cleaner to place it straight in
the __init__, but due to the semantic of 'def' this does not seem
possible.
 

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,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top