assignment overriden to keep changing original instance variable in Python class - python

Basically, this is what the problem is. I have a class with an optional parameter: lst. lst is a nested list. Below is a method that returns another instance of the class that it is inside.
self.lst = [[[1, 2]]] # this is defined in the constructor
s = self.lst
for a in s:
for b in a:
if b[0] != item:
b[0] = 5
return ChangeNumb(lst=s)
What happens after this is VERY weird. Returning ChangeNumb.lst is now [[[5, 2]]], but the class that the method is run in ALSO ChangeNumb.lst == [[[5, 2]]].
So that instead of just returning a new instance of a class with a different parameter, this method changes the self.lst of the class it is inside as well.
I need to make it so that self.lst does not change, while returning a new instance.
PLEASE HELP
EDIT: Although I've encountered a similar problem when writing the class constructor, I was able to resolve it as -> self.lst = lst.copy(); however, s = self.lst.copy() does not work!

You have to make a copy in order not to modify self.lst.
For simple lists just list() is enough, but as you have nested lists you should use copy.deepcopy().
So you could change your declaration with something like :
s = copy.deepcopy(self.lst)

Related

TypeError: __init__() missing 1 required positional argument: 'lists'

I created a class, something like below -
class child:
def __init__(self,lists):
self.myList = lists
def find_mean(self):
mean=np.mean(self.myList)
return mean
and when I create an onject something like below -
obj=child()
it gives the error -
TypeError: __init__() missing 1 required positional argument: 'lists'
if I create object like below then it works well -
obj=child([44,22,55)
or If I create the class like below -
class child:
def find_mean(self,myList):
mean=np.mean(myList)
return mean
and then I create the object like below -
obj=child()
then also it works well, however I need to make it in the way I explained in the very begining. Can you please help me understand this context?
In the first example, the __init__ method expects two parameters:
self is automatically filled in by Python.
lists is a parameter which you must give it. It will try to assign this value to a new variable called self.myList, and it won't know what value it is supposed to use if you don't give it one.
In the second example, you have not written an __init__ method. This means that Python creates its own default __init__ function which will not require any parameters. However, the find_mean method now requires you to give it a parameter instead.
When you say you want to create it in the way you explained at the beginning, this is actually impossible: the class requires a value, and you are not giving it one.
Therefore, it is hard for me to tell what you really want to do. However, one option might be that you want to create the class earlier, and then add a list to it later on. In this case, the code would look like this:
import numpy as np
class Child:
def __init__(self, lists=None):
self.myList = lists
def find_mean(self):
if self.myList is None:
return np.nan
mean = np.mean(self.myList)
return mean
This code allows you to create the object earlier, and add a list to it later. If you try to call find_mean without giving it a list, it will simply return nan:
child = Child()
print(child.find_mean()) # Returns `nan`
child.myList = [1, 2, 3]
print(child.find_mean()) # Returns `2`
the code you have at the top of your question defines a class called child, which has one attribute, lists, which is assigned at the time of instance creation in the __init__ method. This means that you must supply a list when creating an instance of child.
class child:
def __init__(self, lists):
self.myList = lists
def find_mean(self):
mean=np.mean(self.myList)
return mean
# works because a list is provided
obj = child([44,22,55])
# does not work because no list is given
obj = child() # TypeError
If you create the class like in your second example, __init__ is no longer being explicitly specified, and as such, the object has no attributes that must be assigned at instance creation:
class child:
def find_mean(self, myList):
mean=np.mean(myList)
return mean
# does not work because `child()` does not take any arguments
obj = child([44,22,55]) # TypeError
# works because no list is needed
obj = child()
The only way to both have the myList attribute, and not need to specify it at creation would be to assign a default value to it:
class child:
def find_mean(self,myList=None):
mean=np.mean(myList)
return mean
# now this will work
obj = child()
# as will this
obj = child([24, 35, 27])

What is the proper way to share a list between class instances?

I have this example code
my_list = ["a","b","c","d"]
class A:
def __repr__(self):
return ', '.join(my_list)
def add(self, num):
my_list.append(num)
class_list = []
for x in range(5):
class_list.append(A())
class_list[x].add("class_%s" % (x))
print class_list[x]
The non-example code of mine is more complicated, but the idea is that I have multiple instances of the classes off doing a "thing". The global my_list is utilized across all instances. When certain logic is met within a class, that instance will modify the list. The rest of the classes will utilize that list to perform their logic as well. Any instance can add to the list, and all instances should be able to utilize the updated value.
Now in this example, the my_list is shared, but is this the correct way to do it?
A class attribute is usually better than a global, because then they're just sharing it with each other, rather than with everyone in the world.
To do that, move the my_list = ["a","b","c","d"] line under the class A:, and change every reference to my_list to self.my_list or A.my_list:
class A(object):
shared_list = []
def add(self, num):
self.my_list.append(num)
However, an instance attribute is often even better. If you assign the same list to a bunch of different variables, it's still just one list; changing it affects all those variables. So, you can do something like this:
class A(object):
def __init__(self, shared_list):
self.shared_list = shared_list
def add(self, num):
self.shared_list.append(num)
Now it's up to the code that uses the A objects to decide whether to give them all the same list. You can even create 20 instances that share one list, and 10 that share a different one:
list1 = []
group1 = [A(list1) for _ in range(20)
list2 = []
group2 = [A(list2) for _ in range(10)
The question is whether the caller, or the A class, or nobody at all is the one who should be making the decision of how "shared" the list is. The answer is different for different applications, so it's hard to give an answer for an abstract example with names like A and my_list.

Why does assigning to self not work, and how to work around the issue?

I have a class (list of dicts) and I want it to sort itself:
class Table(list):
…
def sort (self, in_col_name):
self = Table(sorted(self, key=lambda x: x[in_col_name]))
but it doesn't work at all. Why? How to avoid it? Except for sorting it externally, like:
new_table = Table(sorted(old_table, key=lambda x: x['col_name'])
Isn't it possible to manipulate the object itself? It's more meaningful to have:
class Table(list):
pass
than:
class Table(object):
l = []
…
def sort (self, in_col_name):
self.l = sorted(self.l, key=lambda x: x[in_col_name])
which, I think, works.
And in general, isn't there any way in Python which an object is able to change itself (not only an instance variable)?
You can't re-assign to self from within a method and expect it to change external references to the object.
self is just an argument that is passed to your function. It's a name that points to the instance the method was called on. "Assigning to self" is equivalent to:
def fn(a):
a = 2
a = 1
fn(a)
# a is still equal to 1
Assigning to self changes what the self name points to (from one Table instance to a new Table instance here). But that's it. It just changes the name (in the scope of your method), and does affect not the underlying object, nor other names (references) that point to it.
Just sort in place using list.sort:
def sort(self, in_col_name):
super(Table, self).sort(key=lambda x: x[in_col_name])
Python is pass by value, always. This means that assigning to a parameter will never have an effect on the outside of the function. self is just the name you chose for one of the parameters.
I was intrigued by this question because I had never thought about this. I looked for the list.sort code, to see how it's done there, but apparently it's in C. I think I see where you're getting at; what if there is no super method to invoke? Then you can do something like this:
class Table(list):
def pop_n(self, n):
for _ in range(n):
self.pop()
>>> a = Table(range(10))
>>> a.pop_n(3)
>>> print a
[0, 1, 2, 3, 4, 5, 6]
You can call self's methods, do index assignments to self and whatever else is implemented in its class (or that you implement yourself).

instance of object versus object value python

I having difficulties to understand the instance of an object in a list.
How to save the value of an object into a list without saving the instance?
This is not possible isnt it?
The colde below works but i would like to avoid to use .value as i might have several parameters.. not sure if i am clear enough..
class BougieBuffer:
def __init__(self):
self.bougiebuffer=deque(maxlen = 10000)
self.maximum = Maximum()
def update(self,bougie):
self.maximum.value = random.randint(-1,1)
bougie.maximum.value = self.maximum.value
self.bougiebuffer.append(bougie)
print len(self.bougiebuffer)
for i in range (len(self.bougiebuffer),0,-1):
print self.bougiebuffer[i-1].prixFermeture, self.bougiebuffer[i-1].maximum.value
I would have wrote naturally something like below but obviously this is not working and it returns the same value for all
bougie.maximum = self.maximum
You want to create a copy of the Maximum() instance to assign to the bougie.maximum attribute; use either copy.copy or copy.deepcopy:
from copy import deepcopy
bougie.maximum = deepcopy(self.maximum)
You'll need deepcopy if there are any attributes of Maximum that are mutable; a list, dict, set or another custom class instance are all mutable, but things like integers and strings are not.

Why is this simple python class not working?

I'm trying to make a class that will get a list of numbers then print them out when I need. I need to be able to make 2 objects from the class to get two different lists. Here's what I have so far
class getlist:
def newlist(self,*number):
lst=[]
self.number=number
lst.append(number)
def printlist(self):
return lst
Sorry I'm not very clear, I'm a bit new to oop, can you please help me cos I don't know what I'm doing wrong. Thanks.
In Python, when you are writing methods inside an object, you need to prefix all references to variables belonging to that object with self. - like so:
class getlist:
def newlist(self,*number):
self.lst=[]
self.lst += number #I changed this to add all args to the list
def printlist(self):
return self.lst
The code you had before was creating and modifying a local variable called lst, so it would appear to "disappear" between calls.
Also, it is usual to make a constructor, which has the special name __init__ :
class getlist:
#Init constructor
def __init__(self,*number):
self.lst=[]
self.lst += number #I changed this to add all args to the list
def printlist(self):
return self.lst
Finally, use like so
>>> newlist=getlist(1,2,3, [4,5])
>>> newlist.printlist()
[1, 2, 3, [4,5]]
You should use "self.lst" instead of "lst". Without the "self", it's just internal variable to current method.

Categories