[Tutor] __init__ Problem, self is not defined [default values assigned at method definition time]

Danny Yoo dyoo@hkn.eecs.berkeley.edu
Tue, 30 Jul 2002 23:36:13 -0700 (PDT)


On Tue, 30 Jul 2002, Matthew Navarre wrote:

> I'm attempting to subclass Pmw.ComboBox. when my class definition is run
> by the interpreter I get a NameError: name self is not defined. AFAICT,
> my syntax is correct, but I've obviously missed something.
>
> here's the relevant part of my class definition:
>
>
> from Tkinter import *
> import Pmw
>
> class AddressComboBox(Pmw.ComboBox):
>     def __init__(self, root=None, label_text='Address:',
>                 label_pos='w', listbox_width=24, dropdown=1,
>                 scrolledlist_items=self.addresses, history=1,
>                 unique=1, selectioncommand=self.getAddress,
>                 addressfile=None ):


Hi Matt,

During the __init__() function, you should assume that none of the 'self'
values have been initialized yet, so that's why one reason why
'self.addresses' and 'self.getAddress' can confuse Python...


... actually, I'm fibbing.  A more truthful reason is that all of the
default values in the __init__() definition need to make sense to Python
during the definition, even before any instances of AddressComboBox exist.


This can be really problematic if we're working with mutable things.  For
example:

###
>>> class TestInitializer:
...     def __init__(self, some_number=3*4, some_list=[]):
...         self.number = some_number
...         self.list = some_list
...
>>> t1 = TestInitializer()
>>> t1.number
12
>>> t1.list
[]
>>> t2 = TestInitializer()
>>> t2.number
12
>>> t2.list
[]
>>> t1.list.append(42)
>>> t1.list
[42]
>>> t2.list
[42]
###

Notice here that both t1 and t2 share that default list that was created
during the definition of __init__:

    def __init__(self, some_number=3*4, some_list=[]):

So default values get set once, during method definition, and they stick
with that same value for all calls to that method.



Here's another small example that shows when the default stuff is being
evaluated:

###
>>> def makelist():
...     print "I'm being called"
...     return []
...
>>> class TestDefault:
...     def __init__(self, value=makelist()):
...         self.value = value
...
I'm being called
>>> TestDefault()
<__main__.TestDefault instance at 0x8155884>
>>> TestDefault()
<__main__.TestDefault instance at 0x8152d5c>
###

The key thing here is seeing that "I'm being called" shows up immediately,
even before we create an instance.  And it only gets called once.



Going back to your original question: this is why trying to refer to
'self.addresses' and 'self.getAddress' doesn't work: those two names won't
be defined at that particular type as you're typing that __init__
definition.



Hmmm... what is self.addresses, though?  Is it referring to some
'addresses' variable in your class?  If so, you can express things this
way:

###
class AddressComboBox(Pmw.ComboBox):
    def __init__(self, root=None, label_text='Address:',
                label_pos='w', listbox_width=24, dropdown=1,
                scrolledlist_items=None, history=1,
                unique=1, selectioncommand=None,
                addressfile=None):

        if scrolledlist_items == None:
             scrolledlist_items = self.addresses

        if selectioncommand == None:
             selectioncommand = self.getAddress

        ## Continue on as in the previous code
###

Here, we sidestep things by setting our defaults to some sentinel "None"
value.  By the time we get into the body of the __init__, self.addresses
and self.getAddress should be accessible, so we should be in safe waters.


I get the feeling I didn't explain this well, so please ask more questions
about the places where things feel fuzzy.  Talk to you later!