By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,294 Members | 2,681 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,294 IT Pros & Developers. It's quick & easy.

Refactoring Dilemma

P: n/a
'''
I'm in the middle of a refactoring dilemma.
I have several singletons that I'm turning into modules, for ease of
access.
The usual method is noted as 'Module 1' below.
The new method is noted as 'Module 2'.
Is there any reason NOT to do this that I may be unaware of?
It's easier than remembering to declare global variables at the top of
the function.
'''

# ----------- Module 1.py ------------
# Normal module processing
var = 0

def MyRoutine():
global var
var = 1

MyRoutine()
print var
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var

Sep 10 '06 #1
Share this Question
Share on Google+
7 Replies


P: n/a
Kamilche wrote:
'''
I'm in the middle of a refactoring dilemma.
I have several singletons that I'm turning into modules, for ease of
access.
The usual method is noted as 'Module 1' below.
The new method is noted as 'Module 2'.
Is there any reason NOT to do this that I may be unaware of?
It's easier than remembering to declare global variables at the top of
the function.
'''

# ----------- Module 1.py ------------
# Normal module processing
var = 0

def MyRoutine():
global var
var = 1

MyRoutine()
print var
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var

What's wrong with
<code>
def MyRoutine():
return 1

var = MyRoutine()
</code>
?

George

Sep 10 '06 #2

P: n/a
Kamilche wrote:
'''
I'm in the middle of a refactoring dilemma.
I have several singletons that I'm turning into modules, for ease of
access.
The usual method is noted as 'Module 1' below.
The new method is noted as 'Module 2'.
Is there any reason NOT to do this that I may be unaware of?
It's easier than remembering to declare global variables at the top of
the function.
'''

# ----------- Module 1.py ------------
# Normal module processing
var = 0

def MyRoutine():
global var
var = 1

MyRoutine()
print var
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var
I don't see any major problem with it. In fact, I think it's a very
good idea to do this, rather than use global statements, when using
module as a singleton class.

I recently made the same transition myself, though I solved it a bit
differently. I used a decorator to pass the module as the first
argument (actually it passes a proxy to module's global dict, since the
module itself isn't accesible from the function object).

def modmethod(func):
class modproxy(object):
__getattribute__ = func.func_globals.__getitem__
__setattr__ = func.func_globals.__setitem__
self = modproxy()
def call_with_module(*args,**kwargs):
return func(self,*args,**kwargs)
call_with_module.func_name = func.func_name
return call_with_module

@modmethod
def MyRoutine(self):
self.var = 1

MyRoutine()
print var

One problem with my decorator is it makes stack traces a little
bloated. (Also attribute errors are raised as KeyError, but that's
easily fixable.) Other than that, I've been running it for awhile
without any problems. I doubt your approach would have many problems,
either.
Carl Banks

Sep 10 '06 #3

P: n/a
George Sakkis wrote:
Kamilche wrote:
'''
I'm in the middle of a refactoring dilemma.
I have several singletons that I'm turning into modules, for ease of
access.
The usual method is noted as 'Module 1' below.
The new method is noted as 'Module 2'.
Is there any reason NOT to do this that I may be unaware of?
It's easier than remembering to declare global variables at the top of
the function.
'''

# ----------- Module 1.py ------------
# Normal module processing
var = 0

def MyRoutine():
global var
var = 1

MyRoutine()
print var
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var


What's wrong with
<code>
def MyRoutine():
return 1

var = MyRoutine()
</code>
?

Kamilche simplified things in his example that obscured the main use
case. Short story is, var is considered a state of the module, and
MyRoutine() is designed to be called from outside the module to modify
the state. Your suggestion can only modify the state of the module
from within, so it won't do.

More detailed discussion:

A common piece of advice when people ask "how can I implement a
singleton in Python" is to use a module. A module, after all, is a
singleton object.

But there's a big problem using a module as a singleton: you need to
use then global statement to rebind module-level variables. Which
means that if you want your module to have lots of modifyable state,
most likely you'll have to use a bunch global statements. And that's a
major pain in the neck when you have to do it a lot and in many
functions.

In these cases, it would be nice if a module could access its own state
in the same way that an instance does, that is, as an attribute. My
approach of using a decorator to pass in a module proxy, and Kamilche's
approach of binding the module object to self, both accomplish this.
Internally, the module behaves very much like a class instance.
Functions in the module act almost exactly like bound methods, and
module-level variables act like instance attributes. The difference
between writing a module and a singleton class thus becomes mostly a
matter of indentation.

In fact, when I made a major switch from using singleton classes to
modules, I was able to change it by dedenting once, and pasting a
@modmethod decoration above each method, with very few other changes.

Carl Banks

Carl Banks

Sep 10 '06 #4

P: n/a
Carl Banks wrote:
Kamilche wrote:
'''
I'm in the middle of a refactoring dilemma.
I have several singletons that I'm turning into modules, for ease of
access.
The usual method is noted as 'Module 1' below.
The new method is noted as 'Module 2'.
Is there any reason NOT to do this that I may be unaware of?
It's easier than remembering to declare global variables at the top of
the function.
'''

# ----------- Module 1.py ------------
# Normal module processing
var = 0

def MyRoutine():
global var
var = 1

MyRoutine()
print var
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var

I don't see any major problem with it. In fact, I think it's a very
good idea to do this, rather than use global statements, when using
module as a singleton class.

I recently made the same transition myself, though I solved it a bit
differently. I used a decorator to pass the module as the first
argument (actually it passes a proxy to module's global dict, since the
module itself isn't accesible from the function object).

def modmethod(func):
class modproxy(object):
__getattribute__ = func.func_globals.__getitem__
__setattr__ = func.func_globals.__setitem__
self = modproxy()
def call_with_module(*args,**kwargs):
return func(self,*args,**kwargs)
call_with_module.func_name = func.func_name
return call_with_module

@modmethod
def MyRoutine(self):
self.var = 1

MyRoutine()
print var
This looks quite hackish, both the implementation and the usage; most
people would get confused when they didn't find var's assignment at
global scope. I prefer the the simple global statements if they aren't
that many, otherwise the assignment of the module to self is also fine.

George

Sep 11 '06 #5

P: n/a

George Sakkis wrote:
Carl Banks wrote:
I don't see any major problem with it. In fact, I think it's a very
good idea to do this, rather than use global statements, when using
module as a singleton class.

I recently made the same transition myself, though I solved it a bit
differently. I used a decorator to pass the module as the first
argument (actually it passes a proxy to module's global dict, since the
module itself isn't accesible from the function object).

def modmethod(func):
class modproxy(object):
__getattribute__ = func.func_globals.__getitem__
__setattr__ = func.func_globals.__setitem__
self = modproxy()
def call_with_module(*args,**kwargs):
return func(self,*args,**kwargs)
call_with_module.func_name = func.func_name
return call_with_module

@modmethod
def MyRoutine(self):
self.var = 1

MyRoutine()
print var

This looks quite hackish, both the implementation and the usage;
Once again, the function MyRoutine is intended to be called from
another module. The usage I have here is just an example demonstrating
that var is in the module's dict. Please keep this in mind when
criticizing usage. The intended usage would be something like this
(from another module):

from xxx import yyy
yyy.MyRoutine()

As far as the caller is concerned, yyy could be a module using globals,
a module with this "hack", or a class instance, or something else. It
doesn't matter; usage is the same in all three cases.

As for the implementation...
most
people would get confused when they didn't find var's assignment at
global scope. I prefer the the simple global statements if they aren't
that many, otherwise the assignment of the module to self is also fine.
For ordinary modules that might have one or two serial number counters
or debug flags or whatnot, I agree. For modules that have lots of
state and act more like class instances than modules, the massive
amounts of globals are ugly and error prone. In that case you should
either use a regular class, or tolerate the "hack"; don't use a bunch
of globals.

I think a simple comment at the top could take care of any confusion
about what's happening. Since it mimics how a class works, it won't be
anything completely new.

YMMV.

Carl Banks

Sep 11 '06 #6

P: n/a
George Sakkis wrote:
I prefer the the simple global statements if they aren't
that many, otherwise the assignment of the module to self is also fine.
I replied to this article, and then canceled the reply (but that never
works), thinking you were criticizing the general concept of "module as
a singleton" and not just my approach. I should learn to read. I
apologize for the misunderstanding.

I do agree my implementation is a little hacky, although IMHO it feels
to fit in with the way classes work better, which appeals to me but
probably not most people :). For one thing, the methods have the
explicit self argument, like regular classes. For another, it uses a
decorator to affect the binding of the modules. In regular classes,
functions are instance methods by default, and become class methods or
static methods with a decorator. I like to think of modules as having
a special metaclass that treats functions as static methods by default,
and uses a decorator to get an instance method. (Even though that
isn't remotely how it works, it fits how it would work if you were
implementing modules based on classes.)

Having said that, I've no problem with Kamilche's approach. The real
evil in my mind is using lots of global statements, and anything you
can do to stay away from that is a good thing.
Carl Banks

Sep 11 '06 #7

P: n/a
Kamilche wrote:
Is there any reason NOT to do this that I may be unaware of?
[snip]
# ----------- Module 2.py ------------
# 'Self' module processing
import sys
var = 0
self = sys.modules[__name__]

def MyRoutine():
self.var = 1

MyRoutine()
print var
Looks basically fine to me, but I'd probably write it as::

_self = __import__(__name__)

Then you don't need to import sys. I'd also use a leading underscore
for "self" so that it's clearly marked as a module-internal attribute.

STeVe
Sep 11 '06 #8

This discussion thread is closed

Replies have been disabled for this discussion.