Having Trouble with Scoping Rules

C

Charles Krug

List:

I've a module that's not doing what I expect. My guess is that I don't
quite understand the scoping rules the way I should.

I have an object that's costly to create. My thought was to create it
at the module level like this:

# expensive Object Module

_expensiveObject = None
def ExpensiveObject():

if not(_expensiveObject):
_expensiveObject = "A VERY Expensive object"

return _expensiveObject

if __name__ == '__main__':
print _expensiveObject
print ExpensiveObject()

When I run this module, I get the expected "None" but then I get
UnboundLocalError from the function call when _expensiveObject is
accessed:

Traceback (most recent call last):
File
"C:\Python24\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py",
line 310, in RunScript
exec codeObject in __main__.__dict__
File "Expensive.py", line 13, in ?
print ExpensiveObject()
File "Expensive.py", line 6, in ExpensiveObject
if not(_expensiveObject):
UnboundLocalError: local variable '_expensiveObject' referenced before
assignment

I obviously missed some part of the scoping rules.

What's the correct way to do this?

Thanx


Charles
 
F

Farshid Lashkari

You need to declare _expensiveObject as global inside your function.
Whenever you assign something to a variable that resides in the global
scope inside a function, you need to declare it as global at the
beginning of the function. So your function should look like this

def ExpensiveObject():
global _expensiveObject
if not(_expensiveObject):
_expensiveObject = "A VERY Expensive object"

return _expensiveObject

The documentation will no doubtedly explain it better than I have

-Farshid
 
M

Michael Spencer

Charles said:
List:
....
# expensive Object Module

_expensiveObject = None
def ExpensiveObject():

if not(_expensiveObject):
_expensiveObject = "A VERY Expensive object"

return _expensiveObject
....
I obviously missed some part of the scoping rules.

What's the correct way to do this?

Thanx


Charles
As the traceback says:
> UnboundLocalError: local variable '_expensiveObject' referenced before
assignment

By assigning to _expensiveObject in the function you make _expensiveObject a
local identifier (no matter which code path is followed). The easiest 'fix' for
your code is to insert a global statement:


_expensiveObject = None
def ExpensiveObject():
global _expensiveObject # Without this, _expensiveObject is local
# because of the assignment below
if not(_expensiveObject):
_expensiveObject = "A VERY Expensive object"

return _expensiveObject


[See: http://docs.python.org/ref/naming.html for more details]

But there is likely to be a more elegant solution, depending on what your real
code looks like.

HTH

Michael
 
C

Charles Krug

You need to declare _expensiveObject as global inside your function.
Whenever you assign something to a variable that resides in the global
scope inside a function, you need to declare it as global at the
beginning of the function. So your function should look like this

def ExpensiveObject():
global _expensiveObject
if not(_expensiveObject):
_expensiveObject = "A VERY Expensive object"

return _expensiveObject

The documentation will no doubtedly explain it better than I have

Not really. If I'd understood the docs, I wouldn't need to ask here.

Okay, that works in the module where I define the function. But if I
import the module:

# expensive Object User
import Expensive

print Expensive.ExpensiveObject()

I get the same exception. That approach most likely isn't going to work
for me, as I need to be able to reuse the costly (to create) object.


Okay THIS seems to be working for me:

# expensive Object Module

_expensiveObject = None
def ExpensiveObject():
try:
retval = _expensiveObject
except UnboundLocalError:
_expensiveObject = "A VERY Expensive object"
retval = _expensiveObject

return retval

if __name__ == '__main__':
print _expensiveObject
print ExpensiveObject()



Which gives me:

I'll try it with my actual class instance to verify. Anyone see
anything I'm missing?


Thanx
 
F

Fredrik Lundh

Okay, that works in the module where I define the function. But if I
import the module:

# expensive Object User
import Expensive

print Expensive.ExpensiveObject()

I get the same exception.

not if you followed Farshid's instructions.
Okay THIS seems to be working for me:

# expensive Object Module

_expensiveObject = None
def ExpensiveObject():
try:
retval = _expensiveObject
except UnboundLocalError:
_expensiveObject = "A VERY Expensive object"
retval = _expensiveObject

return retval

that doesn't work at all:

_expensiveObject = None
def ExpensiveObject():
try:
retval = _expensiveObject
except UnboundLocalError:
_expensiveObject = "A VERY Expensive object"
print "CREATED VERY EXPENSIVE OBJECT"
retval = _expensiveObject
return retval

if __name__ == "__main__":
print _expensiveObject
print ExpensiveObject()
print ExpensiveObject()
print ExpensiveObject()


prints

None
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object

compare this to Farshid's solution:

_expensiveObject = None

def ExpensiveObject():
global _expensiveObject
if _expensiveObject is None:
_expensiveObject = "A VERY Expensive object"
print "CREATED VERY EXPENSIVE OBJECT"
return _expensiveObject

if __name__ == "__main__":
print _expensiveObject
print ExpensiveObject()
print ExpensiveObject()
print ExpensiveObject()

which prints

None
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object
A VERY Expensive object
A VERY Expensive object

and works perfectly fine if you import it from another module:
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object A VERY Expensive object
Which gives me:

the only thing you've proved here is that the string constant "A
VERY Expensive object" is the same thing as the string constant
"A VERY Expensive object".
I'll try it with my actual class instance to verify. Anyone see
anything I'm missing?

</F>
 
B

bruno at modulix

Charles said:
List:

I've a module that's not doing what I expect. My guess is that I don't
quite understand the scoping rules the way I should.

I have an object that's costly to create. My thought was to create it
at the module level like this:
(snip)

What's the correct way to do this?
See other answers in this thread for how to solve the UnboundLocalError
problem.

Now about your *real* problem - which is nothing new -, you may want to
read about some known solutions:

http://en.wikipedia.org/wiki/Singleton_pattern
http://en.wikipedia.org/wiki/Proxy_pattern
http://en.wikipedia.org/wiki/Lazy_initialization
http://en.wikipedia.org/wiki/Memoization

HTH
 
C

Charles Krug

def ExpensiveObject():
global _expensiveObject
if _expensiveObject is None:
_expensiveObject = "A VERY Expensive object"
print "CREATED VERY EXPENSIVE OBJECT"
return _expensiveObject

if __name__ == "__main__":
print _expensiveObject
print ExpensiveObject()
print ExpensiveObject()
print ExpensiveObject()

which prints

None
CREATED VERY EXPENSIVE OBJECT
A VERY Expensive object
A VERY Expensive object
A VERY Expensive object

and works perfectly fine if you import it from another module:

Ah, I had another error that broke it on import. Testing with a
dictionary showed me the "creates another object" error. Using a string
constant for that was just a "between the ears" problem that would have
shown me the error much quicker.

Thanks.
 
C

Charles Krug

See other answers in this thread for how to solve the UnboundLocalError
problem.

Now about your *real* problem - which is nothing new -, you may want to
read about some known solutions:

http://en.wikipedia.org/wiki/Singleton_pattern
http://en.wikipedia.org/wiki/Proxy_pattern
http://en.wikipedia.org/wiki/Lazy_initialization
http://en.wikipedia.org/wiki/Memoization

No, I'm already quite certain that this is a Singleton--pythonically a
module-level instance of Something--I'm not thinking of it as "lazy
instantiated" 'cuz it's initialized at startup, but I suppose it
qualifies.

I considered using a Borg for this, but discarded the idea as "too
complicated for my needs--this will be easier to implement . . . "

Which I suppose it would be if I knew how correctly to use the "global"
keyword.

I don't need to restrict the actual class to One Instance so much as I'd
prefer that to be the default behavior. I can't think of a reason why
I'd want to subclass this, so I can't say whether it makes sense to
Borg/Highlander/Singleton it or not. For now, I'll Do The Simplest
Thing That Might Work and figure YAGNI (or IAGNI, I guess).

Thanks for the help.
 

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
474,266
Messages
2,571,078
Members
48,772
Latest member
Backspace Studios

Latest Threads

Top