Scope and program structure problems

Discussion in 'Python' started by John Dann, Jul 1, 2008.

  1. John Dann

    John Dann Guest

    Trying to learn Python here, but getting tangled up with variable
    scope across functions, modules etc and associated problems. Can
    anyone advise please?

    Learning project is a GUI-based (wxPython) Python program that needs
    to access external data across a serial port.

    The first thing that I need the program to do when it starts up is to
    check that it can see the serial port, the first step of which is to
    check that it can import the pySerial module, and display an
    appropriate message in the statusbar of the main frame. (I know there
    may be other ways of doing this but this is what I'm trying to do
    currently and I'd like to understand how to make this approach work
    robustly even if there are other options.)

    So it seems that if I want to write to the frame's statusbar, I can't
    do anything re serial import until the frame is initialising. And I
    can't put any serial import code after the app.Mainloop() code line
    because it won't run until the frame closes.

    In other words, I seem to be stuck with importing the serial library
    either in the __init__ function or - if implemented slightly
    differently - in a separate function of the wxPython Frame class, ie a
    code structure of:

    -----------
    # Can't put serial import code here because there's no GUI yet to send
    messages to.

    Class Frame(wx.Frame)
    def __init__()
    etc
    #code to check and use serial port has to go below??
    def CheckSerial() # eg triggered by appropriate menu click event
    Try:
    import serial
    etc
    ser = Serial.etc # set ser object as handle to serial port
    def UseSerial():
    # Code to perform serial IO

    app=App(wx.App)
    app.MainLoop()

    # Can't put serial import code here because it won't execute until
    frame closes?
    -----------

    But then I hit another problem. If I set the object ser to point to
    the serial port in the CheckSerial function, then it's just got local
    scope within the function and can't be seen from the UseSerial
    function.

    So I've got 2 questions. The main one is whether there's any way of
    explicitly giving the ser object global scope in the code line:

    ser = Serial.etc # set ser object as handle to serial port

    And, second, does my overall description above reveal any serious
    misunderstanding about how best to handle this sort of problem in
    Python?

    Apologies for the long post and hope that my description is clear
    enough to make sense.

    JGD
     
    John Dann, Jul 1, 2008
    #1
    1. Advertising

  2. John Dann a écrit :
    > Trying to learn Python here, but getting tangled up with variable
    > scope across functions, modules etc and associated problems. Can
    > anyone advise please?
    >
    > Learning project is a GUI-based (wxPython) Python program that needs
    > to access external data across a serial port.
    >
    > The first thing that I need the program to do when it starts up is to
    > check that it can see the serial port, the first step of which is to
    > check that it can import the pySerial module, and display an
    > appropriate message in the statusbar of the main frame. (I know there
    > may be other ways of doing this but this is what I'm trying to do
    > currently and I'd like to understand how to make this approach work
    > robustly even if there are other options.)
    >

    (snip)
    >
    > -----------
    > # Can't put serial import code here because there's no GUI yet to send
    > messages to.
    >
    > Class Frame(wx.Frame)
    > def __init__()
    > etc
    > #code to check and use serial port has to go below??
    > def CheckSerial() # eg triggered by appropriate menu click event
    > Try:
    > import serial
    > etc
    > ser = Serial.etc # set ser object as handle to serial port
    > def UseSerial():
    > # Code to perform serial IO
    >
    > app=App(wx.App)
    > app.MainLoop()
    >
    > # Can't put serial import code here because it won't execute until
    > frame closes?
    > -----------
    >
    > But then I hit another problem. If I set the object ser to point to
    > the serial port in the CheckSerial function, then it's just got local
    > scope within the function and can't be seen from the UseSerial
    > function.
    >
    > So I've got 2 questions. The main one is whether there's any way of
    > explicitly giving the ser object global scope in the code line:
    >
    > ser = Serial.etc # set ser object as handle to serial port


    It is possible - for the python definition of 'global' being
    'module-global' - but it's not necessarily the best solution. Look up
    the doc for the 'global' statement to learn more.

    Another solution would be to make ser an attribute of your Frame object:

    class Frame(wx.Frame):
    def __init__(self):
    self.serial = None

    #code to check and use serial port has to go below??
    def setup_serial(self) # eg triggered by appropriate menu click event
    try:
    import serial
    except ImportError, e:
    # display an error message and either crash
    # or return

    else:
    # set self.serial object as handle to serial port
    self.serial = serial.etc()


    def use_serial(self):
    # Code to perform serial IO
    # won't work if setupSerial failed

    > And, second, does my overall description above reveal any serious
    > misunderstanding about how best to handle this sort of problem in
    > Python?


    Well... You'd get a very similar result by doing your import at the
    top-level but within a try/except block, then when you have a gui setup
    takes appropriate actions.

    try:
    import serial
    serial_import_error = None
    except ImportError, e:
    serial = None
    serial_import_error = e


    class Frame(wx.Frame):
    def __init__(self):
    self.serial = None

    def setup_serial(self):
    if serial is None:
    # display an error message and either crash
    # or return
    else:
    self.serial = serial.whatever()



    <disclaimer>

    But anyway: I have no real (read : anything requiring any brain-cell)
    experience writing rich GUI apps in Python, and I haven't done rich GUI
    programming for four last years at least, so there may be better
    solutions here.

    So : Any wxPython guru around ?-)

    </disclaimer>
     
    Bruno Desthuilliers, Jul 1, 2008
    #2
    1. Advertising

  3. John Dann

    John Dann Guest

    Many thanks for the repsonse - much appreciated.

    And sorry - yes I was probably compounding two separate issues here -
    the GUI one and the variable scope one. Maybe the wxPython list would
    be the best place to ask more about the GUI side of things.

    Then actually I can simplify my remaining question quite a lot - I
    know you've partly answered this already but let me just rephrase
    this:

    It must be commonplace to first use a variable or object within one
    function in a module and then want to use it again - perhaps still
    with the same value as in the first function - in a second function.

    So what's the recommended Python way round this? If I was using .Net
    then I'd be declaring the variables/objects explicitly and could set
    their scope according to how/where they were declared. But if the
    Python way is not to declare vars before use then this must create
    some problems for cross-function use.

    So it is best to declare such vars at the module level (ie outside of
    a function) and set eg to Null/None or to assign them with a keyword
    such as global or static (assuming that concept applies in Python) at
    first use inside a function or what?

    JGD
     
    John Dann, Jul 1, 2008
    #3
  4. John Dann a écrit :
    > Many thanks for the repsonse - much appreciated.
    >
    > And sorry - yes I was probably compounding two separate issues here -
    > the GUI one and the variable scope one. Maybe the wxPython list would
    > be the best place to ask more about the GUI side of things.
    >
    > Then actually I can simplify my remaining question quite a lot - I
    > know you've partly answered this already but let me just rephrase
    > this:
    >
    > It must be commonplace to first use a variable or object


    What difference do you make between a variable and an object in this
    context ?

    > within one
    > function in a module and then want to use it again - perhaps still
    > with the same value


    For which definition of "same value" ? Remember, Python has *no*
    primitive types. Everything is an object.

    > as in the first function - in a second function.
    > So what's the recommended Python way round this?


    This is way too general to give a single straight answer - whatever the
    language FWIW. Are both function in the same module ? Are they methods
    of a same object ? Is the first function calling the second or are they
    totally unrelated ?

    > If I was using .Net
    > then I'd be declaring the variables/objects explicitly


    I personnally find the assignment, import, class and def statements (all
    having a binding behaviour) to be rather explicit.

    > and could set
    > their scope according to how/where they were declared.


    Names bound at the top-level are globals to the module. Names bound
    within a function (including arguments) are locals, unless there has
    been a global statement for them before in the same function's body.
    Names bound within a class statement lives in the class's namespace (IOW
    : they become class attributes, shared by all instances). And names
    bound as object attributes (using either obj.name = whatever or
    setattr(obj, "name", whatever)) become, well, object attributes.

    Now there may be a couple points worth paying attention to :

    1/ some types (numerics, strings and tuples at least) are immutable.
    When doing :

    i = 1
    i = 2

    the second assignment doesnt mutate the integer object bound to i, but
    rebinds i to another integer object.

    2/ the scope of a name isn't necessarily related to the scope of the
    object bound to that name.

    As an example, function's parameters *names* are local to the function,
    but the actual arguments - the objects passed when calling the function
    - are not.

    IOW, rebinding a parameter name withing a function body will rebind the
    name locally, but will not affect the object originally bound to that
    name. But *mutating* an object passed as argument *will* (off course)
    affect the object outside the function.


    > But if the
    > Python way is not to declare vars before use


    For which definition of "var" and "use" ? Did you really try to use a
    name that didn't exist in the accessible namespace - I mean, "use" for
    anything else than binding ?

    The first binding of a name in a namespace *is* the 'declaration'. If a
    name is not defined in the currently accessible namespace, and you try
    to do anything else than binding it, you'll get an exception.

    > then this must create
    > some problems for cross-function use.


    If what you mean is that it makes it harder to litter you code with true
    global variables, then it's obviously a *very* good thing IMHO !-)

    But no, there's no more problem with shared state in Python than in any
    other (imperative) language I know - usually less in fact since there's
    no "application global" namespace. Most of the time, the appropriate way
    to share state is to use classes - heck, that's what they were created
    for, isn't it ?-). Sharing state thru module-level variables is ok as
    long you only access it for reading (pseudo-constants etc).

    Sometimes, it's ok to use a module-level variable read-write, but this
    should be restricted to this particular module's implementation stuff
    (IOW : the name is not part of the API, and only functions / methods in
    this same module access it), and never done without a clear enough
    understanding of Python's namespaces and bindings.


    > So it is best to declare such vars at the module level (ie outside of
    > a function) and set eg to Null/None or to assign them with a keyword
    > such as global or static (assuming that concept applies in Python)


    it doesnt.

    > at first use inside a function or what?


    Usually, when you really need a module-level variable to be shared
    read-write between functions, it happens that you can bind it to a
    sensible default. Now what is a "sensible default" depends on the
    concrete use case. Anyway, by all means, explicitely bind this name at
    the top-level, before any code accessing it, and if possible with a
    comment about this name being shared read/write by functions x and y.

    My 2 cents...
     
    Bruno Desthuilliers, Jul 1, 2008
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Paul Opal
    Replies:
    12
    Views:
    974
    Paul Opal
    Oct 11, 2004
  2. ann
    Replies:
    13
    Views:
    682
    Patricia Shanahan
    Sep 13, 2005
  3. Steven T. Hatton
    Replies:
    9
    Views:
    504
  4. Xah Lee
    Replies:
    0
    Views:
    2,260
    Xah Lee
    Feb 26, 2009
  5. Andrew Falanga
    Replies:
    2
    Views:
    205
    Andrew Falanga
    Nov 22, 2008
Loading...

Share This Page