I just started to learn Python and I"m struggling a little with instance variables. So I create an instance variable in a method that's of a list type. Later on, I want to call and display that variable's contents. However, I'm having issues doing that. I read some online, but I still can't get it to work. I was thinking of something along the following (this is a simplified version):
What would the proper way of doing this be?
class A:
def _init_(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal():
#Is this needed? Likewise a "get" method"?
def randomMethod():
A.listVar[0] #something like that to call/display it right? Or would a for
#for loop style command be needed?
Class B:
def _init_(self):
self.a = ""
self.b = ""
self.c = ""
Is the list something you'll be passing to the instance when you create it (i.e. will it be different each time)?
If so, try this:
class A:
def __init__(self, list):
self.listVar = list
Now, when you instantiate (read: create an instance) of a class, you can pass a list to it and it will be saved as the listVar attribute for that instance.
Example:
>>> first_list = [B("1","2","3"), B("1","2","3")]
>>> second_list = [C("1","2","3"), C("1","2","3")]
>>> first_instance = A(first_list) # Create your first instance and pass it your first_list. Assign it to variable first_instance
>>> first_instance.listVar # Ask for the listVar attribute of your first_instance
[B("1","2","3"), B("1","2","3")] # Receive the list you passed
>>> second_instance = A(second_list) # Create your second instance and pass it your second_list. Assign it to variable second_instance
>>> second_instance.listVar # Ask for the listVar attribute of your second_instance
[C("1","2","3"), C("1","2","3")] # Receive the list you passed second instance
Feel free to ask if anything is not clear.
class A:
def __init__(self):
self.listVar = [B("1","2","3"), B("1","2","3")]
def setListVal(self, val):
self.listVar[0] = val # example of changing the first entry
def randomMethod(self):
print self.listVar[0].a # prints 'a' from the first entry in the list
class B:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
I made several changes. You need to use self as the first argument to all the methods. That argument is the way that you reference all the instance variables. The initialization function is __init__ note that is 2 underscores before and after. You are passing three arguments to initialize B, so you need to have 3 arguments in addition to self.
Related
I inherit features from one parent class (__Parent) to two different child classes for constructing a nested data structure. The init() method of ChildTwo initiallizes the parent class using super() thereby setting i_am_from_child to "Two". It then appends an instance of ChildOne to the inherited list var. It is this appending that behaves unexpected when using list.append(). The init() of ChildOne also initializes the parent class in the same way, setting its i_am_from_child to "One", but without appending its inherited list var.
Therfore the list var of the instance of ChildOne stored in ChildTwo's var[0] should have a length of 0 as it is empty. This behaviour is obtained when using numpy append. However pythons list.append() results in an instance of ChildOne beingg strored at that location.
import numpy as np
class __Parent:
var = []
i_am_from_child = None
def __init__(self, which:str=None) -> None:
self.i_am_from_child = which
print(f"Parent initiallized by Child{which}")
def how_long(self) -> int:
return len(self.var)
def from_child(self) -> str:
return self.i_am_from_child
class ChildOne(__Parent):
def __init__(self) -> None:
print("Initiallizing ChildOne")
super().__init__(which="One")
print(f"ChildOne: len(self.var) = {len(self.var)}")
class ChildTwo(__Parent):
def __init__(self) -> None:
print("Initiallizing ChildTwo")
super().__init__(which="Two")
# two options in question
self.var.append(ChildOne()) # this behaves unexpected
#self.var = np.append(self.var, ChildOne()) # this behaves as expected
#####################################################################
X = ChildTwo() # instance of ChildTwo
Y = X.var[0] # copy of ChildOne instance created by ChildTwo constructor
print(f"type(X) = {type(X)} and type(Y) = {type(Y)}")
print(f"X is from Child{X.from_child()} and Y=X.var[0] is from Child{Y.from_child()}")
# something weird with var happens when using diffent append methods
print()
print(f"ChildOne: self.function() = {Y.how_long()} should be 0")
print(f"ChildTwo: self.function() = {X.how_long()} should be 1")
print(f"Type of Y.var[0] is {type(Y.var[0])}")
Using print() I checked the correct sequence of method calles, additionally the types are correct. But Y.var[0] should be empty, i.e. [] and thus should have length zero. Commenting out the python append and uncommenting the numpy append statements in ChildTwo.init() produces the desired behaviour.
It's because you're declaring var as a class variable, which is shared between all instances of that class, rather than as an instance variable. When you type self.var, python first looks in the instance attribute dictionary, and if an attribute with the name var doesn't appear there, it looks in the instance's class dictionary, where it finds the shared var attribute. If you want each instance to treat them separately, you need to assign that variable in the __init__ method.
class Parent:
def __init__(self):
self.var = []
How can something be modified or added to a class?
For example, in the following code what does it mean that hello can modify the a I used in __init__? I still have a lot of uncertainty on OOP, especially the relation between instantiation and other things.
I'm sure it's possible; if so, how can I do it?
class Foo:
def __init__(self, a):
self.a = a
def hello(self):
You are already successfully adding/modifying the class instance. Namely, __init__ takes some a and adds it as the attribute .a to the instance self. Using the same name for the variable and attribute is incidental -- you could as well use different names:
class Bla:
def __init__(self, b):
self.a = b # add `b` as attribute `a`
Once an attribute is added to an instance, this attribute can by default be read and overridden freely. This applies to the initial method, other methods, and any other function/method having access to the instance.
class Bla:
def __init__(self, b):
self.a = b # add `b` as attribute `a`
def lol(self, c):
self.a = c # add `c` as attribute `a` (discarding previous values)
def rofl(self, d):
self.a += d # re-assign `self.a + d` as attribute `a`
# external function modifying `Bla` instance
def jk(bla, e):
bla.a = e # add `e` as attribute `a` (discarding previous values)
I'm not sure how Bla.lol() can modify the argument a that is supplied to it, but it can alter the attribute Bla.a or self.a as it is referenced from within the object, which initially has the value of argument a assigned to it.
We can have the function lol assign a new value to the attribute a, in this case I'm going to assume a is a string, and extend the string with the phrase ' has been altered':
>>> class Bla:
... def __init__(self, a):
... self.a = a
...
... def lol(self):
... self.a += ' has been altered'
...
>>> instance = Bla('the arg a')
>>> instance.a # check a
'the arg a'
>>> instance.lol() # modifies a
>>> instance.a # check a again
'the arg a has been altered'
>>> instance.lol() # we can alter it again, as many times as we run the function
>>> instance.a # and see it has been altered a second time
'the arg a has been altered has been altered'
So I have an idea in mind for a project, but i would need to have 2 instances of a certain class output into eachother. I want to make it so that the instance something gets its input from is an attribute of the instance, for example:
class Class(object):
def __init__(self, input):
self.input = input
a = Class(b)
b = Class(a)
Where "Class"is the class that these instances are from.
I hope my question makes my point clear, thanks in advance for your help!
Well, you could design the class constructor in such a way that by default an instance creates its own self-referential input instance:
class Class(object):
def __init__(self, input=None):
if input None:
self.input = Class(self)
else:
self.input = input
>>> a = Class()
>>> b = a.input
>>> b.input is a
True
Or, make it more explicit and just set the input on the first instance after creating the second:
class Class(object):
def __init__(self, input=None):
self.input = input
>>> a = Class()
>>> b = a.input = Class(a)
>>> a.input is b
True
>>> b.input is a
True
I'm afraid you won't be able to pass object b to the constructor of object a before the object b is created. Of course you may try:
a = Class(Class(b(a))
to pass just created b to the constructor of a but this time you's want to pass not-yet-created object a to the constructor of a. This won't work either.
But that's only one battle is lost. To win the war you may try:
a = Class()
b = Class()
a.input = b
b.input = a
Now they can talk to each other.
If you still want to save two lines of code, you may pass a to the constructor of b which will attach newly created b (that means self) to a and vice versa. Constructor of a will be given empy object so it won't do any connection util b gets a to do so.
But to make the code clear I'd prefer the first solution.
I'm collecting instances using the following code:
class Hand():
instances = []
def __init__(self):
Hand.instances.append(self)
self.value = 5
def do_something(self, a):
self.value = self.value * a
class Foo():
def __init__(self):
pass
def insty(self):
self.hand1 = Hand()
self.hand2 = Hand()
foo = Foo()
foo.insty()
print Hand.instances
for hand in Hand.instances:
print "how do I print the instance name?"
The last line is just a way to learn how to to access the instance name so i can call the 'do_something' method on each instance in order.
How do I access the instance name for each instance of Hand?
If you mean how to get hand1 from the instance you assigned to self.hand1, the answer is that you can't. When you do self.hand1 = Hand(), you tell the Foo object it has a Hand, but the Hand object has no knowledge that it has been assigned to a Foo. You could do this:
h = Hand()
self.bob = h
self.larry = h
Now what is the "name" of that Hand supposed to be? You assigned the same hand to both "bob" and "larry", so there's no way it can have a single unique name.
If you want to have a name for each hand, you need to tell the hand what name you want to give it. You would have to modify your Hand code to allow you to pass a name to the constructor, then create the Hand with Hand("some name").
You can of course give the hands "names" by assigning attributes on them:
self.hand1 = Hand()
self.hand1.name = "hand 1"
. . . but these names are not special or "automatic" in any way.
The bottom line is that if you want something to have a name, you need to decide how to handle that name. You need write your own code that gives it its name, and your own code that retrieves its name.
I don't know if this would solve your problem or not. I needed to get instance names in order to do clear error reporting. Everywhere I looked, folks said "variables don't have names! The name is just a pointer to the thing!"
But it turns out that getting instance names in python is pretty straightforward.
Here's how I did it:
import gc
def instance_names(self):
referrers = gc.get_referrers(self)
result = []
dict_of_things = {}
for item in referrers:
if isinstance(item, dict):
dict_of_things = item
for k, v in dict_of_things.items():
if v == self:
result.append(k)
if not result:
result = ['unnamed instance']
return result
foo = Foo() means that the variable foo just points to the object returned by Foo(), there's no concept of name here.
foo.__dict__ will have "hand1" and "hand2" keys (among others). But you're probably going about this the wrong way. If the names are significant, you should use them as explicit indices in Foo (or somewhere).
e.g.
class Foo():
def __init__(self):
self.hands = {}
def insty(self):
self.hands['hand1'] = Hand()
self.hands['hand2'] = Hand()
I am trying to load a whole class instance via dill rather than dump and load each class variable one at a time.
Can anybody show me how to do this:
class Object(object):
pass
class ClassA:
def __init__(self):
self.DATA = "Initial"
class ClassB:
def __init__(self, CA):
self.CA = CA
def updateValue(self):
#a = dill.load(ClassA.storage)
a = Object()
a.DATA = "new value"
self.CA = a
print self.CA.DATA
CA = ClassA()
CB = ClassB(CA)
CB.updateValue()
print CA.DATA
So that the output is:
new value
new value
I think you're asking:
Given object A and object B, how can I copy all of A's attributes to B in one step (or programatically)?
Naive approach:
B.__dict__ = dict(A.__dict__) # make a copy so objects don't share same dict
The problem with this approach is that it clobbers any preexisting attributes in B that did not exist in A. eg.
B.attr = "some value"
B.__dict__ = dict(A.__dict__)
print(hasattr(B, "attr")) # expect False
A better approach. This approach copies over A's attributes and leaves any attributes that exist on B, but not on A, alone.
B.__dict__.update(A.__dict__)
However, there are still problems if there are attributes on A's class and its parent classes that you want to copy over. But I think that's a different question.
In your updateValue
def updateValue(self):
self.ca.DATA = "new value"