Reference objects for accessing members [duplicate] - python

This question already has answers here:
How do I pass a variable by reference?
(39 answers)
Closed 5 years ago.
I would like to change the value of a member of an object. I can do this as long as i adress the object itself. However when I store a reference to this member it doesn't work. Instead it changes the reference object.
class MyClass:
def __init__(self, name):
self.a = 1
self.b = name
obj1=MyClass("string")
refob1=obj1.a
refob2=obj1.b
#I can change my object like this:
obj1.a=99
obj2.b="changed"
#But not like this:
refob1 = 1
refob2 = "changed_reference"
How can I use the reference object to change the members?

This is because int and strings in python are non-mutable objects. This means that they pass a copy of the value instead of the reference.
lists, in the other hand, are mutable objects and allow your desired behavior. Here you have an example:
class MyClass(object):
def __init__(self):
self.a = 3
self.b = [1]
obj = MyClass()
outside_a = obj.a
outside_a = 3 # outside_a is just a copy of obj.a value, so it won't change obj object
outisde_b = obj.b
outside_b[0] = 3 # Lists are mutabe, so this will change obj.b value
print obj.b # Prints [3]
So, how can you go around this? It is a bit ugly, but you can use a container (list) to contain the variables you want to reference later on. Here you can find a related post: Python passing an integer by reference

Related

Class inheritance with iterable properties in Python [duplicate]

This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 3 years ago.
I am trying to understand something in Python.
When I have a property that I want shared across all subclasses, I place it in the parent class. I was expecting that the actual value of this property would be unique for each object instance. However, if this property is iterable and I modify it in one object, the change is made for all other instantiated objects.
class Animal:
sounds = []
def add_sounds(self, sound):
return self.sounds.append(sound)
class Cat(Animal):
pass
class Dog(Animal):
pass
cat = Cat()
cat.add_sounds('meow')
dog = Dog()
dog.add_sounds('bark')
print('The cat says: ' + str(cat.sounds))
print('The dog says: ' + str(dog.sounds))
This gives:
The cat says: ['meow', 'bark']
The dog says: ['meow', 'bark']
... but I was expecting:
The cat says: ['meow']
The dog says: ['bark']
This doesn't seem to be the case for other variable types like strings or numbers. What am I missing? This is Python 3.7.3.
sounds is a class attribute shared by all instances. You want an instance attribute instead, which is best initialized in Animal.__init__.
class Animal:
def __init__(self):
self.sounds = []
def add_sounds(self, sound):
self.sounds.append(sound)
In your code, since sounds doesn't exist as an attribute on an individual instance of Animal, self.sounds resolves to Animal.sounds.
The difference you are observing with strings and numbers is the those types are immutable; you don't specify how you observe the difference, but here's an example:
x = 3
y = x
x += 1
assert y == 3
The += operator doesn't modify the existing int that both x and y refer to; it creates a new int object and makes x refer to that, leaving y as the sole reference to the previous int object.

Instance from another instance in Python [duplicate]

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

Understanding classes 'self' [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 6 years ago.
In the following example employee is not used in the __init__ function, but we used it in the add_employee function calling self.employee.append().
Why is that? Why did we use self.employee.append() instead of employee.append() ? I thought we only use self for variables in the __init__ function.
class Workers():
employee = []
def __init__(self, name):
self.name = name
self.skills = []
self.add_employee()
def add_employee(self):
self.employee.append(self.name)
print('{} added to list'.format(self.name))
employee, __init__, and add_employee are just attributes of the class Workers.
employee is an attribute being a list, and __init__ is another attribute, being a method.
Also from the [def documentation](
https://docs.python.org/3/reference/compound_stmts.html#grammar-token-funcdef):
A function definition is an executable statement. Its execution binds the function name in the current local namespace to a function object (a wrapper around the executable code for the function).
so employees and __init__ and all other methods are really the same: names in a namespaces.
See also
https://docs.python.org/3/tutorial/classes.html#class-objects
The employee object is a class variable, not an instance variable. This means it is shared across all instances of that class. You can access it with classname.classvariablename or instancename.classvariablename. If you reassign an instance's version of it with something like instancename.classvariablename = newvalue, that instance will have a new instance variable of that name that masks its access to the class variable with the self reference (i.e., you won't be able to do instancename.classvariablename to get the class variable), but other instances - and the class - will still be able to (i.e., classname.classvariable will still work, and otherinstancename.classvariable will still point to that class variable). The following example demonstrates this.
>>> class A:
... l = []
...
>>> a = A()
>>> b = A()
>>> a.l
[]
>>> A.l
[]
>>> a.l = 3
>>> b.l
[]
>>> b.l.append(1)
>>> b.l
[1]
>>> A.l
[1]
>>> a.l
3

python constructor default argument list [duplicate]

This question already has answers here:
"Least Astonishment" and the Mutable Default Argument
(33 answers)
Closed 9 years ago.
Is the default argument list the same object for all instances?
class X():
def __init__(self,a=[]):
self.member=a
print id(a)
k=X([1,2,3])
g=X([1,2,3])
t=X()
h=X()
The output surprises me:
140072782781832
140072782901976
140072782816536
140072782816536
As you can see, the id is different when a equals [1,2,3] but stays the same when a is empty. However, if I delete self.member, now the code looks like this:
class X():
def __init__(self,a=[]):
print id(a)
k=X([1,2,3])
g=X([1,2,3])
t=X()
h=X()
The output becomes like this:
140033294171528
140033294171528
140033294206232
140033294206232
The id stay the same when a equals [1,2,3].
I am totally confused... Anyone can explain that?
Yes, which is why you are supposed to do
class X():
def __init__(self, a=None):
self.a = [] if a is None else a
Edit:
I would point out that
class X():
def __init__(self,a=[]):
print(id(a))
k = X([1,2,3])
g = X([1,2,4]) # <- different list values
t = X()
h = X()
also gives
42678232
42678232
42680152
42680152
so I would expect the answer is something like "if you create a list, delete it, and create another list, the odds are good it will reuse the same allocated memory location".

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