How to initialise class variables just once using arguments passed to init - python

I want to share data across different instances of a class, but the data must be provided externally the first time the class is created.
I have written the snippet below.
class Foo(object):
_config = False
eggs = None
def __init__(self, spam, eggs=None):
if Foo._config:
# Assume initialized and eggs exists
print(Foo.eggs)
else:
if eggs is None:
raise ValueError('eggs must be provided the first time')
else:
Foo.eggs = eggs
Foo._config = True
print("Scrambled {}?".format(Foo.eggs))
self.spam = spam
print("Class variable - eggs: {}".format(Foo.eggs))
print("Instance variable - spam: {}".format(self.spam))
which seems to work ...
>>>Foo._config
False
>>>a = Foo('chicken', 'eggs')
Scrambled eggs?
Class variable - eggs: eggs
Instance variable - spam: chicken
>>>Foo._config
True
and the second time doesn't raise an error and shares the class variable
>>>b = Foo('duck')
eggs
Class variable - eggs: eggs
Instance variable - spam: duck
My question is whether this is a good approach? I have seen this question which suggests that including things in __init__ that are only called once is a bad idea, and I should use a metaclass?
My justification is that eggs will actually contain a very large pandas dataframe that I don't to repeat with each instance.

I would advise against using the class namespace.
see:
class holder():
x = 5
def __init__(self):
self.x = 6
return;
alpha = holder()
beta=holder()
beta.x = 4
holder.x = 100
print(holder.x)
print(alpha.x)
print(beta.x)
> 100
> 6
> 4
The scope of the variable gets diluted very quickly. I would reserve the class namespace for constants.
If you attempt to set a reference in the class namespace then you will have to generate the panda dataframe before. It will likely be easier to genereate it somewhere in your code before creating objects and then pass it by reference to each class.
As mentioned by #juanpa.arrivillaga : self.df = df

One way to do it is to create a #classmethod which you would call at the beginning in order to instantiate your constant values shared by all objects.

Related

Why is super() not behaving like I expected when assigning to a class variable of the base class?

I am attempting to experiment with classes so I can better understand what they do. I wanted to build a counter which records the number of instances of a class (MyClass):
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
class MyClass(ObjectCounter):
def __init__(self):
super().myclass_obj_count += 1 # AttributeError: 'super' object has no attribute 'myclass_obj_count'
m1 = MyClass()
m2 = MyClass()
m3 = MyClass()
print(ObjectCounter.myclass_obj_count)
Since that didn't work, I looked online for someone trying to do the same thing. Here is some code I found online. This works as expected, and I feel like I have a basic understanding of how this works. This is a better solution to the task I was attempting, but I'm not satisfied because I want to know how super() works.
class geeks:
counter = 0
def __init__(self):
geeks.counter += 1
g1 = geeks()
g2 = geeks()
g3 = geeks()
print(geeks.counter) # this gives an expected result
Therefore, I tried this instead:
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
class MyClass(ObjectCounter):
def __init__(self):
super().add_myclass()
my_class_1 = MyClass()
my_class_2 = MyClass()
my_class_3 = MyClass()
print(ObjectCounter.myclass_obj_count) # expected output: 3
Instead of getting the expected output of 3, I got an output of 0. Why is this happening?
First, be aware of the += operator; it's implementation is quite subtle:
a += b
becomes
a = a.__iadd__(b)
This perhaps strange definition allows python to support it even for immutable types (like strings).
Note what happens when used for a class variable that is referred to by the alias self
class ObjectCounter: # I want this to count the number of objects in each class
myclass_obj_count = 0
def add_myclass(self):
self.myclass_obj_count += 1
# effectively becomes:
# self.myclass_obj_count = self.myclass_obj_count.__iadd__(1)
This will introduce an instance variable of the same name, shadowing the class variable.
You don't even need the subclass to test this:
>>> x = ObjectCounter()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.add_myclass()
>>> x.myclass_obj_count
3
>>> ObjectCounter.myclass_obj_count
0
Referring to the class variable directly instead of using self fixes this
def add_myclass(self):
ObjectCounter.myclass_obj_count += 1
I'm hesitant to give definite answers of what happens under the hood when class variables, super() and assignments are used, other than it just doesn't work. Perhaps because it would be quite ambiguous of whether or not we are defining class variables or new instance variables.
super() won't let you assign to either;
class ObjectCounter:
myclass_obj_count = 0
def __init__(self):
self.x = 'test'
class MyClass(ObjectCounter):
def __init__(self):
super().__init__()
print(super().myclass_obj_count) # reading works just fine
print(type(super())) # this isn't actually exactly the same as "ObjectCounter"
super().myclass_obj_count = 123 # no good
super().x = 'foo' # also no good.
All in all, for any assignment to class variables you can use the class name itself.

How to implement a factory class?

I want to be able to create objects based on an enumeration class, and use a dictionary. Something like this:
class IngredientType(Enum):
SPAM = auto() # Some spam
BAKE_BEANS = auto() # Baked beans
EGG = auto() # Fried egg
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class BakedBeans(Ingredient):
pass
class Egg(Ingredient):
pass
class IngredientFactory(object):
"""Factory makes ingredients"""
choice = {
IngredientType.SPAM: IngredientFactory.MakeSpam,
IngredientType.BAKED_BEANS: IngredientFactory.MakeBakedBeans,
IngredientType.EGG: IngredientFactory.MakeEgg
}
#staticmethod
def make(type):
method = choice[type]
return method()
#staticmethod
def makeSpam():
return Spam()
#staticmethod
def makeBakedBeans():
return BakedBeans()
#staticmethod
def makeEgg():
return Egg()
But I get the error:
NameError: name 'IngredientFactory' is not defined
For some reason the dictionary can't be created.
Where am I going wrong here?
Python is not Java and doesn't require everything to be in a class. Here your IngredientFactory class has no states and only staticmethods, so it's actually just a singleton namespace, which in python is canonically done using the module as singleton namespace and plain functions. Also since Python classes are already callable, wrapping the instanciation in a function doesn't make sense. The simple, straightforwrad pythonic implementation would be:
# ingredients.py
class IngredientType(Enum):
SPAM = auto() # Some spam
BAKE_BEANS = auto() # Baked beans
EGG = auto() # Fried egg
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
_choice = {
IngredientType.SPAM: Spam,
IngredientType.BAKED_BEANS: Beans,
IngredientType.EGG: Egg
}
def make(ingredient_type):
cls = _choice[ingredient_type]
return cls()
And the client code:
import ingredients
egg = ingredients.make(ingredients.IngredientType.EGG)
# or much more simply:
egg = ingredients.Egg()
FWIW the IngredientType enum doesn't bring much here, and even makes things more complicated that they have to be - you could just use plain strings:
# ingredients.py
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
_choice = {
"spam": Spam,
"beans": Beans,
"egg": Egg
}
def make(ingredient_type):
cls = _choice[ingredient_type]
return cls()
And the client code:
import ingredients
egg = ingredients.make("egg")
Or if you really want to use an Enum, you can at least get rid of the choices dict by using the classes themselves as values for the enum as suggested by MadPhysicist:
# ingredients.py
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
class IngredientType(Enum):
SPAM = Spam
BEANS = Beans
EGG = Egg
#staticmethod
def make(ingredient_type):
return ingredient_type.value()
and the client code
from ingredients import IngredientType
egg = IngredientType.make(IngredientType.EGG)
But I really don't see any benefit here either
EDIT: you mention:
I am trying to implement the factory pattern, with the intent of hiding the creation of objects away. The user of the factory then just handles 'Ingredients' without knowledge of the concrete type
The user still have to specify what kind of ingredients he wants (the ingredient_type argument) so I'm not sure I understand the benefit here. What's your real use case actually ? (the problem with made up / dumbed down examples is that they don't tell the whole story).
After looking at Bruce Eckel's book I came up with this:
#Based on Bruce Eckel's book Python 3 example
# A simple static factory method.
from __future__ import generators
import random
from enum import Enum, auto
class ShapeType(Enum):
CIRCLE = auto() # Some circles
SQUARE = auto() # some squares
class Shape(object):
pass
class Circle(Shape):
def draw(self): print("Circle.draw")
def erase(self): print("Circle.erase")
class Square(Shape):
def draw(self): print("Square.draw")
def erase(self): print("Square.erase")
class ShapeFactory(object):
#staticmethod
def create(type):
#return eval(type + "()") # simple alternative
if type in ShapeFactory.choice:
return ShapeFactory.choice[type]()
assert 0, "Bad shape creation: " + type
choice = { ShapeType.CIRCLE: Circle,
ShapeType.SQUARE: Square
}
# Test factory
# Generate shape name strings:
def shapeNameGen(n):
types = list(ShapeType)
for i in range(n):
yield random.choice(types)
shapes = \
[ ShapeFactory.create(i) for i in shapeNameGen(7)]
for shape in shapes:
shape.draw()
shape.erase()
This gets the user to select a class type from the enumeration, and blocks any other type. It also means user's are less likely to write 'bad strings' with spelling mistakes. They just use the enums.
The output from the test is then, something like this:
Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase
Square.draw
Square.erase
Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase
Place your mapping at the end of the class, and reference the methods directly, since they're in the same namespace:
choice = {
IngredientType.SPAM: makeSpam,
IngredientType.BAKED_BEANS: makeBakedBeans,
IngredientType.EGG: makeEgg
}
A class object is not created until all the code in the class body, so you can't access the class itself. However, since the class body is processed in a dedicated namespace, you can access any attribute you've defined up to that point (which is why the mapping has to come at the end). Note also that while you can access globals and built-ins, you can't access the namespaces of enclosing classes or functions.
Here's the detailed but still introductory explanation from the official docs explaining how classes are executed: https://docs.python.org/3/tutorial/classes.html#a-first-look-at-classes

How can I spot class redefinitions?

Here is a very simple setup:
class A(object):
x = 1
class B(object):
x = 2
class C(object):
x = 3
class B(B):
x = 4
class C(object):
x = 5
print A.x, B.x, C.x
which outputs the expected:
1, 4, 5
is it possible to detect that B has been 'redefined' but C has been 'replaced'? either during the creation phase or subsequently by inspecting the objects?
These classes are being used as configuration objects, there is a loader class above this which imports the module and then introspects to find the classses and their attributes which are then used by the application to instance named parameters. e.g.
class tcp_port(number):
minimum = 1024
maximum = 2048
really python is just being used as a convenient scripting language to define parameters in the main app. These files are available for edit by that most dangerous of people: The Customer So the requirement is to be able to detect at run time if a class has (accidentally) reused a name that has already been defined, but to safely pass the occasional case where a class is redefined, but then tweaks or add some attribute:
class tcp_port(tcp_port):
maximum = 4096
So... I'm hoping for some runtime sanity checker rather than a lint-like solution. Also, this needs to happen when the class is defined, not instanced.
... is there global function which gets called to create a class (or any other object)? a bit like have a __new__ but in the global context?
If the class was redefined in-place, it will have old versions of itself hanging around in the MRO:
>>> [x for x in A.mro() if x.__name__ == A.__name__]
[__main__.A]
>>> [x for x in B.mro() if x.__name__ == B.__name__]
[__main__.B, __main__.B]
>>> [x for x in C.mro() if x.__name__ == C.__name__]
[__main__.C]
So you could detect that occurrence:
def has_redefinition_inplace(classobj):
types = [x for x in classobj.mro() if x.__name__ == classobj.__name__]
return len(types) > 1
This would not be 100% reliable, because there could be legitimately be colliding class names coming into the MRO from different modules.
A good linter will warn on re-declarations without usage. And a good IDE will lint directly in the editor.
For example, PyCharm will highlight these class names with a warning colour:
Another good way to spot these is with coverage in your test suite. Functions with names that were overwritten (common copy-paste errors) can not have test coverage in the function body.
I solved this by defining a parent class for all the classes then using a metaclass to keep a track of the names of the classes created, check for 'derived from itself' and then report any names that were reused.
#!/usr/bin/python
class _Object_Meta(type):
_defined = set()
def __init__(cls, name, bases, dct):
for base in bases:
if name == base.__name__:
break
else:
if name in _Object_Meta._defined:
raise RuntimeError("%s redefined" % name)
_Object_Meta._defined.add(name)
class Object(object):
__metaclass__ = _Object_Meta
class A(Object):
x = 1
class B(Object):
x = 2
class C(Object):
x = 3
class B(B):
x = 4
class C(Object):
x = 5

What is the difference between declaring data attributes inside or outside __init__ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python: Difference between class and instance attributes
I'm trying to get my head around OOP in Python and I'm a bit confused when it comes to declare variables within a class. Should I declare them inside of the __init__ procedure or outside it? What's the difference?
The following code works just fine:
# Declaring variables within __init__
class MyClass:
def __init__(self):
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
But declaring the variables outside of the __init__ procedure also works:
# Declaring variables outside of __init__
class MyClass:
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
In your first example you are defining instance attributes. In the second, class attributes.
Class attributes are shared between all instances of that class, where as instance attributes are "owned" by that particular instance.
Difference by example
To understand the differences let's use an example.
We'll define a class with instance attributes:
class MyClassOne:
def __init__(self):
self.country = "Spain"
self.city = "Barcelona"
self.things = []
And one with class attributes:
class MyClassTwo:
country = "Spain"
city = "Barcelona"
things = []
And a function that prints out information about one of these objects:
def information(obj):
print "I'm from {0}, ({1}). I own: {2}".format(
obj.city, obj.country, ','.join(obj.things))
Let's create 2 MyClassOne objects and change one to be Milan, and give Milan "something":
foo1 = MyClassOne()
bar1 = MyClassOne()
foo1.city = "Milan"
foo1.country = "Italy"
foo1.things.append("Something")
When we call information() on the foo1 and bar1 we get the values you'd expect:
>>> information(foo1)
I'm from Milan, (Italy). I own: Something
>>> information(bar1)
I'm from Barcelona, (Spain). I own:
However, if we were to do exactly the same thing, but using instances of MyClassTwo you'll see that the class attributes are shared between instances.
foo2 = MyClassTwo()
bar2 = MyClassTwo()
foo2.city = "Milan"
foo2.country = "Italy"
foo2.things.append("Something")
And then call information()...
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own: Something
So as you can see - things is being shared between the instances. things is a reference to a list that each instance has access to. So if you append to things from any instance that same list will be seen by all other instances.
The reason you don't see this behaviour in the string variables is because you are actually assigning a new variable to an instance. In this case that reference is "owned" by the instance and not shared at the class level. To illustrate let's assign a new list to things for bar2:
bar2.things = []
This results in:
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own:
You're two versions of the code are very different. In python, you have 2 distinct entities: classes and class instances. An instance is what is created when you do:
new_instance = my_class()
You can bind attributes to an instance within __init__ via self (self is the new instance).
class MyClass(object):
def __init__(self):
self.country = "" #every instance will have a `country` attribute initialized to ""
There's nothing terribly special about self and __init__. self is the customary name that is used to represent the instance that gets passed to every method (by default).
a.method() #-> Inside the class where `method` is defined, `a` gets passed in as `self`
The only thing special here is that __init__ gets called when the class is constructed:
a = MyClass() #implicitly calls `__init__`
You can also bind attributes to the class (putting it outside __init__):
class MyClass(object):
country = "" #This attribute is a class attribute.
At any point, you can bind a new attribute to an instance simply by:
my_instance = MyClass()
my_instance.attribute = something
Or a new attribute to a class via:
MyClass.attribute = something
Now it gets interesting. If an instance doesn't have a requested attribute, then python looks at the class for the attribute and returns it (if it is there). So, class attributes are a way for all instances of a class to share a piece of data.
Consider:
def MyClass(object):
cls_attr = []
def __init__(self):
self.inst_attr = []
a = MyClass()
a.inst_attr.append('a added this')
a.cls_attr.append('a added this to class')
b = MyClass()
print (b.inst_attr) # [] <- empty list, changes to `a` don't affect this.
print (b.cls_attr) # ['a added this to class'] <- Stuff added by `a`!
print (a.inst_attr) #['a added this']
When you define a variable in class scope (outside any method), it becomes a class attribute. When you define a value in method scope, it becomes a method local variable. If you assign a value to an attribute of self (or any other label referencing an object), it becomes (or modifies) an instance attribute.

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