Instance from another instance in Python [duplicate] - python

This question already has answers here:
Copy constructor in python?
(8 answers)
Closed 6 years ago.
In C++, the constructor of a class allows an instance to be constructed from another instance. e.g.
C::C(const C & c) {
bala...;
cc = c.cc;
}
In Python, what is the similar way of doing this? How may I use
c1 = C()
c2 = C(c1)
?

We don't mention the type while defining a variable in python. For example: if x=4, you can always set x to something else, x="shasha". No problem at all.
Note we can not overload a method in python.
Coming back to your question:
Assuming that you understand the python memory management and the difference between a reference and an actual value, You may use deepcopy feature in python:
import copy
class A(object):
def __init__(self):
self.a=10
x = A()
y= copy.deepcopy(x)
x.a=15
print(y.a) # prints 10.
Note that you can always copy one object into another using = operator like y = x but it wont actually copy anything. Now both the references y and x will actually be pointing to the same instance. i.e. if you change anything using one, it will automatically be reflected into the other one:
class A(object):
def __init__(self):
self.a=10
x = A()
y = x
x.a=15
print(y.a) # prints 15.
You can also create a dummy constructor as mentioned in following example:
class A:
def __init__(self):
self.a=10
def dummy_constructor(self):
temp = A()
temp.a = (self.a + 20 - 5 )*100/10
return temp
x=A()
y=x.dummy_constructor()
print(y.a) #250
print(x.a) #10

Related

Order of classes matters when using variables from each other

I am new to Python and didn't find an answer to the following problem:
I have two classes and want them to use variables from each other. Is there a simple way to do this because if I do it like this class a does not know that class b exists.
class a:
y=1
print(b.x)
class b:
x=1
print(a.y)
And how do I use overwrite the variables, the following code does not work:
class a:
y=b.x
class b:
x=1
You are executing print as part of the class definition. It executes as soon as python sees that line of code, before it's read the part about class b.
Instead, use functions inside the classes to execute code after the classes have been defined:
class a:
y=1
def go():
print(b.x)
class b:
x=1
def go():
print(a.y)
a.go()
b.go()
As I said in a comment, your code isn't making effective use of classes. Here's what I think would be better approach that offers more flexibility in working around the circular reference issue.
First the class definitions (which follow the PEP 8 naming convention guidelines):
class A:
def __init__(self, value, linked_value=None):
self.y = value
if isinstance(linked_value, B):
self.linked_value = linked_value.x
def print_linked_value(self):
print(self.linked_value)
class B:
def __init__(self, value, linked_value=None):
self.x = value
if isinstance(linked_value, A):
self.linked_value = linked_value.y
def print_linked_value(self):
print(self.linked_value)
Definitions like that provide two ways to set up the circular references:
By creating them separately, then explicitly linking them:
# First create instances of each class.
a = A(1)
b = B(42)
# Then link them.
a.linked_value = b.x
b.linked_value = a.y
a.print_linked_value() # -> 42
b.print_linked_value() # -> 1
*OR* by creating the first one without a linked value and leaving only the second needing to be linked manually.
# First create instances of each class, but link the second to the first
# when it's created.
a = A(1)
b = B(42, a) # Create and link to first.
# Then link the first to the second to complete the circular references.
a.linked_value = b.x
# Same result.
a.print_linked_value() # -> 42
b.print_linked_value() # -> 1
Final note: Another, more advanced alternative that can also be applied in situations like this by using the built-in property() function as a decorator to create "descriptors". Here's an answer to a somewhat related question that illustrating its use.
class A:
y = 1
def foo(self):
print B.x
class B:
x = 1
def bar(self):
print A.y
>>> A().foo()
2
>>> B().bar()
1
Use 'print' in some function definition.

class initialization in Python

I found that some classes contain a __init__ function, and some don’t. I’m confused about something described below.
What is the difference between these two pieces of code:
class Test1(object):
i = 1
and
class Test2(object):
def __init__(self):
self.i = 1
I know that the result or any instance created by these two class and the way of getting their instance variable are pretty much the same. But is there any kind of “default” or “hidden” initialization mechanism of Python behind the scene when we don’t define the __init__ function for a class? And why I can’t write the first code in this way:
class Test1(object):
self.i = 1
That’s my questions. Thank you very much!
Thank you very much Antti Haapala! Your answer gives me further understanding of my questions. Now, I understand that they are different in a way that one is a "class variable", and the other is a "instance variable". But, as I tried it further, I got yet another confusing problem.
Here is what it is. I created 2 new classes for understanding what you said:
class Test3(object):
class_variable = [1]
def __init__(self):
self.instance_variable = [2]
class Test4(object):
class_variable = 1
def __init__(self):
self.instance_variable = 2
As you said in the answer to my first questions, I understand the class_variable is a "class variable" general to the class, and should be passed or changed by reference to the same location in the memory. And the instance_variable would be created distinctly for different instances.
But as I tried out, what you said is true for the Test3's instances, they all share the same memory. If I change it in one instance, its value changes wherever I call it.
But that's not true for instances of Test4. Shouldn't the int in the Test4 class also be changed by reference?
i1 = Test3()
i2 = Test3()
>>> i1.i.append(2)
>>> i2.i
[1, 2]
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
Why is that? Does that "=" create an "instance variable" named "i" without changing the original "Test4.i" by default? Yet the "append" method just handles the "class variable"?
Again, thank you for your exhaustive explanation of the most boring basic concepts to a newbie of Python. I really appreciate that!
In python the instance attributes (such as self.i) are stored in the instance dictionary (i.__dict__). All the variable declarations in the class body are stored as attributes of the class.
Thus
class Test(object):
i = 1
is equivalent to
class Test(object):
pass
Test.i = 1
If no __init__ method is defined, the newly created instance usually starts with an empty instance dictionary, meaning that none of the properties are defined.
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
So you can do things like:
class Test:
i = 1
t = Test()
print(t.i) # prints 1
t.i += 1
print(t.i) # prints 2
but what this actually does is:
>>> class Test(object):
... i = 1
...
>>> t = Test()
>>> t.__dict__
{}
>>> t.i += 1
>>> t.__dict__
{'i': 2}
There is no i attribute on the newly created t at all! Thus in t.i += 1 the .i was looked up in the Test class for reading, but the new value was set into the t.
If you use __init__:
>>> class Test2(object):
... def __init__(self):
... self.i = 1
...
>>> t2 = Test2()
>>> t2.__dict__
{'i': 1}
The newly created instance t2 will already have the attribute set.
Now in the case of immutable value such as int there is not that much difference. But suppose that you used a list:
class ClassHavingAList():
the_list = []
vs
class InstanceHavingAList()
def __init__(self):
self.the_list = []
Now, if you create 2 instances of both:
>>> c1 = ClassHavingAList()
>>> c2 = ClassHavingAList()
>>> i1 = InstanceHavingAList()
>>> i2 = InstanceHavingAList()
>>> c1.the_list is c2.the_list
True
>>> i1.the_list is i2.the_list
False
>>> c1.the_list.append(42)
>>> c2.the_list
[42]
c1.the_list and c2.the_list refer to the exactly same list object in memory, whereas i1.the_list and i2.the_list are distinct. Modifying the c1.the_list looks as if the c2.the_list also changes.
This is because the attribute itself is not set, it is just read. The c1.the_list.append(42) is identical in behaviour to
getattr(c1, 'the_list').append(42)
That is, it only tries read the value of attribute the_list on c1, and if not found there, then look it up in the superclass. The append does not change the attribute, it just changes the value that the attribute points to.
Now if you were to write an example that superficially looks the same:
c1.the_list += [ 42 ]
It would work identical to
original = getattr(c1, 'the_list')
new_value = original + [ 42 ]
setattr(c1, 'the_list', new_value)
And do a completely different thing: first of all the original + [ 42 ] would create a new list object. Then the attribute the_list would be created in c1, and set to point to this new list. That is, in case of instance.attribute, if the attribute is "read from", it can be looked up in the class (or superclass) if not set in the instance, but if it is written to, as in instance.attribute = something, it will always be set on the instance.
As for this:
class Test1(object):
self.i = 1
Such thing does not work in Python, because there is no self defined when the class body (that is all lines of code within the class) is executed - actually, the class is created only after all the code in the class body has been executed. The class body is just like any other piece of code, only the defs and variable assignments will create methods and attributes on the class instead of setting global variables.
I understood my newly added question. Thanks to Antti Haapala.
Now, when Python does the get attribute (as in print(instance.i) operation, it first looks for the attribute named i that is set on the instance). If that fails, the i attribute is looked up on type(i) instead (that is, the class attribute i).
I'm clear about why is:
j1 = Test4()
j2 = Test4()
>>> j1.i = 3
>>> j2.i
1
after few tests. The code
j1.3 = 3
actually creates a new instance variable for j1 without changing the class variable. That's the difference between "=" and methods like "append".
I'm a newbie of Python coming from c++. So, at the first glance, that's weird to me, since I never thought of creating a new instance variable which is not created in the class just using the "=". It's really a big difference between c++ and Python.
Now I got it, thank you all.

How to instantiate a class given its name as a string? [duplicate]

This question already has answers here:
Calling a function of a module by using its name (a string)
(18 answers)
Closed 8 years ago.
Give some class definitiona in python, e.g.
class A(object):
def __init__(self):
self.x = 5
class B(object):
def __init(self):
self.x = 42
I want to instantiate one of these classes given its name as string. So for example, if classname is A, then I want to do something like
myinstance = SOMETHING(classname)
which should correspond to
myinstance = A()
Is there a way to do this without the use of eval eval or maps? If not, I would do the following:
map = {'A': A, 'B': B}
myinstance = map[classname]()
Considering that the Python naming scheme is based on maps, you will be using maps of one sort or another even if you use eval.
Building your own map is the most appropriate, but you can also use globals() or retrieve it from a module if your classes are stored somewhere else:
import util_module
getattr(util_module, 'A') ()

Python: update value of an instance and use it in a class from imported module

I have been studying Python for three months and now I have a question that I could not solve by using google, but luckily I am able to simplify it here:
I have a var_class module:
#var_class.py
class A(object):
def __init__(self, x):
self.x = x+2
zz = A(10)
class B():
b = 0
def __init__(self):
pass
def update(self):
B.b = zz.x
and in main program I have:
#main.py
from var_class import *
b_class = B()
b_class.b # I get 0 as expected
zz = A(100)
b_class.update()
b_class.b # Instead of 102, I get 12 unexpectedly
You see my goal is to change "zz" frequently for every data input and then update a class variable 'b', and the reason I wrote zz = A(10) in var_class.py is that otherwise when I import it, module 'var_class' is missing 'zz' in 'class B', it would give error "global name zz is not defined".
However, as I write it like this now, looks like the value '10' is stuck to the class, and I am not able to change it in the main program. Don't know how to overcome this.
Thanks in advance for any help.
Answer:
alKid wrote the whole answer first, have to thanks to alexvassel and Mr. C too, would like to know if there is way to thanks them, others helped me with the knowledge, also appreciate a lot.
Easy understanding:
You can't do that, you're inside var_class module, so zz is A(10).
How about passing zz as a parameter? Like this!
class A(object):
def __init__(self, x):
self.x = x+2
zz = A(10)
class B():
b = 0
def __init__(self):
pass
def update(self, zz):
B.b = zz.x
Hope this helps!
When you do B.b = zz.x (update method) you are inside the module var_class, so zz is A(10)
python runtime finds variables by namespace. Namespace is something like scope. When B.b = zz.x executes, the runtime first searches the local namespace(the function namespace -- update), it sees no zz. Then, it goes to the module space(var_class), well we get the variable and stop searching.
The python namespace search order:
1. local namespace, the function scope
2. global namespace, the module scope
3. built-in namespace
Better not use global variables around.
your code may like this:
class B():
b = 0
def __init__(self):
pass
def update(self, zz):
B.b = zz.x
That is a matter of scope! you are using zz = A(100) in your main.py. But when you call b_class.update(), b_class has to get "some2 variable called "zz" - and the one which is available is the one you defined in your class.py - and this one still has the value A(10)!!!
To work around this, you have different options.
The dirty hack (but closest to your solution) would be to define a global variable and use ist (google "python globals" for more info on this approach).
I would suggest passing A (or A.x) to your "update" method b_class.update(A.x). This avoids globals and is more readable. Of course, you'll have to adjust B.update for the new parameter.
when you import a variable from a module, you get a copy of the variable, not the original. you need to write to the original directly.
from a.b import c
from a.b.c import var
a.b.c.var = 1
var = 2
a.b.c.var
1
var
2
Edit: So, more correctly, in python, there are objects in memory, and then there are names for the objects. When you import a module, you create two separate names, but they both point to the same object - i.e. they have a reference to the same object. It's very similar to the below code, which doesn't require any imports at all:
>>> a = 4
>>> b = a
>>> b
4
>>> a
4
>>> b = 'something different'
>>> b
'something different'
>>> a
4
Why did changing b not also change a? The sequence is like this: First, we create an object (the 4) and point the name 'a' at it. Then, we create a name 'b', and we point it at the same object. So, now looking up those names returns the same object. Now, we then go back and point the name 'b' at a different object, a string. The object 'a' is pointing to still exists, and has not changed.
New users more often trip up on this the other way, with objects like lists:
>>> a = [1,2,3,4,5]
>>> b = a
>>> b
[1,2,3,4,5]
>>> a
[1,2,3,4,5]
>>> b.append(6)
>>> b
[1,2,3,4,5,6]
>>> a
[1,2,3,4,5,6]
>>> b = b[1:3]
>>> b
[2,3]
>>> a
[1,2,3,4,5,6]
What's going on here? Well, first we create a list object, and then point the name 'a' at it. Then we create the name 'b' and point it to the same object. So, 'a' and 'b' both point to the same list. Then, we use the reference b to get the object and modify it. Note that we haven't changed what 'b' points to in this case - we grabbed the reference, and then modified the object it points to directly. So, in this case, both 'a' and 'b' will see the change - they both point to the modified object. Then, we extract a slice, and assign it to 'b'. Now, this actually creates a new object, and points 'b' at it - 'b' is no longer pointing to the original object. So now 'a' and 'b' point to different objects, and now updates to one are no longer reflected in the other.
The import case is just a special case of this.
maybe you can do this
class B():
b = 0
def __init__(self):
pass
def update(self,value):
self.b =self.b+value

Understanding Python inheritance and initialisation [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
In Python 2.7, consider I have the following code:
class Base(object):
# Variant 1
def __init__(self, records=[]):
self._records = records
# Variant 2
# def __init__(self, records=[]):
# self._records = []
# if records:
# self._records = records
def append(self, value):
self._records.append(value)
class ChildA(Base):
pass
class ChildB(Base):
pass
a = ChildA()
b = ChildB()
a.append(100)
b.append(200)
print a._records
print b._records
If I use variant 1 to initialize my base class, self._records behaves like a class variable. Executing the code using variant 1 to initialize my base class, I get the ouput:
[100, 200]
[100, 200]
Using variant 2 to initialize my base class, self._records behaves like a instance variable (as expected). Executing the code using variant 2 to initialize my base class, I get the output:
[100]
[200]
What is the difference between these both variants? Why does variant 1 work different to variant 2? Thanks a lot for your help!
Your default argument is [], which is a common pitfall with Python. See more in the tutorial:
Important warning: The default value is evaluated only once. This
makes a difference when the default is a mutable object such as a
list, dictionary, or instances of most classes.
It has nothing to do with inheritance, class or instance variables. Consider the next code:
>>> def f(a=[]):
... a.append(1)
... print a
...
>>> f.func_defaults
([],)
>>> f()
[1]
>>> f()
[1, 1]
>>> f.func_defaults
([1, 1],)
Default values for function parameters are evaluated only ones and stored within function object. Each time f is called it operates with the same list. Right as in your case.
As the others had put - this has to do with using an empty list as default value, not with class inheritance behavior at all.
The correct way to do it is:
class Base(object):
# Variant 1
def __init__(self, records=None):
if records is None:
records = []
self._records = records
Therefore ensuring a new list instance is created each time the Base class is instantiated. The way you put it in your code, a list object is instantiated when your class body is parsed by Python - and that one instance of list is used as the default parameter each time the __init__ method is run. As the objects hold a reference and change that same list, the change is visible in all other objects which share the Base class.

Categories