Does circular reference between two objects requires the use of weakref? - python

I am trying to implement something that works on the principle below:
from weakref import WeakValueDictionary
class Container(object):
def __init__(self):
self.dic = WeakValueDictionary({})
def put_in(self, something):
self.dic[something] = Thing(self, something)
class Thing(object):
def __init__(self, container, name):
self.container = container
self.name = name
def what_I_am(self):
print("I am a thing called {}".format(self.name))
pot = Container()
pot.put_in('foo')
pot.dic['foo'].what_I_am()
But I get :
File "C:/Users/jacques/ownCloud/dev/weakref.py", line 26, in <module>
pot.dic['foo'].what_I_am()
File "C:\Program Files\Anaconda3\lib\weakref.py", line 131, in __getitem__
o = self.data[key]()
KeyError: 'foo'
I understand that my implementation is not correct because Thing instance gets GCed and deleted from the WeakValueDictionary.
Is there any way I could achieve something like this to prevent the circular reference between Container and Thing ?
Edit : If I change the code above for the one below, would it solve the circular reference issue ?
from weakref import proxy
class Container(dict):
def put_in(self, something):
self[something] = Thing(self)
class Thing(object):
def __init__(self, container):
self.container = proxy(container)
def what_is_it(self):
print("I am a thing called {}".format(self))
def __getattr__(self, name):
try: #Look up the Thing instance first
return object.__getattribute__(self, name)
except AttributeError: #Try to find the attribute in container
return self.container.__getattribute__(name)
def __format__(self, spec):
(name,) = (key for key, val in self.container.items() if self == val)
return name
pot = Container()
pot.location = 'Living room'
pot.put_in('foo')
pot['foo'].what_is_it()
print(pot['foo'].location)

The very point of WeakValueDictionary is that its keys are automatically deleted once the objects are no longer in use.
Immediately after
self.dic[thing] = Thing(self)
there is no reference to the Thing object outside of the WeakValueDictionary anymore, so the behavior you are seeing is correct and expected.
If you expect the key to be reachable, replace WeakValueDictionary with the regular dict. Alternatively, make sure there is a reference to the thing, for instance by returning it or referencing it somewhere else.

You do not need to worry about circular references. Python is fully capable of managing its own memory in this case. And will delete objects with circular references as and when necessary.
Your implemenation need only look like this:
class Container(dict):
def put_in(self, something):
self[something] = Thing(self, something)
class Thing:
def __init__(self, container, name):
self.container = container
self.name = name
def what_is_it(self):
assert self.container[self.name] is self, "Thing stored under wrong name"
print("I am a thing called {}".format(self.name))
def __getattr__(self, name):
# By the time __getattr__ is called, normal attribute access on Thing has
# already failed. So, no need to check again. Go straight to checking the
# container
try:
return getattr(self.container, name)
except AttributeError:
# raise a fresh attribute error to make it clearer that the
# attribute was initially accessed on a Thing object
raise AttributeError("'Thing' object has no attribute {!r}".format(name)) from e
A quick test to show you how things work:
c = Container()
c.put_in("test")
c.value = 0
# Attribute demonstration
c["test"].what_is_it()
t = c["test"]
print("name:", t.name) # get a Thing attribute
print("value:", t.value) # get a Container Attribute
c.name = "another name"
print("Thing name:" t.name) # gets Thing attrs in preference to Container attrs
# Garbage collection demonstration
import weakref
import gc
r = weakref.ref(c["test"])
del c, t
# no non-weak references to t exist anymore
print(r()) # but Thing object not deleted yet
# collecting circular references is non-trivial so Python does this infrequently
gc.collect() # force a collection
print(r()) # Thing object has now been deleted

Related

How to reference an existing class object with no defined variable?

I'm trying to use a function of a class object to create a new class object and running into problems. Here's the code I have so far:
class Room(object):
def __init__(self, name):
self.name = name
self.N = None
self.E = None
self.S = None
self.W = None
'''relevant code'''
def north(self,room):
self.N = Room(room)
self.N.S = self
def south(self,room):
self.S = Room(room)
self.S.N = self
So I want at least one of these print statements
room1 = Room('room1')
room1.north('room2')
print(room2.S)
print(Room(room2).S)
print(Room('room2').S)
to spit out 'room1', but the first two don't work because room2 as a variable is yet to be defined, and the last one doesn't work because it seems to be creating a new object instead of referencing the existing one, so it just prints the default 'None'.
Does there actually exist a way to reference an existing object with no variable set to it? Or is my only option to do something like this?
def north(self,room):
roomDict[room] = Room(room)
self.N = roomDict[room]
self.N.S = self
Edit: I realize I should probably be calling the new Room's south() function instead of directly changing the S variable, but that seems intuitively like it would cause a loop so I haven't touched it yet.
* Edited based on OP's clarification *
If you have a large number of objects you want to refer to without binding them to variables, dict is the way to go.
You can use #Berci's solution. But note that with that solution, if you already have a room named foo, you can't overwrite it by simply calling Room('foo') again -- doing that will just return the original foo room. To overwrite an existing room you must first do del Room.roomDict['foo'], and then call Room('foo'). This may be something you want, but maybe not.
The implementation below is less fanciful and doesn't require __new__ (in fact, Berci's solution doesn't need __new__ either and can be all done in __init__):
class Room:
registry = {}
def __init__(self, name):
self.registry[name] = self
# the rest of your __init__ code
If you want rooms to be non-overwritable, as they are in Berci's solution, just add two lines:
class Room:
registry = {}
def __init__(self, name):
if name in self.registry:
raise ValueError('room named "{}" already exists'.format(name))
self.registry[name] = self
It's not necessary to nest registry inside Room. You can make it an external dict if you want. The advantage of having the registry as a class attribute is that your Room object can access it as self.registry without knowing its global name. The (slight) disadvantage is that you need to type Room.registry or someroom.registry instead of just, say, registry, every time you access it.
Your dict solution can be brought to work. Use a class level roomDict and a new constructor not to create an already existing object referred by its name:
class Room(object):
roomDict = {}
def __new__(cls, name):
if name in cls.roomDict:
return cls.roomDict[name]
self = object.__new__(cls, name) # here the object is created
cls.roomDict[name] = self
return self
def __init__(self, name):
...
So that you can refer to room2 as Room('room2') afterwards.

Accessing a full chain of attribute calls in python

is there a way to intercept chains of attribute calls?
What I mean is the following:
myobj.attr1.attr2.{...}.attrN
Can I somehow intercept the whole chain within myobj?
What I have in mind is a function that gets access to the full chain 'attr1.{...}.attrN' at once, for example as a string. Is there a way to establish this?
So far, I have a rather hacky solution where getatrr creates novel objects whose getattr also creates novel objects and thereby the whole attribute chain is recorded as a string (below in fullname). But I don't like this solution at all since this has several drawbacks especially when it comes to pickling and error handling.
class NameCollector(object):
''' A helper class that resolves natural naming
'''
def __init__(self,myobj,name='',parent_name=''):
self._myobj=myobj
if name == '':
self._fullname = ''
elif parent_name == '':
self._fullname = name
else:
self._fullname = parent_name + '.' + name
def __getattr__(self,name):
new_collector = NameCollector(self._myobj,name,self._fullname, self._regexp)
if name in self._myobj._leaves:
return self._myobj._do_stuff_with_chained_attribute_names(new_collector)
return new_collector
Cheers and thanks a lot,
Robert
One solution could be to create a new object on the first access a.b, and then every subsequent access, just update the object's internal structure (eg. a list of values), and add a __str__ and __repr__ method.
class NameCollector(object):
def __init__(self, parent, attr):
self.parent = parent
self.structure = [attr]
def __getattr__(self, attr):
if attr in self.__dict__: return attr
self.structure.append(attr)
return self
def __str__(self):
retval = ""
for item in self.structure:
retval += str(getattr(self.parent, item)) + "."
return retval[:-1] # Remove the last '.'
class Foo(object):
def __getattr__(self, attr): return NameCollector(self, attr)

How to lazy load a data structure (python)

I have some way of building a data structure (out of some file contents, say):
def loadfile(FILE):
return # some data structure created from the contents of FILE
So I can do things like
puppies = loadfile("puppies.csv") # wait for loadfile to work
kitties = loadfile("kitties.csv") # wait some more
print len(puppies)
print puppies[32]
In the above example, I wasted a bunch of time actually reading kitties.csv and creating a data structure that I never used. I'd like to avoid that waste without constantly checking if not kitties whenever I want to do something. I'd like to be able to do
puppies = lazyload("puppies.csv") # instant
kitties = lazyload("kitties.csv") # instant
print len(puppies) # wait for loadfile
print puppies[32]
So if I don't ever try to do anything with kitties, loadfile("kitties.csv") never gets called.
Is there some standard way to do this?
After playing around with it for a bit, I produced the following solution, which appears to work correctly and is quite brief. Are there some alternatives? Are there drawbacks to using this approach that I should keep in mind?
class lazyload:
def __init__(self,FILE):
self.FILE = FILE
self.F = None
def __getattr__(self,name):
if not self.F:
print "loading %s" % self.FILE
self.F = loadfile(self.FILE)
return object.__getattribute__(self.F, name)
What might be even better is if something like this worked:
class lazyload:
def __init__(self,FILE):
self.FILE = FILE
def __getattr__(self,name):
self = loadfile(self.FILE) # this never gets called again
# since self is no longer a
# lazyload instance
return object.__getattribute__(self, name)
But this doesn't work because self is local. It actually ends up calling loadfile every time you do anything.
The csv module in the Python stdlibrary will not load the data until you start iterating over it, so it is in fact lazy.
Edit: If you need to read through the whole file to build the datastructure, having a complex Lazy load object that proxies things is overkill. Just do this:
class Lazywrapper(object):
def __init__(self, filename):
self.filename = filename
self._data = None
def get_data(self):
if self._data = None:
self._build_data()
return self._data
def _build_data(self):
# Now open and iterate over the file to build a datastructure, and
# put that datastructure as self._data
With the above class you can do this:
puppies = Lazywrapper("puppies.csv") # Instant
kitties = Lazywrapper("kitties.csv") # Instant
print len(puppies.getdata()) # Wait
print puppies.getdata()[32] # instant
Also
allkitties = kitties.get_data() # wait
print len(allkitties)
print kitties[32]
If you have a lot of data, and you don't really need to load all the data you could also implement something like class that will read the file until it finds the doggie called "Froufrou" and then stop, but at that point it's likely better to stick the data in a database once and for all and access it from there.
If you're really worried about the if statement, you have a Stateful object.
from collections import MutableMapping
class LazyLoad( MutableMapping ):
def __init__( self, source ):
self.source= source
self.process= LoadMe( self )
self.data= None
def __getitem__( self, key ):
self.process= self.process.load()
return self.data[key]
def __setitem__( self, key, value ):
self.process= self.process.load()
self.data[key]= value
def __contains__( self, key ):
self.process= self.process.load()
return key in self.data
This class delegates the work to a process object which is either a Load or a
DoneLoading object. The Load object will actually load. The DoneLoading
will not load.
Note that there are no if-statements.
class LoadMe( object ):
def __init__( self, parent ):
self.parent= parent
def load( self ):
## Actually load, setting self.parent.data
return DoneLoading( self.parent )
class DoneLoading( object ):
def __init__( self, parent ):
self.parent= parent
def load( self ):
return self
Wouldn't if not self.F lead to another call to __getattr__, putting you into an infinite loop? I think your approach makes sense, but to be on the safe side, I'd make that line into:
if name == "F" and not self.F:
Also, you could make loadfile a method on the class, depending on what you're doing.
Here's a solution that uses a class decorator to defer initialisation until the first time an object is used:
def lazyload(cls):
original_init = cls.__init__
original_getattribute = cls.__getattribute__
def newinit(self, *args, **kwargs):
# Just cache the arguments for the eventual initialization.
self._init_args = args
self._init_kwargs = kwargs
self.initialized = False
newinit.__doc__ = original_init.__doc__
def performinit(self):
# We call object's __getattribute__ rather than super(...).__getattribute__
# or original_getattribute so that no custom __getattribute__ implementations
# can interfere with what we are doing.
original_init(self,
*object.__getattribute__(self, "_init_args"),
**object.__getattribute__(self, "_init_kwargs"))
del self._init_args
del self._init_kwargs
self.initialized = True
def newgetattribute(self, name):
if not object.__getattribute__(self, "initialized"):
performinit(self)
return original_getattribute(self, name)
if hasattr(cls, "__getitem__"):
original_getitem = cls.__getitem__
def newgetitem(self, key):
if not object.__getattribute__(self, "initialized"):
performinit(self)
return original_getitem(self, key)
newgetitem.__doc__ = original_getitem.__doc__
cls.__getitem__ = newgetitem
if hasattr(cls, "__len__"):
original_len = cls.__len__
def newlen(self):
if not object.__getattribute__(self, "initialized"):
performinit(self)
return original_len(self)
newlen.__doc__ = original_len.__doc__
cls.__len__ = newlen
cls.__init__ = newinit
cls.__getattribute__ = newgetattribute
return cls
#lazyload
class FileLoader(dict):
def __init__(self, filename):
self.filename = filename
print "Performing expensive load operation"
self[32] = "Felix"
self[33] = "Eeek"
kittens = FileLoader("kitties.csv")
print "kittens is instance of FileLoader: %s" % isinstance(kittens, FileLoader) # Well obviously
print len(kittens) # Wait
print kittens[32] # No wait
print kittens[33] # No wait
print kittens.filename # Still no wait
print kittens.filename
The output:
kittens is instance of FileLoader: True
Performing expensive load operation
2
Felix
Eeek
kitties.csv
kitties.csv
I tried to actually restore the original magic methods after the initialization, but it wasn't working out. It may be necessary to proxy additional magic methods, I didn't investigate every scenario.
Note that kittens.initialized will always return True because it kicks off the initialization if it hasn't already been performed. Obviously it would be possible to add an exemption for this attribute so that it would return False if no other operation had been performed on the object, or the check could be changed to the equivalent of a hasattr call and the initialized attribute could be deleted after the initialization.
Here's a hack that makes the "even better" solution work, but I think it's annoying enough that it's probably better to just use the first solution. The idea is to execute the step self = loadfile(self.FILE) by passing the the variable name as an attribute:
class lazyload:
def __init__(self,FILE,var):
self.FILE = FILE
self.var = var
def __getattr__(self,name):
x = loadfile(self.FILE)
globals()[self.var]=x
return object.__getattribute__(x, name)
Then you can do
kitties = lazyload("kitties.csv","kitties")
^ ^
\ /
These two better match exactly
After you call any method on kitties (aside from kitties.FILE or kitties.var), it will become completely indistinguishable from what you'd have gotten with kitties = loadfile("kitties.csv"). In particular, it will no longer be an instance of lazyload and kitties.FILE and kitties.var will no longer exist.
If you need use puppies[32] you need also define __getitem__ method because __getattr__ don't catch that behaviour.
I implement lazy load for my needs, there is non-adapted code:
class lazy_mask(object):
'''Fake object, which is substituted in
place of masked object'''
def __init__(self, master, id):
self.master=master
self.id=id
self._result=None
self.master.add(self)
def _res(self):
'''Run lazy job'''
if not self._result:
self._result=self.master.get(self.id)
return self._result
def __getattribute__(self, name):
'''proxy all queries to masked object'''
name=name.replace('_lazy_mask', '')
#print 'attr', name
if name in ['_result', '_res', 'master', 'id']:#don't proxy requests for own properties
return super(lazy_mask, self).__getattribute__(name)
else:#but proxy requests for masked object
return self._res().__getattribute__(name)
def __getitem__(self, key):
'''provide object["key"] access. Else can raise
TypeError: 'lazy_mask' object is unsubscriptable'''
return self._res().__getitem__(key)
(master is registry object that load data when i run it's get() method)
This implementation works ok for isinstance() and str() and json.dumps() with it

How to create instances of a class from a static method?

Here is my problem. I have created a pretty heavy readonly class making many database calls with a static "factory" method. The goal of this method is to avoid killing the database by looking in a pool of already-created objects if an identical instance of the same object (same type, same init parameters) already exists.
If something was found, the method will just return it. No problem. But if not, how may I create an instance of the object, in a way that works with inheritance?
>>> class A(Object):
>>> #classmethod
>>> def get_cached_obj(self, some_identifier):
>>> # Should do something like `return A(idenfier)`, but in a way that works
>>> class B(A):
>>> pass
>>> A.get_cached_obj('foo') # Should do the same as A('foo')
>>> A().get_cached_obj('foo') # Should do the same as A('foo')
>>> B.get_cached_obj('bar') # Should do the same as B('bar')
>>> B().get_cached_obj('bar') # Should do the same as B('bar')
Thanks.
import weakref
class A(object):
_get_obj_cache = weakref.WeakValueDictionary()
#classmethod
def get_obj(cls, identifier):
cache = cls._get_obj_cache
obj = cache.get((cls, identifier))
if obj is None:
obj = cache[(cls, identifier)] = cls(identifier)
return obj
class B(A):
pass
Because a WeakValueDictionary is used, the objects will remain cached as long as you have any other reference to them, and you can call SomeClass.get_obj(identifier) as many times as you like to get that same object. If I've understood you correctly, it's the cls(identifier) which will hit the database and what you want to call less frequently, since you know the objects are immutable.
If you want to keep objects in the cache even if they are no longer referenced elsewhere, then change the WeakValueDictionary into a normal dict.
This requires that identifier is suitable for a dict key, and if it's a string as you have in your example code, then it is.
One usual approach is this.
class SomeClass( object ):
# Something that is precious and needs to be pooled.
class SomeClassPool( object ):
def __init__( self ):
self.pool= [ SomeClass() ]
def getInstance( self ):
if len(self.pool) == 0:
self.pool.append( SomeClass() )
# maybe allocate several, depends on the costs
return self.pool.pop()
def release( self, anInstance ):
self.pool.append( anInstance )
We separate the pool from the objects being pooled. They have nothing to do with each other.
You can subclass the objects being pooled all you want.
You can -- independently -- change the pooling strategies without breaking or retesting the objects being pooled.
Expanding on S.Lott's comment:
"I want to return the correct instance
each time, without removing it from
the pool". You mean you want a
dictionary of objects? -S.Lott
the_cache = {}
def get_obj(cls, identifier):
key = (cls, identifier)
if key not in the_cache:
the_cache[key] = cls(identifier)
return the_cache[key]
or
def get_obj(cls, identifier):
key = (cls, identifier)
try:
return the_cache[key]
except KeyError:
the_cache[key] = cls(identifier)
return the_cache[key]

Getting an instance name inside class __init__() [duplicate]

This question already has answers here:
Getting the name of a variable as a string
(32 answers)
Closed 3 years ago.
While building a new class object in python, I want to be able to create a default value based on the instance name of the class without passing in an extra argument. How can I accomplish this? Here's the basic pseudo-code I'm trying for:
class SomeObject():
defined_name = u""
def __init__(self, def_name=None):
if def_name == None:
def_name = u"%s" % (<INSTANCE NAME>)
self.defined_name = def_name
ThisObject = SomeObject()
print ThisObject.defined_name # Should print "ThisObject"
Well, there is almost a way to do it:
#!/usr/bin/env python
import traceback
class SomeObject():
def __init__(self, def_name=None):
if def_name == None:
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
def_name = text[:text.find('=')].strip()
self.defined_name = def_name
ThisObject = SomeObject()
print ThisObject.defined_name
# ThisObject
The traceback module allows you to peek at the code used to call SomeObject().
With a little string wrangling, text[:text.find('=')].strip() you can
guess what the def_name should be.
However, this hack is brittle. For example, this doesn't work so well:
ThisObject,ThatObject = SomeObject(),SomeObject()
print ThisObject.defined_name
# ThisObject,ThatObject
print ThatObject.defined_name
# ThisObject,ThatObject
So if you were to use this hack, you have to bear in mind that you must call SomeObject()
using simple python statement:
ThisObject = SomeObject()
By the way, as a further example of using traceback, if you define
def pv(var):
# stack is a list of 4-tuples: (filename, line number, function name, text)
# see http://docs.python.org/library/traceback.html#module-traceback
#
(filename,line_number,function_name,text)=traceback.extract_stack()[-2]
# ('x_traceback.py', 18, 'f', 'print_var(y)')
print('%s: %s'%(text[text.find('(')+1:-1],var))
then you can call
x=3.14
pv(x)
# x: 3.14
to print both the variable name and its value.
Instances don't have names. By the time the global name ThisObject gets bound to the instance created by evaluating the SomeObject constructor, the constructor has finished running.
If you want an object to have a name, just pass the name along in the constructor.
def __init__(self, name):
self.name = name
You can create a method inside your class that check all variables in the current frame and use hash() to look for the self variable.
The solution proposed here will return all the variables pointing to the instance object.
In the class below, isinstance() is used to avoid problems when applying hash(), since some objects like a numpy.array or a list, for example, are unhashable.
import inspect
class A(object):
def get_my_name(self):
ans = []
frame = inspect.currentframe().f_back
tmp = dict(frame.f_globals.items() + frame.f_locals.items())
for k, var in tmp.items():
if isinstance(var, self.__class__):
if hash(self) == hash(var):
ans.append(k)
return ans
The following test has been done:
def test():
a = A()
b = a
c = b
print c.get_my_name()
The result is:
test()
#['a', 'c', 'b']
This cannot work, just imagine this: a = b = TheMagicObjet(). Names have no effect on Values, they just point to them.
One horrible, horrible way to accomplish this is to reverse the responsibilities:
class SomeObject():
def __init__(self, def_name):
self.defined_name = def_name
globals()[def_name] = self
SomeObject("ThisObject")
print ThisObject.defined_name
If you wanted to support something other than global scope, you'd have to do something even more awful.
In Python, all data is stored in objects. Additionally, a name can be bound with an object, after which that name can be used to look up that object.
It makes no difference to the object what names, if any, it might be bound to. It might be bound to dozens of different names, or none. Also, Python does not have any "back links" that point from an object to a name.
Consider this example:
foo = 1
bar = foo
baz = foo
Now, suppose you have the integer object with value 1, and you want to work backwards and find its name. What would you print? Three different names have that object bound to them, and all are equally valid.
print(bar is foo) # prints True
print(baz is foo) # prints True
In Python, a name is a way to access an object, so there is no way to work with names directly. You could search through various name spaces until you find a name that is bound with the object of interest, but I don't recommend this.
How do I get the string representation of a variable in python?
There is a famous presentation called "Code Like a Pythonista" that summarizes this situation as "Other languages have 'variables'" and "Python has 'names'"
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
If you want an unique instance name for a class, try __repr__() or id(self)
class Some:
def __init__(self):
print(self.__repr__()) # = hex(id(self))
print(id(self))
It will print the memory address of the instance, which is unique.
Inspired by the answers of unutbu and Saullo Castro, I have created a more sophisticated class that can even be subclassed. It solves what was asked for in the question.
"create a default value based on the instance name of the class
without passing in an extra argument."
Here's what it does, when an instance of this class or a subclass is created:
Go up in the frame stack until the first frame which does not belong to a method of the current instance.
Inspect this frame to get the attributes self.creation_(name/file/module/function/line/text).
Perform an an additional check whether an object with name self.creation_name was actually defined in the frame's locals() namespace to make 100% sure the found creation_name is correct or raise an error otherwise.
The Code:
import traceback, threading, time
class InstanceCreationError(Exception):
pass
class RememberInstanceCreationInfo:
def __init__(self):
for frame, line in traceback.walk_stack(None):
varnames = frame.f_code.co_varnames
if varnames is ():
break
if frame.f_locals[varnames[0]] not in (self, self.__class__):
break
# if the frame is inside a method of this instance,
# the first argument usually contains either the instance or
# its class
# we want to find the first frame, where this is not the case
else:
raise InstanceCreationError("No suitable outer frame found.")
self._outer_frame = frame
self.creation_module = frame.f_globals["__name__"]
self.creation_file, self.creation_line, self.creation_function, \
self.creation_text = \
traceback.extract_stack(frame, 1)[0]
self.creation_name = self.creation_text.split("=")[0].strip()
super().__init__()
threading.Thread(target=self._check_existence_after_creation).start()
def _check_existence_after_creation(self):
while self._outer_frame.f_lineno == self.creation_line:
time.sleep(0.01)
# this is executed as soon as the line number changes
# now we can be sure the instance was actually created
error = InstanceCreationError(
"\nCreation name not found in creation frame.\ncreation_file: "
"%s \ncreation_line: %s \ncreation_text: %s\ncreation_name ("
"might be wrong): %s" % (
self.creation_file, self.creation_line, self.creation_text,
self.creation_name))
nameparts = self.creation_name.split(".")
try:
var = self._outer_frame.f_locals[nameparts[0]]
except KeyError:
raise error
finally:
del self._outer_frame
# make sure we have no permament inter frame reference
# which could hinder garbage collection
try:
for name in nameparts[1:]: var = getattr(var, name)
except AttributeError:
raise error
if var is not self: raise error
def __repr__(self):
return super().__repr__()[
:-1] + " with creation_name '%s'>" % self.creation_name
A simple example:
class MySubclass(RememberInstanceCreationInfo):
def __init__(self):
super().__init__()
def print_creation_info(self):
print(self.creation_name, self.creation_module, self.creation_function,
self.creation_line, self.creation_text, sep=", ")
instance = MySubclass()
instance.print_creation_info()
#out: instance, __main__, <module>, 68, instance = MySubclass()
If the creation name cannot be determined properly an error is raised:
variable, another_instance = 2, MySubclass()
# InstanceCreationError:
# Creation name not found in creation frame.
# creation_file: /.../myfile.py
# creation_line: 71
# creation_text: variable, another_instance = 2, MySubclass()
# creation_name (might be wrong): variable, another_instance
I think that names matters if they are the pointers to any object..
no matters if:
foo = 1
bar = foo
I know that foo points to 1 and bar points to the same value 1 into the same memory space.
but supose that I want to create a class with a function that adds a object to it.
Class Bag(object):
def __init__(self):
some code here...
def addItem(self,item):
self.__dict__[somewaytogetItemName] = item
So, when I instantiate the class bag like below:
newObj1 = Bag()
newObj2 = Bag()
newObj1.addItem(newObj2)I can do this to get an attribute of newObj1:
newObj1.newObj2
The best way is really to pass the name to the constructor as in the chosen answer. However, if you REALLY want to avoid asking the user to pass the name to the constructor, you can do the following hack:
If you are creating the instance with 'ThisObject = SomeObject()' from the command line, you can get the object name from the command string in command history:
import readline
import re
class SomeObject():
def __init__(self):
cmd = readline.get_history_item(readline.get_current_history_length())
self.name = re.split('=| ',cmd)[0]
If you are creating the instance using 'exec' command, you can handle this with:
if cmd[0:4] == 'exec': self.name = re.split('\'|=| ',cmd)[1] # if command performed using 'exec'
else: self.name = re.split('=| ',cmd)[0]

Categories