Extending list class to make a list of objects - python

I have a particle list of objects of type Particle, which takes two parameters, position and energy:
class Particle(object):
def __init__(self, pos, energy):
self.x = pos
self.E = energy
The only way I've managed to do it so far is to create a list of particles using a list comprehension:
number_of_particles = 10
initial_energy = 0
particle_list = [Particle(initial_energy,i) for i in range(number_of_particles)]
which now allows me to do things like:
particle_list[0].x
which is what I want.
However, what I would really, really like is to do something as follows:
particle_list = ParticleList(no_of_particles, initial_energy)
and it create the exact same list.
I assume I have to extend the list class somehow but I'm at a loss as to how to do this.

Why not just build a function to do this for you. You could do something simple like:
def ParticleList(no_of_particles, initial_energy):
return [Particle(initial_energy,i) for i in range(number_of_particles)]
This should be a simple way of getting your list.

class Particle(object):
def __init__(self, pos, energy):
self.x = pos
self.E = energy
#classmethod
def make_particle_list(self, no_particles, initial_energy=0):
return [Particle(i, initial_energy) for i in range(no_particles)]
# this is just for display purposes
def __repr__(self):
return 'pos: {p.x} - Energy: {p.E}'.format(p=self)
This offers you a little flexibility. If you only need one particle you can make just one the normal way or:
>>> lst = Particle.make_particle_list(10)
>>> lst
[pos: 0 - Energy: 0, pos: 1 - Energy: 0, pos: 2 - Energy: 0, pos: 3 - Energy: 0, pos: 4 - Energy: 0, pos: 5 - Energy: 0, pos: 6 - Energy: 0, pos: 7 - Energy: 0, pos: 8 - Energy: 0, pos: 9 - Energy: 0]
This also allows you to pass in a different initial_energy if you ever need a different value.
You also had your arguments backwards in your example. You had initial_energy as the first positional argument in your list comprehension but you have it as the second in your __init__() method.

Create your class with your custom __init__ method.
class ParticleList(list):
def __init__(self, num, energy):
self.particle_list = [Particle(energy,i) for i in range(num)]
particles = ParticleList(2, 0).particle_list
for particle in particles:
print (particle.x, particle.E)
>>(0, 0)
>>(0, 1)
You may create your own method and not use the __init__, this way you will be able to simply return the created list and not assign it to a member (__init__ is not allowed to have a return value).
class ParticleList(list):
def create_list(self, num, energy):
return [Particle(energy,i) for i in range(num)]
my_list = ParticleList().create_list(2, 0)
And as others have said, you don't even need the class and can get away with only creating a function:
def create_list(num, energy):
return [Particle(energy,i) for i in range(num)]
my_list = create_list(2, 0)

Related

Make a data profiler class that takes as params on the init a list of data

I need this class to include the following methods only using self:
get_summary_stats: should calculate the mean, min and max.
min_max_scale: converts the array to 0-1 values.
score_scale: converts the array to zscores.
I'm trying it this way without luck, when you run it it smoothly goes by but when I add the last line it gives me an error saying __init__() takes 1 positional argument but 2 were given
class summary_stats_class():
def __init__(self):
min_my_list = min(self)
max_my_list = max(self)
mean_my_list = sum(self)/len(self)
std_my_list = np.std(self)
def get_summary_stats(self):
print(min_my_list,max_my_list,mean_my_list)
def min_max_scale(self):
print((i - min_my_list) / (max_my_list-mean_my_list) for i in self)
def zscore_scale(self):
print((i - mean_my_list) / std_my_list for i in self)
summary_stats_class([1,2,3,4,5,6,7,8,9,10])
I've tried
1)adding list to summary_stats_class(list) because I read it in other question, as well as
2)adding self.list = [] after __init__ piece and finally
3) adding , *args, **kwargs to each method without any luck, could someone please help? Thanks in advance!
The self is used to indicate the class level variables. The documentation on classes has some examples showing how to use the self to declare instance variables and use them in class methods.
I have updated the class to mitigate the error:
import numpy as np
class summary_stats_class():
def __init__(self, data):
self.data = data
self.min_data = min(data)
self.max_data = max(data)
self.mean_data = sum(data) / len(data)
self.std_data = np.std(data)
def get_summary_stats(self):
return self.min_data, self.max_data, self.mean_data
def min_max_scale(self):
return [(i - self.min_data) / (self.max_data - self.min_data) for i in
self.data]
def zscore_scale(self):
return [(i - self.mean_data) / self.std_data for i in self.data]
if __name__ == "__main__":
ssc = summary_stats_class([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(ssc.get_summary_stats())
print(ssc.min_max_scale())
print(ssc.zscore_scale())
Output:
(1, 10, 5.5)
[0.0, 0.1111111111111111, 0.2222222222222222, 0.3333333333333333, 0.4444444444444444, 0.5555555555555556, 0.6666666666666666, 0.7777777777777778, 0.8888888888888888, 1.0]
[-1.5666989036012806, -1.2185435916898848, -0.8703882797784892, -0.5222329678670935, -0.17407765595569785, 0.17407765595569785, 0.5222329678670935, 0.8703882797784892, 1.2185435916898848, 1.5666989036012806]
Explanation:
We passed the list as a parameter when we created the ssc object of the class summary_stats_class.
In the constructor of summary_stats_class, we set the parameter list to self.data variable.
We then calculated the min, max, and mean of the data and set them to self.min_data, self.max_data, and self.mean_data respectively. We set them to self so that these variables can be used from any other methods of the class.
In the get_summary_stats, min_max_scale, and zscore_scale method, we used the previously calculated variables using self.VARIABLE_NAME.
References:
Documentation on classes
Z-score normalization

Simple loop bug which i forgot [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I want to build a list. The list have the number of values asked for from the user, and can't pass the limit and can't repeat, but I don't know where my error is. The program doesn't build the list by the way I want to. It's a bit hard to explain, but run the code by yourself with the attributes:
class Mundo:
def __init__(self):
self.existValues = list()
self.world = self.grid()
self.blues = self.blue()
print(f"Map: {self.world}\n"
f"Exist Values: {self.existValues}\n"
f"Blue values: {self.blues}")
# self.theArray = [[self.world, ], [self.world, ]]
def grid(self):
while True:
self.size = input("Size of Map: ")
if self.size.isnumeric() and int(self.size) >= 2:
self.size = int(self.size)
intSize = self.size * self.size
mapH = list()
gridMap = list()
for elementos in range(1, intSize + 1):
if len(mapH) == self.size - 1:
mapH.append(elementos)
gridMap.append(mapH)
mapH = []
else:
mapH.append(elementos)
return gridMap
def blue(self):
while True:
try:
qt = int(input("How many blues to spawn?"))
except ValueError:
continue
posBlue = list()
controle = list()
control = False
for _ in range(qt):
while not control:
x = rint(1, self.size)
y = rint(1, self.size)
controle.append([x, y])
for elements in controle:
if elements in self.existValues:
control = True
else:
posBlue.append([x, y])
self.existValues.append([x, y])
control = False
return posBlue
If i run the code (being that qt == 2, and self.size == 4 ), one, two or three times the code outputs a list that contains 3 or 2 values, and sometimes 4.
I will run 3 times to show.
Output 1:
Only 3 values in blues, i asked for 4.
Output 2:
Only 2, i ask for 4.
Output 3:
Again.
I need that output that the user ask.
Here's your code in more of an MRE form:
from random import randint
def blue(qt: int, size: int):
existValues = []
posBlue = list()
controle = list()
for _ in range(qt):
x = randint(1, 4)
y = randint(1, 4)
controle.append([x, y])
for elements in controle:
if elements in existValues:
continue
else:
posBlue.append([x, y])
existValues.append([x, y])
return posBlue
print(len(blue(2, 4))) # prints 2 because odds are good of getting 2 uniques
print(len(blue(10, 4))) # prints 8 because it missed some
print(len(blue(100, 4))) # prints 16 because that's the maximum number
By testing with different quantities, it's easy to see where the problem is -- you're dependent on random chance to generate unique combinations, so there's not a guarantee that each iteration of your for loop is going to find a combination that doesn't already exist.
The easy way to do this is to generate an exhaustive list of combinations and then select them randomly:
from itertools import product
from random import sample
def blue(qt: int, size: int):
possibilities = list(product(range(1, size+1), range(1, size+1)))
return sample(possibilities, qt)
print(len(blue(2, 4))) # prints 2
print(len(blue(10, 4))) # prints 10
print(len(blue(100, 4))) # ValueError: Sample larger than population
Note that this raises an exception if qt is greater than size^2. You can decide if you want to handle this a different way -- maybe given a sufficiently large quantity repeats are acceptable? Here's a simple approach to making that fix: multiply possibilities by the quantity necessary to ensure that it's at least as big as qt.
def blue(qt: int, size: int):
possibilities = list(product(range(1, size+1), range(1, size+1)))
possibilities *= 1 + (qt - 1) // len(possibilities)
return sample(possibilities, qt)

Python - find a substring in string and record position of substrings in a tuple

I am getting the below error when I check this function. I am very close, but not understanding why the loop continues after reaching the end of the text. How do I get it to stop searching for the substring after it has reached the end of the string to search?
Error:
The call findall('how now brown cow', 'ow') returns (1, 5, 10, 15, -1, 1, 5, 10, 15, -1, 1, 5, 10, 15, -1, 1, 5), not (1, 5, 10, 15).
Note - I have searched through the similar problems here in stack overflow, but do not see anything that is helping me resolve this using the method I have prepared so far. Please advise. Greatly appreciated for this newbie!! :)
def findall(text,sub):
"""
Returns the tuple of all positions of substring sub in text.
If sub does not appears anywhere in text, this function returns the
empty tuple ().
Examples:
findall('how now brown cow','ow') returns (1, 5, 10, 15)
findall('how now brown cow','cat') returns ()
findall('jeeepeeer','ee') returns (1,2,5,6)
Parameter text: The text to search
Precondition: text is a string
Parameter sub: The substring to search for
Precondition: sub is a nonempty string
"""
result = ()
x = 0
pos = 0
for x in range(len(text)):
if sub in text:
# find pos of sub in text starting from pos in text
pos = introcs.find_str(text,sub,pos)
# record result
result = result + (pos,)
# increase accumulator by 1 to find next pos of sub in text
pos = pos + 1
else:
# when sub is not present in text
result = ()
return result
Using recursive
def findall(text,sub,list1=[],l=0):
try:
a = text.index(sub)
except:
return list1
list1.append(a+l)
l+=1+a
return findall(text[a+1:], sub, list1,l)
text = 'aascfvbscaaasc'
sub = 'a'
b=findall(text,sub)
print(text)
print(b)
# [0, 1, 9, 10, 11]
Currently you are iterating equal to the number of characters in your string. If you want to simply end upon a condition you can go
if pos >= len(text):
break
but I don't think this is the best idea. You seem to ignore x, which is your position according to your loop statement. If you want something with a more dynamic position you might consider:
pos = 0
while pos < len(text):
# do your current logic
Still, this seems arduous. A two-liner compact version that is less optimized but do everything you want would look like this:
def findall(text,sub):
result = (text.find(sub, x) for x in range(len(text)))
return tuple(sorted(set(i for i in result if i > -1)))
Another, more advanced, option would be to write your own custom iterator (not actually that hard) and could be used for either your solution, if you want it as a for loop over a while loop, or for optimizing my two-liner:
class PositionBySubIterator:
def __init__(self, text, sub):
"""
Initialize.
"""
self.pos = 0
self.text = text
self.sub = sub
def __iter__(self):
"""
Makes it an iterator. Resets.
"""
self.pos = 0
return self
def __next__(self):
"""
Makes it an iterable. Determines the variable for each iteration.
"""
new_pos = self.text.find(self.sub, self.pos)
self.pos = new_pos + 1
if new_pos == -1 or new_pos > len(self.text):
raise StopIteration
return new_pos
then use:
def findall(text,sub):
return tuple(i for i in PositionBySubIterator(text, sub))

Python - Implement iteration over certain class attributes on given order

I have a Position class, and it has two attributes, Lat and Lon.
I would like the following API by implementing iterator protocol (but some googling just confused me more):
pos = Position(30, 50)
print pos.Latitude
> 30
print pos.Longitude
> 50
for coord in pos:
print coord
> 30
> 50
print list(pos)
> [30, 50]
You need to define an __iter__ method:
class Position(object):
def __init__(self, lat, lng):
self.lat = lat
self.lng = lng
def __iter__(self):
yield self.lat
yield self.lng
pos = Position(30, 50)
print(pos.lat)
# 30
print(pos.lng)
# 50
for coord in pos:
print(coord)
# 30
# 50
print(list(pos))
# [30, 50]
PS. The PEP8 style guide recommends reserving capitalized names for classes. Following the conventional will help others understand your code more easily, so I've resisted the urge to use your attribute names, and have instead replaced them with lat and lng.

Adding records to a numpy record array

Let's say I define a record array
>>> y=np.zeros(4,dtype=('a4,int32,float64'))
and then I proceed to fill up the 4 records available. Now I get more data, something like
>>> c=('a',7,'24.5')
and I want to add this record to y. I can't figure out a clean way to do it. The best I have seen in np.concatenate(), but that would require turning c into an record array in and of itself. Is there any simple way to tack my tuple c onto y? This seems like it should be really straightforward and widely documented. Apologies if it is. I haven't been able to find it.
You can use numpy.append(), but as you need to convert the new data into a record array also:
import numpy as np
y = np.zeros(4,dtype=('a4,int32,float64'))
y = np.append(y, np.array([("0",7,24.5)], dtype=y.dtype))
Since ndarray can't dynamic change it's size, you need to copy all the data when you want to append some new data. You can create a class that reduce the resize frequency:
import numpy as np
class DynamicRecArray(object):
def __init__(self, dtype):
self.dtype = np.dtype(dtype)
self.length = 0
self.size = 10
self._data = np.empty(self.size, dtype=self.dtype)
def __len__(self):
return self.length
def append(self, rec):
if self.length == self.size:
self.size = int(1.5*self.size)
self._data = np.resize(self._data, self.size)
self._data[self.length] = rec
self.length += 1
def extend(self, recs):
for rec in recs:
self.append(rec)
#property
def data(self):
return self._data[:self.length]
y = DynamicRecArray(('a4,int32,float64'))
y.extend([("xyz", 12, 3.2), ("abc", 100, 0.2)])
y.append(("123", 1000, 0))
print y.data
for i in xrange(100):
y.append((str(i), i, i+0.1))
This is because concatenating numpy arrays is typically avoided as it requires reallocation of contiguous memory space. Size your array with room to spare, and then concatenate in large chunks if needed. This post may be of some help.

Categories

Resources