python too many self in class - python

I'm learning Python OOP and trying to convert a Java class to a Python class
See page 15 in this PDF for Java code google doc link
class QuickFindUF:
"""docstring for QuickFindUF"""
def __init__(self, n):
self.id = []
for e in range(n):
self.id.append(e)
def connected(self,p,q):
return self.id[p]==self.id[q]
def union(self,p,q):
self.pid = self.id[p]
self.qid = self.id[q]
for i in range(len(self.id)):
if(self.id[i]==self.pid):
self.id[i]=self.qid
quf = QuickFindUF(9)
quf.union(3,4)
print quf.connected(3,4)
There are 16 self keywords in this class. Is there a better way to write this class?

Yea, you don't want to assign these variables to self, these are local variables:
def union(self,p,q):
self.pid = self.id[p]
self.qid = self.id[q]
for i in range(len(self.id)):
if(self.id[i]==self.pid):
self.id[i]=self.qid
Should be:
def union(self,p,q):
pid = self.id[p]
qid = self.id[q]
for i in range(len(self.id)):
if self.id[i] == pid:
self.id[i] = qid
You only use self when referring to instance variables, not to any variable inside of a method.

You could use some shortcuts:
class QuickFindUF:
"""docstring for QuickFindUF"""
def __init__(self, n):
self.id = range(n)
def connected(self,p,q):
_id = self.id
return _id[p]==_id[q]
def union(self,p,q):
_id = self.id
pid = _id[p]
qid = _id[q]
for (k, _i) in enumerate(_id):
if (_i == pid):
_id[k]=qid
Note the simplification in __init__, as pointed by #katrielalex, and the use of enumerate instead of looping on range(len(self.id)).
Using shortcuts can be a bit more efficient (as you save a call to __getattr__), but should not impair readability.

Make use of list(range()) and enumerate like so:
class QuickFindUF:
def __init__(self, n):
self.id = list(range(n))
def connected(self, i, j):
return self.id[i] == self.id[j]
def union(self, i, j):
x = self.id[i]
y = self.id[j]
for index, elem in enumerate(self.id):
if elem == x:
self.id[index] = y
But even better: Don't forget that Python is multi-paradigm. If methods have generic aspects, then outsource these to pure functions:
def map_each_x_to_y(array, x, y):
for i, elem in enumerate(array):
if elem == x:
array[i] = y
return array
def map_ith_values_to_jth(array, i, j):
x = array[i]
y = array[j]
return map_each_x_to_y(array, x, y)
This more declarative style might seem verbose, but it makes the desired behaviour of the union method's intent easier to understand:
def union(self, i, j):
self.id = map_ith_values_to_jth(self.id, i, j)
And makes your code more re-usable for other things.

Related

How can I modify my __repr__ to respresent correctly?

My __repr__ method works fine using objects created in it's class, but with objects that were created with the help of importing a library and using methods from it, it only represented the memory address...
from roster import student_roster #I only got the list if students from here
import itertools as it
class ClassroomOrganizer:
def __init__(self):
self.sorted_names = self._sort_alphabetically(student_roster)
def __repr__(self):
return f'{self.get_combinations(2)}'
def __iter__(self):
self.c = 0
return self
def __next__(self):
if self.c < len(self.sorted_names):
x = self.sorted_names[self.c]
self.c += 1
return x
else:
raise StopIteration
def _sort_alphabetically(self,students):
names = []
for student_info in students:
name = student_info['name']
names.append(name)
return sorted(`your text`names)
def get_students_with_subject(self, subject):
selected_students = []
for student in student_roster:
if student['favorite_subject'] == subject:
selected_students.append((student['name'], subject))
return selected_students
def get_combinations(self, r):
return it.combinations(self.sorted_names, r)
a = ClassroomOrganizer()
# for i in a:
# print(i)
print(repr(a))
I tried displaying objects that don't rely on anther library, and they dispayed properly.
The issue I was facing was linked to me not understanding the nature of the object. itertools.combinations is an iterable, and in order to represent the values stored I needed to either:
unpack it inside a variable like:
def get_combinations(self, r):
*res, = it.combinations(self.sorted_names, r)
return res
Iter through it inside a loop and leave the original code intact like
for i in a.get_combinations(2):
print(i)
I prefer the second solution

How do I pass the second parameter to a function without passing the first?

I have the following code:
class Player:
def __init__(self, N, V, F, L, T):
self._Name = N
self._VPs = V
self._Fuel = F
self._Lumber = L
self._PiecesInSupply = T
def AddPiecesInSupply(self, T):
modify = T+1
modify.T
return T
I have been given code and been asked to make changes. "self._PiecesInSupply = T" is a protected attribute. I have been asked to create a method to allow "self._PiecesInSupply = T" to be modified so I created the code:
def AddPiecesInSupply(self, T):
modify = T+1
modify.T
return T
This is how I call Player.AddPiecesInSupply
Player.AddPiecesInSupply(0)
however the value 0 is being passed to the parameter self not T. Any ideas on how to pass the value 0 to T?
Thanks
Maybe you want something like this:
class Player:
def __init__(self, N, V, F, L, T):
self._Name = N
self._VPs = V
self._Fuel = F
self._Lumber = L
self._PiecesInSupply = T
def AddPiecesInSupply(self, T):
self._PiecesInSupply += T
return T
player = Player('name','VPs',1,2,3)
player.AddPiecesInSupply(0)
print(player._PiecesInSupply)

Checking if contained object has changed

I am working on creating a module with a class that acts as a container for a list of another created class. Is there a way for the container class to be able to tell if any of the objects it contains has changed?
Here is an example:
class Part:
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
def __getitem__(self, key):
return self.parts[key]
This is what I get if I try to change any of the Part properties in the Assembly:
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
3
I know that I can create additional methods that will call the update method if I replace, delete, or add Part objects to the Assembly, but is there any way to have the Assembly notified if any of the contained Part properties have changed?
The answer is in your question. Use a property.
class Part:
_size = 0
assembly = None
#property
def part_size(self):
return self._size
#part_size.setter
def part_size(self, value):
self._size = value
if self.assembly: # only notify if an Assembly is set
self.assembly.update()
def set_assembly(self, assembly):
self.assembly = assembly
def __init__(self, size):
self.part_size = size
class Assembly:
def __init__(self, *parts):
self.parts = list(parts) # `parts` are all Part() objects
for part in self.parts:
part.set_assembly(self) # reference to self needed to notify changes
self.update()
def update(self):
self.assy_size = 0
for each in self.parts:
self.assy_size += each.part_size
In this version of Assembly the constructor sets a reference on the Part to itself. This way it can update the assembly when the part_size changes. Use it as the example in your question.
>>>x = Part(1)
>>>y = Part(1)
>>>z = Part(1)
>>>u = Assembly(x, y, z)
>>>u.assy_size
3
>>>u[0].part_size = 4
>>>u.assy_size
6
If update isn't an expensive operation (in your example it isn't, but maybe in reality you have thousands of parts), you could calculate the size ad-hoc using a property:
class Assembly:
def __init__(self, *parts):
self.parts = list(parts)
#property
def assy_size(self):
result = 0
for each in self.parts:
result += each.part_size
return result
which can be accessed the same way: assembly.assy_size.
The calculation can also be simplified:
#property
def assy_size(self):
return sum(part.part_size for part in self.parts)

Getting Pylint E1101 - Instance has no such member

Pylint doesn't like this code:
class sequence(list):
def __init__(self, n):
list.__init__(self)
self += self._generate_collatz_seq(n)
self.pivots = self._generate_pivots()
self.data = self._make_data()
def _collatz_function(self, n):
if n % 2 == 0:
return(int(n/2))
else:
return(3*n + 1)
def _generate_collatz_seq(self, x):
int(x)
sequence_holder = []
while x != 1:
sequence_holder.append(x)
x = self._collatz_function(x)
sequence_holder.append(1)
return sequence_holder
def _generate_pivots(self):
pivots_holder = []
for element in self:
if element % 2 != 0:
pivots_holder.append(element)
return pivots_holder
def _make_data(self):
data_holder = []
data_holder.append(len(self))
data_holder.append(len(self.pivots))
return data_holder
It says
E1101: Instance of 'sequence' has no 'pivots' member(56,36)
This is before I have made any instances of sequence. I'm sure I haven't gone about my task in the most efficient way, but I can't see that I've done anything wrong.

Print dict with custom class as values wont call their string method?

I was messing around with classes in python and wrote 2 little ones:
class ClaElement:
start = None
end = None
basesLeft = None
orientation = None
contig = None
size = None
def __init__(self, contig, start, end, orientation, basesLeft=None):
self.contig = contig
self.start = start
self.end = end
self.orientation = orientation
self.basesLeft = basesLeft
self.size = self.end - self.start
def __str__(self):
return "{ClaElement: "+str(self.contig)+"_"+str(self.start)+"_"+str(self.end)+"_"+str(self.orientation)+"}"
def getSize(self):
return self.size
class ClaCluster:
contig = None
clusterElements = []
def __init__(self, contig, firstElement):
self.contig = contig
self.addElement(firstElement)
def addElement(self, claElement):
self.clusterElements.append(claElement)
def getFirst(self):
return self.clusterElements[0]
def getLast(self):
return self.clusterElements[-1]
def getElements(self):
return self.clusterElements
def getContig(self):
return self.contig
def __str__(self):
return "{ClaCluster: "+str(self.contig)+" "+str(len(self.clusterElements))+" elements}"
And my test-main:
from ClaElement import ClaElement
from ClaCluster import ClaCluster
if __name__ == '__main__':
ele = ClaElement("x",1,2,"left")
claDict = dict()
cluster = ClaCluster("x", ele)
claDict["hello"] = cluster
print(claDict)
print(claDict["hello"])
print(ele)
This leads to the following output:
{'hello': <ClaCluster.ClaCluster object at 0x7fe8ee04c5f8>}
{ClaCluster: x 1 elements}
{ClaElement: x_1_2_left}
Now my question is why is the output of my first print the memory address even though I provided a functioning string-method for my class ClaCluster? Is there a way to get the method invoked when I am printing the dictionary or do I have to iterate by hand?
The __str__() method of the built-in dict type uses the __repr__() method of your class, not __str__(). Simply rename your method, and all should work fine.

Categories

Resources