The usual way to do this is
# Let 'data' be a list or iterable object
# For example, data = [1, 4, 3, 4, 3, 1]
# or, data = sys.stdin
d = {}
for x in data:
d[x] = 1
subset = d.keys()
subset.sort()
# Use 'subset' as needed
Python 2.3 offers at least two new ways to do this. The first is
with the new 'Set' class
# Let 'data' be a list or iterable object
import sets
subset = list(sets.Set(data))
subset.sort()
# Use 'subset' as needed
(The 'list()' is needed because that's the only way to get elements
out from a list. It provides an __iter__ but no 'tolist()' method.)
The other is with the new 'fromkeys' class, which constructs
a dictionary from a list -- the elements of the list become the
keys and you can choose the value for the item, or just use the
default of None. To show you what that means
>>> dict.fromkeys([1,2,5,3,2,1], 0)
{1: 0, 2: 0, 3: 0, 5: 0}
>>> dict.fromkeys([1,2,5,3,2,1])
{1: None, 2: None, 3: None, 5: None}
>>>
So for the task at hand,
# Let 'data' be a list or iterable object
subset = dict.fromkeys(data).keys()
subset.sort()
# Use 'subset' as needed
For a real-life example, suppose you want to get unique lines
from the stdin input stream, sort them, and dump the results
to stdout. Here's how to do it in Python 2.3
import sys
unique_lines = dict.fromkeys(sys.stdin).keys()
unique_lines.sort()
sys.stdout.writelines(unique_lines)
Andrew
da...@dalkescientific.com
Using sets is definitely the Right Way (TM) to do it. This is one of the
primary use cases for sets (*everyone* wants to do this).
> (The 'list()' is needed because that's the only way to get elements
> out from a list. It provides an __iter__ but no 'tolist()' method.)
And this is the canonical way to transform any iterable to a list. Why
should every class that you want to transform to a list have to supply a
`tolist` method? Why not a `totuple` method?
> The other is with the new 'fromkeys' class, which constructs
Actually, dictionary class (static?) method.
> # Let 'data' be a list or iterable object
> subset = dict.fromkeys(data).keys()
> subset.sort()
> # Use 'subset' as needed
This, whilst slightly shorter (due to no import - which in future versions
will be going away anyway), is definitely *not* the Right Way (TM) to do it.
It is likely to confuse people.
> For a real-life example, suppose you want to get unique lines
> from the stdin input stream, sort them, and dump the results
> to stdout. Here's how to do it in Python 2.3
>
> import sys
> unique_lines = dict.fromkeys(sys.stdin).keys()
> unique_lines.sort()
> sys.stdout.writelines(unique_lines)
Nope - this is better done as:
import sets
import sys
unique_lines = list(sets.Set(sys.stdin))
unique_lines.sort()
sys,stdout.writelines(unique_lines)
It says explicitly what you are doing - creating a set of unique *values*
(since that is the definition of a set), the sorting the result.
Tim Delaney
> > The other is with the new 'fromkeys' class, which constructs
>
> Actually, dictionary class (static?) method.
It's a class method.
> > # Let 'data' be a list or iterable object
> > subset = dict.fromkeys(data).keys()
> > subset.sort()
> > # Use 'subset' as needed
>
> This, whilst slightly shorter (due to no import - which in future versions
> will be going away anyway), is definitely *not* the Right Way (TM) to do it.
> It is likely to confuse people.
I don't know. It's currently (apparently) a lot faster than using the
sets module. With the fromkeys() addition dicts are quite comfortable as
poor-man's sets.
Just
- the performance of Sets is slower than that of a simple dict
(because, after all, Sets are built on top of a dict but with
extra overhead). I just tested it -- fromdict is about 20%
faster than using Set
>>> import time, sets, random
>>> data = [random.randrange(1000000) for i in range(2000000)]
>>> def do_set():
... return len(sets.Set(data))
...
>>> def do_dict():
... return len(dict.fromkeys(data).keys())
...
>>> t1=time.clock();do_set();t2=time.clock()
865149
>>> t2-t1
2.9100000000000001
>>> t1=time.clock();do_dict();t2=time.clock()
865149
>>> t2-t1
2.3299999999999983
>>> 2.33/2.9
0.80344827586206902
>>>
- there's the extra import, which is a bit tedious if you don't
need the power of a Set
- using dicts is a basic part of using Python, so the step to using
a different way to construct a dict is easier than thinking
about using a different class
>>(The 'list()' is needed because that's the only way to get elements
>>out from a list. It provides an __iter__ but no 'tolist()' method.)
>
>
> And this is the canonical way to transform any iterable to a list. Why
> should every class that you want to transform to a list have to supply a
> `tolist` method? Why not a `totuple` method?
I put that there as a reminder for fogies like me who even now have
spent more time on pre-2.x version of Python than post-2.x versions.
When I started back in the 1.3 days, there were modules like 'array',
which *did* have a 'tolist' method, and that was the proper way to
do it.
>>> import array
>>> x=array.array("c", "AndreW")
array('c', 'AndreW')
>>> x.tolist()
['A', 'n', 'd', 'r', 'e', 'W']
>>>
The implication that there should be one was not my intention, though
my wording in that regard was unfortunate.
This is also a case where it isn't obvious how to get data from a
container. Every other container spells it through [] or through
a method name which *doesn't* start with a "_". So people just
starting with a Set might not know what to look for.
It would be nice if the example code showed iterating data from
a Set...
>>The other is with the new 'fromkeys' class, which constructs
>
>
> Actually, dictionary class (static?) method.
Yep. Meant to say "class method". Just didn't get through my
fingers.
> This, whilst slightly shorter (due to no import - which in future versions
> will be going away anyway), is definitely *not* the Right Way (TM) to do it.
> It is likely to confuse people.
It will? Given how much pre-2.3 code uses the "build a dict then
get the keys" to get the unique values in a data set, it's an idiom
that any intermediate Python programmer should understand and expect
to understand.
As for beginning Python programmers, I can't put myself into their
shoes.
My feeling for now is that I'll use "Set" when I want to do set
manipulations, like
set1 = { identifiers matching query 1}
set2 = { identifiers matching query 2}
total = set1 + set2
and not use it for getting unique values.
Andrew
da...@dalkescientific.com
subset = [x for x in sets.Set(data)]
to create the list you want to sort? I'm not convinced this is a win,
though, so timings might be interesting.
>
> The other is with the new 'fromkeys' class, which constructs
> a dictionary from a list -- the elements of the list become the
> keys and you can choose the value for the item, or just use the
> default of None. To show you what that means
>
> >>> dict.fromkeys([1,2,5,3,2,1], 0)
> {1: 0, 2: 0, 3: 0, 5: 0}
> >>> dict.fromkeys([1,2,5,3,2,1])
> {1: None, 2: None, 3: None, 5: None}
> >>>
>
> So for the task at hand,
>
> # Let 'data' be a list or iterable object
> subset = dict.fromkeys(data).keys()
> subset.sort()
> # Use 'subset' as needed
>
>
> For a real-life example, suppose you want to get unique lines
> from the stdin input stream, sort them, and dump the results
> to stdout. Here's how to do it in Python 2.3
>
> import sys
> unique_lines = dict.fromkeys(sys.stdin).keys()
> unique_lines.sort()
> sys.stdout.writelines(unique_lines)
>
Of course, becuase your specification contains "in sorted order" you must
apply the sort() function at some stage, but the more interesting aspect of
this post is the new ways to get at the unique elements.
When sorting isn't required, of course, it should be perfectly possible to
simply write:
for element in sets.Set(data):
do_stuff_with(element)
and no list conversion is necessary since the iterator does what it's
supposed to (i.e.: support iteration :-)
regards
-----------------------------------------------------------------------
Steve Holden http://www.holdenweb.com/
Python Web Programming http://pydish.holdenweb.com/pwp/
Bring your musical instrument to PyCon! http://www.python.org/pycon/
-----------------------------------------------------------------------
> Presumably (I don't have 2.3 to hand, so I may get shot down in flames)
> since a set has __iter__() one could also use
>
> subset = [x for x in sets.Set(data)]
>
> to create the list you want to sort? I'm not convinced this is a win,
> though, so timings might be interesting.
list(iterable) is the canonical way to get a list from an iterable.
Using a list comp doesn't look like a win to me at all... It definitely
won't be a win speed-wise, as list() iterates in C and any list comp
involves Python code.
Just
Yes. That's actually what I started off doing until I remembered
that 'list' takes an iterator in the constructor. (All them new-fangled
things these days!)
> When sorting isn't required, of course, it should be perfectly possible to
> simply write:
>
> for element in sets.Set(data):
> do_stuff_with(element)
>
> and no list conversion is necessary since the iterator does what it's
> supposed to (i.e.: support iteration :-)
And for that specific case
map(do_stuff_with, sets.Set(data))
That gets to a level of terseness which makes my head hurt a bit. :)
Andrew
da...@dalkescientific.com
Hi Andrew,
Im using Python 2.2.2 and without the 2.3. - features the
following seems to work fine:
>>> input=[5, 3, 1, 2, 5, 4, 3, 4, 1, 1, 5, 4, 5, 1, 4, 3, 2, 2, 4, 1]
>>> output=dict( zip(input ,range(len(input)) ) ).keys()
>>> output
[1, 2, 3, 4, 5]
What Im wondering about is the fact that in several examples
even without the statment:
>>> output.sort()
I always got a sorted output.
Cheers Peter
My guess is it's because the hash function used for integers is
the integer itself, so the hash values are 1, 2, 3, 4. These then
get put into slot 1, 2, 3, and 4, which are then visited in that
order to get the values.
Put a -1 in the list. You'll get
[1, 2, 3, 4, 5, -1]
I think the initial hash size is about 8 elements
>>> for i in range(10000):
... assert dict(zip(range(i, i+5), range(i, i+5))).keys() ==
range(i, i+5)
...
Traceback (most recent call last):
File "<stdin>", line 2, in ?
AssertionError
>>> i
4
>>>
>>> for i in range(10000):
... assert dict(zip(range(i, i+2), range(i, i+2))).keys() ==
range(i, i+2)
...
Traceback (most recent call last):
File "<stdin>", line 2, in ?
AssertionError
>>> i
7
>>>
that is,
>>> dict(zip((7, 9), (0, 0))).keys()
[9, 7]
>>> dict(zip((7, 8), (0, 0))).keys()
[8, 7]
>>> dict(zip((6, 7), (0, 0))).keys()
[6, 7]
>>>
BTW, this would have been a bit faster and used less memory
>>> output=dict( zip(input , (0,)* len(input)) ).keys()
|-----------------------------------|
same as dict.from_keys(input, 0) in Python 2.3
Andrew
Andrew
Peter> I always got a sorted output.
You just got lucky. Perturb your input list a tad:
>>> input=[5, -1, 1, 2, 5, 4, 3, 4, 1, 1, 5, 4, 5, 1, 4, 3, 2, 2, 4, 1]
>>> output=dict( zip(input ,range(len(input)) ) ).keys()
>>> output
[1, 2, 3, 4, 5, -1]
Skip
An quick example:
-------------------------
class spam:
__init__(self):
self.stuff = eggs()
self.changed = 0
class eggs:
def do_stuff(self):
# after we've done the main part of our work,
# signal parent object that information has changed
self.__some_namespace_voodoo__.changed = 1
return something_unrelated
a = spam()
b = spam()
x = a.stuff.do_stuff() # <--- should set value of a.changed to 1
x = b.stuff.do_stuff() # <--- should set value of b.changed to 1
-------------------------
If this looks like manipulation of the _p_changed attribute in ZODB, it's
not a coincidence... that's what I'm hoping to use this for. I realize I
could pull a reference to a from globals() assuming I know the parent class
is called "a" or "b", but I'm hoping there's a way to go about this without
storing the parent name in every sub-object.
Any pointers and/or nudges in the right direction will be greatly appreciated.
Thanks,
Dylan
I doubt that this is currently possible to do correctly. It would
be a bit messy at least. you can split your problem:
1. getting to the callers 'context' which is represented by a
Frame object. This is easy and the inspect method offers
a way to do this (inspect.getcurrentframe(1)).
2. find an instance (if any) on which you want to modify attributes.
And here it gets very difficult. How would you find it?
What if the actual instance just triggered a function
doing the actual call?
Suppose we just want to get "the" instance from the last callers
execution context. Even then it is kind of hard to get to
the instance. Or to a bound instance function. I don't think
you can simply get to a non-instance function starting from a
frame object.
So the answer is: you can't do anything like this without
defining exact semantics and then doing some "interesting"
hacks to approximately get that.
holger
If I'm understanding you correctly, you have a "spam" instance, which
contains an "eggs" instance, and you want changes to the eggs instance to
persisted.
Why don't you simply make eggs inherit from Persistent, and let it set its
own _p_changed flag?
-Andrew.
>So the answer is: you can't do anything like this without
>defining exact semantics and then doing some "interesting"
>hacks to approximately get that.
>
> holger
Thanks... at least now I don't feel so bad for not figuring it out on my
own. :-)
But that gives me some stuff to look further into.
Dylan
A fair question. What the example obscures is that spam is, itself, a
subobject of a object that uses Persistent. Or does that not matter?
A related issue is that I also want the subobjects to obtain/acquire
settings from their containing object. Seems the same trick to do one
should do the other, but _p_changed won't do double duty here.
Dylan
Yes, exactly.
> To which my response is: What is
>
>c = eggs()
>c.do_stuff()
>
>supposed to be changing? The global namespace?
I don't think so... but if that gets me where I need to go, I'm all ears. :-)
>class eggs(spam):
> def __init__(self):
> spam.__init__(self)
>
> def do_stuff(self):
> self.changed = 1
> ...
I doubt inheritance will do the trick here. Spam isn't a base class, it
defines a container object.
To recap/clarify:
I have several instances of spam, each of which contains (let's say) many
thousands of instances of eggs. I want for changes made to a single
instance of eggs to be made known to the specific instance of spam that
contains it. Similarly, I want for any instance of eggs to be able to get
the value of any attribute of its containing spam object.
To put this more generally, I want a subobject to find its containing
object so that methods of the containing object can be called and
attributes of the containing object can be set/checked. I gather there's no
easy, pre-defined way to do this, so any namespace voodoo that can do the
trick would be most welcome.
Thanks,
Dylan
If spam is also a subclass of Persistent, then it should all Just Work
(assuming you are setting _p_changed when necessary :)
> A related issue is that I also want the subobjects to obtain/acquire
> settings from their containing object. Seems the same trick to do one
> should do the other, but _p_changed won't do double duty here.
For this you probably want to use Acquisition.Explicit or
Acquistion.Implicit, but this is essentially unrelated to persistence.
-Andrew.
>At 09:00 PM 1/18/2003, Dennis Lee Bieber wrote:
>> My understanding was that he wants changes /in/ the eggs instance to
>>affect the "changed" attribute of the spam instance.
>
>Yes, exactly.
>
>> To which my response is: What is
>>
>>c = eggs()
>>c.do_stuff()
>>
>>supposed to be changing? The global namespace?
>
>I don't think so... but if that gets me where I need to go, I'm all ears. :-)
>
>>class eggs(spam):
>> def __init__(self):
>> spam.__init__(self)
>>
>> def do_stuff(self):
>> self.changed = 1
>> ...
>
>I doubt inheritance will do the trick here. Spam isn't a base class, it
>defines a container object.
>
>To recap/clarify:
>
Why not write minimal code to illustrate?
>I have several instances of spam, each of which contains (let's say) many
several_instances_of_spam = [Spam() for i in range(several)]
>thousands of instances of eggs. I want for changes made to a single
for spam_instance in serveral_instances_of_spam:
for i in xrange(thousands):
spam_instance.put_inside_spam(Eggs())
>instance of eggs to be made known to the specific instance of spam that
>contains it. Similarly, I want for any instance of eggs to be able to get
>the value of any attribute of its containing spam object.
several_instances_of_spam[an_index_less_than_several].get_contained_egg(
index_less_than_thousands).changed_attr = some_change
>
>To put this more generally, I want a subobject to find its containing
What is your concept of "subobject"? If you set a = [b] is b a subobject
of a? I.e., simple binding of a reference in any manner into some part
of an object makes the referred-to object a subobject?
>object so that methods of the containing object can be called and
>attributes of the containing object can be set/checked. I gather there's no
see [1] below
>easy, pre-defined way to do this, so any namespace voodoo that can do the
>trick would be most welcome.
>
Sure, there's any number of ways to do it. But to choose one, we need to know
how well/badly the above code reflects your problem world.
How are all the objects created? And how is an Egg instance made a subobject of
a Spam instance? I hypothesized that Spam objects would have a method to stuff
Egg instances into them serially, so the inside collection is implicitly known
by number. I.e, the statement from above that creates and stuffs new eggs into
spam instances is
spam_instance.put_inside_spam(Eggs())
You notice I didn't even pass anything to the Egg constructor, but that would
be a place you could tell an egg what spam instance it was being put into, not
necessarily a direct reference, but you could, though it would create reference
cycles that you should manage carefully. E.g., if the Egg class were
class Egg:
def __init__(self, my_container):
self.my_container = my_container
then
spam_instance.put_inside_spam(Eggs(spam_instance))
would let any egg method reach the enclosing spam container via
self.my_container
[1] and thus
self.my_container.method_of_containing_object()
and
self.my_container.attribute_of_containing_object = value_to_set_check
would do what you wanted. If self.my_container was not a reference, but an index,
you might use it like
several_instances_of_spam = [Spam() for i in range(several)]
for spam_index in xrange(len(several_instances_of_spam):
spam_instance = several_instances_of_spam[spam_index]
for i in xrange(thousands):
spam_instance.put_inside_spam(Eggs(spam_index))
and then assuming several_instances_of_spam is global,
several_instances_of_spam[self.my_container].method_of_containing_object()
and
several_instances_of_spam[self.my_container].attribute_of_containing_object = value_to_set_check
If you are in control over everything, there are going to be better ways, but it's
not clear whether you are trying to tweak some big stuff you don't want to bother fully
understanding, or whether you have creative control. Either way has its difficulties, but
the latter is usually more fun, unless you run up a blind alley with a deadline and no ideas ;-)
Regards,
Bengt Richter
if you want a contained object to at some point know about its
container then you should pass the contained object an explicit
reference of its container. This is simple.
Otherwise, I recommend that you try to come up with a small real
world example.
holger
My first post had this minimal code example:
-------------------------
class spam:
__init__(self):
self.stuff = eggs()
self.changed = 0
class eggs:
def do_stuff(self):
# after we've done the main part of our work,
# signal parent object that information has changed
self.__some_namespace_voodoo__.changed = 1
return something_unrelated
a = spam()
b = spam()
x = a.stuff.do_stuff() # <--- should set value of a.changed to 1
x = b.stuff.do_stuff() # <--- should set value of b.changed to 1
-------------------------
>several_instances_of_spam[an_index_less_than_several].get_contained_egg(
> index_less_than_thousands).changed_attr = some_change
The whole point of doing it the way I'm doing it is to provide a clean,
Pythonic interface, thus:
spam_instance.egg_instance.egg_method()
should have a side effect on the spam instance.
>What is your concept of "subobject"?
Hopefully this should be clear from the code example.
>Sure, there's any number of ways to do it. But to choose one, we need to know
>how well/badly the above code reflects your problem world.
Does the provided code do it?
> class Egg:
> def __init__(self, my_container):
> self.my_container = my_container
This is actually what I'm doing now. If this is the best available answer,
I can certainly live with that, but it been surprised at what some of the
namespace wizards here have come up with in the past, so I thought I'd see
if there is a more elegant/efficient answer.
>If you are in control over everything, there are going to be better ways,
>but it's
>not clear whether you are trying to tweak some big stuff you don't want to
>bother fully
>understanding, or whether you have creative control.
The latter. I'm an experienced developer working on code I have total
control over.
Thanks in advance,
Dylan
Oh... duh. That's exactly what I want to do. :-)
Funny, ExtensionClass/Acquisition was the inspiration for the behavior I
want, but somehow I didn't think to use them.
Thanks for the push in the right direction.
Dylan
Judging from what you've said earlier, the most elegant way to solve your
specific problem would be to have both Egg and Spam inherit from Persistent,
and let them both manage their own _p_changed flags. Then there is no need
to pass parent references around, or other namespace tricks -- ZODB will
take care of it for you.
Is there some reason you can't do this?
-Andrew.
What I want is a reference to the parent object... nothing more, nothing
less. I need my subobjects not only to set values in their parent, but to
acquire values as well.
Looks like I'll be using ExtensionClass & Acquisition since exactly what it
does.
Thanks for your help,
Dylan
>At 03:41 AM 1/19/2003, Bengt Richter wrote:
>>Why not write minimal code to illustrate?
>
>
>My first post had this minimal code example:
>
Ok, sorry for my comment.
>
>-------------------------
>
>class spam:
> __init__(self):
> self.stuff = eggs()
> self.changed = 0
>
>class eggs:
> def do_stuff(self):
> # after we've done the main part of our work,
> # signal parent object that information has changed
> self.__some_namespace_voodoo__.changed = 1
> return something_unrelated
>
>a = spam()
>b = spam()
>x = a.stuff.do_stuff() # <--- should set value of a.changed to 1
>x = b.stuff.do_stuff() # <--- should set value of b.changed to 1
>
>-------------------------
>
>
>>several_instances_of_spam[an_index_less_than_several].get_contained_egg(
>> index_less_than_thousands).changed_attr = some_change
>
>
>The whole point of doing it the way I'm doing it is to provide a clean,
>Pythonic interface, thus:
>
>spam_instance.egg_instance.egg_method()
>
>should have a side effect on the spam instance.
>
I presume you mean "should" as a requirement of your problem, not
"should because a spam has a reference in it to an egg (BTW, uppercasing
the class name's first letter makes it so we can write Spam and spam
for short instead of spam and spam_instance ;-)
If you made Egg inherit from Spam, there would be a natural "should"
implemented in the attribute access machinery's search sequence. E.g.,
since stuff is an attribute of Spam, you could monitor its use and
though it's not going to be near as fast as direct references, it might
be interesting to file for alternative ideas ... well, easier to show
than talk about ;-)
====< spameggs.py >========================================
class Spam(object):
stuff_monitoring_list = []
def _set_changed(self, v): self._changed = v
def _get_changed(self):
if self.__class__ is Spam:
for egg in self.stuff_monitoring_list:
self._changed = self._changed or egg.changed
self.stuff_monitoring_list = []
return self._changed
changed = property(_get_changed, _set_changed)
def _set_stuff(self, v): self._stuff = v
def _get_stuff(self):
self.stuff_monitoring_list.append(self._stuff)
return self._stuff
stuff = property(_get_stuff, _set_stuff)
def __init__(self):
self.stuff = Eggs()
self.changed = 0
class Eggs(Spam):
def __init__(self): pass # must define or we get recursion via self.stuff above
def do_stuff(self):
# after we've done the main part of our work,
# signal parent object that information has changed
## self.__some_namespace_voodoo__.changed = 1
self.changed = 1 # should find property in parent class
return 'something_unrelated' ## string, to avoid name error here
if __name__ == '__main__':
a = Spam()
b = Spam()
print 'a=%s a.changed=%s' % (a, a.changed)
print 'b=%s b.changed=%s' % (b, b.changed)
print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
x = a.stuff.do_stuff() # <--- should set value of a.changed to 1
print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
print 'a._changed = %s, a._stuff._changed = %s' % (a._changed, a._stuff._changed)
print 'a.changed = %s' % a.changed
print 'a.stuff_monitoring_list = %s' % a.stuff_monitoring_list
print 'a._changed = %s, a._stuff._changed = %s' % (a._changed, a._stuff._changed)
print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
x = b.stuff.do_stuff() # <--- should set value of b.changed to 1
print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
print 'b.changed = %s' % b.changed
print 'b.stuff_monitoring_list = %s' % b.stuff_monitoring_list
===========================================================
If you run it, you get:
[19:14] C:\pywk\clp>spameggs.py
a=<__main__.Spam object at 0x007E0F70> a.changed=0
b=<__main__.Spam object at 0x007E0570> b.changed=0
a.stuff_monitoring_list = []
a.stuff_monitoring_list = [<__main__.Eggs object at 0x007E0F40>]
a._changed = 0, a._stuff._changed = 1
a.changed = 1
a.stuff_monitoring_list = []
a._changed = 1, a._stuff._changed = 1
b.stuff_monitoring_list = []
b.stuff_monitoring_list = [<__main__.Eggs object at 0x007E0540>]
b.changed = 1
b.stuff_monitoring_list = []
>>What is your concept of "subobject"?
>
>Hopefully this should be clear from the code example.
>
>>Sure, there's any number of ways to do it. But to choose one, we need to know
>>how well/badly the above code reflects your problem world.
>
>Does the provided code do it?
>
>> class Egg:
>> def __init__(self, my_container):
>> self.my_container = my_container
>
>
>This is actually what I'm doing now. If this is the best available answer,
>I can certainly live with that, but it been surprised at what some of the
>namespace wizards here have come up with in the past, so I thought I'd see
>if there is a more elegant/efficient answer.
>
I doubt if you could get more efficient than a direct reference, but the property
stuff might be interesting. Note that the Spam and Egg ._changed attributes attach
to Spam and Egg instances respectively, but .changed goes through the property whether
inherited or not -- but the accessor methods see different self instances. An egg
instance still doesn't know about its "parent" container instance, but the Egg class
does Know about the base Spam class, and an instance of either can find a Spam class
variable like the stuff_monitoring_list.
Of course, a reference in that list keeps an egg instance alive, so if that is a
consideration, you might want to look into weak references, which would allow eggs
to be deleted and their deletion later noticed via the weak references.
>
>>If you are in control over everything, there are going to be better ways,
>>but it's
>>not clear whether you are trying to tweak some big stuff you don't want to
>>bother fully
>>understanding, or whether you have creative control.
>
>The latter. I'm an experienced developer working on code I have total
>control over.
>
>Thanks in advance,
>
Always fun to explore ;-)
Regards,
Bengt Richter