Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Generate a new object each time a name is imported

2 views
Skip to first unread message

Steven D'Aprano

unread,
Aug 2, 2009, 3:35:08 AM8/2/09
to
I would like to generate a new object each time I import a name from a
module, rather than getting the same object each time. For example,
currently I might do something like this:

# Module
count = 0
def factory():
# Generate a unique object each time this is called
global count
count += 1
return "Object #%d" % count


# Calling module
from Module import factory
a = factory() # a == "Object #1"
b = factory() # b == "Object #2"
del factory


I'm looking for a way to hide the generation of objects from the caller,
so I could do something like this:

from Module import factory() as a # a == "Object #1"
from Module import factory() as b # b == "Object #2"

except of course that syntax is illegal.

--
Steven

Jonathan Gardner

unread,
Aug 2, 2009, 3:38:22 AM8/2/09
to
On Aug 2, 12:35 am, Steven D'Aprano <st...@REMOVE-THIS-

cybersource.com.au> wrote:
>
> I'm looking for a way to hide the generation of objects from the caller,
> so I could do something like this:
>
> from Module import factory() as a  # a == "Object #1"
> from Module import factory() as b  # b == "Object #2"
>

Explicit is better than implicit. In other words, I don't see why you
need to hide this. Just import the factory function and call it.

Hrvoje Niksic

unread,
Aug 2, 2009, 4:23:51 AM8/2/09
to
Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> writes:

> I'm looking for a way to hide the generation of objects from the caller,
> so I could do something like this:
>
> from Module import factory() as a # a == "Object #1"
> from Module import factory() as b # b == "Object #2"
>
> except of course that syntax is illegal.

That sounds reasonable (zen quotes aside), but it's just not possible in
current Python. It would require for any use of "a" in subsequent code
to not only refer to an object, but also to automagically call it. I
know of no mechanism to do that. While Python allows intricate
customization of access to objects, simply referring to a name remains
fixed in meaning, probably intentionally.

(The "cell" mechanism used to implement closures sounds like it could
help, but a cell only stores a single value, not a factory, and it
doesn't apply to global variables anyway.)

Peter Otten

unread,
Aug 2, 2009, 6:33:03 AM8/2/09
to
Steven D'Aprano wrote:

How about

>>> class A(object):
... def __init__(self):
... self._n = 0
... @property
... def a(self):
... try:
... return self._n
... finally:
... self._n += 1
...
>>> import sys
>>> sys.modules["yadda"] = A()
>>> from yadda import a
>>> from yadda import a as b
>>> a, b
(0, 1)

Peter

Steven D'Aprano

unread,
Aug 2, 2009, 6:49:33 AM8/2/09
to
On Sun, 02 Aug 2009 12:33:03 +0200, Peter Otten wrote:

> How about
[snip]


>>>> import sys
>>>> sys.modules["yadda"] = A()

Perfect! That's exactly the sort of thing I'm looking for.

Thanks for everyone who answered.


--
Steven

Paul Rubin

unread,
Aug 2, 2009, 7:18:34 AM8/2/09
to
Peter Otten <__pet...@web.de> writes:
> >>> import sys
> >>> sys.modules["yadda"] = A()

OMG.... wow. I bow to you. But I'm not sure whether that's bowing in
awe or in terror.

Peter Otten

unread,
Aug 2, 2009, 8:19:01 AM8/2/09
to
wrote:

KNode cannot parse your From-string correctly. Strange.

I don't know who invented it, but it's an old trick. It even made it into
the standard library (email.LazyImporter).

For the record: I don't recommend it.

Peter

Terry Reedy

unread,
Aug 2, 2009, 2:07:44 PM8/2/09
to pytho...@python.org
Peter Otten wrote:
> Steven D'Aprano wrote:

>> I'm looking for a way to hide the generation of objects from the caller,
>> so I could do something like this:
>>
>> from Module import factory() as a # a == "Object #1"
>> from Module import factory() as b # b == "Object #2"
>>
>> except of course that syntax is illegal.
>
> How about

For newbies who do not get how the following works, but would like to
know, I am adding some explanation.

>>>> class A(object):
> ... def __init__(self):
> ... self._n = 0
> ... @property
> ... def a(self):
> ... try:
> ... return self._n
> ... finally:
> ... self._n += 1

The @property decorator turns the 'a' function into the hidden getter
function for what looks to the outside world like a simple instance
attribute named 'a'.

>>>> import sys
>>>> sys.modules["yadda"] = A()

sys.modules is a system namespace that normally associates names with
modules. It is used by import statements to find existing modules.
However, there is no requirement that the associated object actually be
a module. A module is simply a collection of objects accessed as
attributes. One can get and set attributes of a module, and but hardly
anyhing else. Other attribute collection objects will do as well as far
as imports are concerned. 'If it quack like a duck (module in this case)...'

The above sets the 'module' to an *instance* of class A.

>>>> from yadda import a

This looks for the 'module' named 'yadda'. It finds one - the instance
of A. It then requests attribute 'a' (of that instance). That request is
routed to the getter function.

This import statement could be in any module, not just the one that set
A() as a module surrogate.

>>>> from yadda import a as b
>>>> a, b
> (0, 1)

As Peter mentioned in his followup, module surrogates were intended for
lazy imports of expensive-to-compute attributes that might never be
needed, so their creation could be delayed to whenever needed, if ever.

Tricks like the above are not recommended for normal usage, but do
illstrate some aspects of the language.

Terry Jan Reedy

alex23

unread,
Aug 2, 2009, 9:23:39 PM8/2/09
to
On Aug 3, 4:07 am, Terry Reedy <tjre...@udel.edu> wrote:
> Peter Otten wrote:
> > Steven D'Aprano wrote:
[...]

Fantastic question, answer & explanation, guys. Well done.

Jean-Michel Pichavant

unread,
Aug 3, 2009, 10:38:43 AM8/3/09
to Steven D'Aprano, pytho...@python.org
Why making standard statements do what they're not meant to do ?

You could write
>import Module
>
>a = factory()
>b = factory()
But you already know that.


So what's the purpose of making

>from Module import factory as a
>from Module import factory as b

return 2 different objects ? If I had to write this code I would expect 'a is b' to return 'True'.

This is no "don't do that" answer, it's a sincere question: what is the
benefit of your /new/ syntax ?

JM

Steven D'Aprano

unread,
Aug 3, 2009, 11:30:35 PM8/3/09
to
On Mon, 03 Aug 2009 16:38:43 +0200, Jean-Michel Pichavant wrote:


> So what's the purpose of making
>
>>from Module import factory as a
>>from Module import factory as b
>
> return 2 different objects ? If I had to write this code I would expect
> 'a is b' to return 'True'.
>
> This is no "don't do that" answer, it's a sincere question: what is the
> benefit of your /new/ syntax ?


Consider it "properties for modules":

a = obj.factory
b = obj.factory

doesn't promise that a is b, because factory might be a property that
returns a new object each time.

Actually, this behaviour pre-dates properties. If obj has a __getattr__
or __getattribute__ method, then it could do the same thing.


--
Steven

Michele Simionato

unread,
Aug 7, 2009, 12:54:25 AM8/7/09
to
On Aug 2, 1:18 pm, Paul Rubin <http://phr...@NOSPAM.invalid> wrote:

I had to play this kind of tricks on our production code, not so long
ago. Not that I am pride of it, but it was the lesser evil to cope
with a wrong design. The scenario: a large legacy code base
based on the idea of using a Python module to keep configuration
parameters. The idea is fine as long as the parameters are
immutable, but in our case the parameters could be changed.
In theory the parameters should have been set only once,
however in practice this was not guaranteed: every piece
of code could change the parameters at some moment, and things
could get "interesting" to debug.
Throwing away the configuration system was not an option, because
it would have involved changing hundreds of modules, so I set out
for a compromise: the parameters are still mutable, but they
can be changed only once. This was implemented by replacing
the module with a configuration object using custom
descriptors. Here is the code:

$ echo config.py
import sys

class WriteOnce(object):
"WriteOnce descriptor"
def __init__(self, default):
self.default = self.value = default
self.already_set = False
def __get__(self, obj, objcls):
return self.value
def __set__(self, obj, value):
if value != self.value and self.already_set:
raise TypeError('You cannot set twice a write-once
attribute!')
self.value = value
self.already_set = True

class Config(object):
"A singleton to be used as a module"
parameter = WriteOnce(0)

sys.modules['config'] = Config()

The usage is

>>> import config
>>> config.parameter = 42
>>> config.parameter = 43
Traceback (most recent call last):
...
TypeError: You cannot set twice a write-once attribute!

Just to say that there are use cases where replacing modules with
objects may have sense.
Still, a better design would just have passed immutable configuration
objects around
(changing the configuration would mean to create a new object).
Unfortunately there are still a lot a people without a functional
mindset :-(

0 new messages