How would i go about making a function to create a certain number of uniquely named variables at runtime, based on initial user input? For instance, user enters dimensions 400 x 400, (x and y), so i would want the function to create 1600 (400 * 400) variables, each to represent every different point on a grid 400 by 400.
What you really want is an array, a list or a tuple of 400*400 points.
So create a class that stores the information you want at each point, and then create a list of size 400*400 of those class objects.
You can do it this way:
width = 400
height = 400
m = [[0]*width for i in range(height)]
And then access points in your field like so:
m[123][105] = 7
To set point (123,105) to 7.
If you want to store more than just a number at each point, create a class like I suggested:
class MyClass:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
And then create your list of "MyClass" objects like so:
m = [[MyClass(0,0,0) for i in range(400)] for j in range(400)]
Are you sure you need to create a different variable for each point on the grid? If there are lots of points with default value of say 0, don't create an array with a bunch of 0s. Instead, create an empty dictionary D = {}. Store data as D[(x,y)] = anything. Access your data by D.get((x,y), 0). Where D.get(key, default value) This saves memory.
Btw, 400*400 is not 1600. Rather 160,000
Related
Beginner with Python, need some help to understand how to manage list of objects.
I built a list of simple objects representing a coordinate
class Point:
def __init__(self, x, y):
self.x=0
self.y=0
I create a empty list to store different points :
combs = []
point = Point(0, 0)
then I build different points using point and ever ytime appending to the list combs
For instance:
point.x=2
point.y=2
combs.append(point)
point.x=4
point.y=4
combs.append(point)
I expect that combs is something like [.. 2,2 4,4] on the contrary it's [....4,4 4,4].
It means that every time I change the instance of a point, I change all the points stored in the list with the latest value.
How can I do this?
The thing is when you're trying to change the value of x and y , you're expecting to have a new object (like a new x and y with different values) but you aren't. What happens is I think is whenever you set point.x =4 and point.y = 4 is you're just changing the attribute x and y in your class
take a look at this link. This helped me a lot, I encountered that kind of problem or should I say similar of yours
I suggest using the copy package
https://www.programiz.com/python-programming/shallow-deep-copy
You are appending the same variable to the combine. You need to create a new Point object and initialize it with the new values.
combine = []
p = Point(2,2)
combine.append(p)
p = Point(4,4)
combine.append(p)
This is because Python is using reference count for the garbage collection.
In your example you create point which increments the ref count.
Then you pass it to the list which increment the count. Then change the value and pass the same variable. Which increments the count once again.
Think of it more like passing a reference or memory pointer of the point variable to the list. You gave the list twice the same pointer.
So you need to create different variables or make a deep copies https://docs.python.org/3.8/library/copy.html
Custom classes, unless they are built on an immutable type, are mutable. This means that you have appended a reference to your list, and that changing the value of the references will change every instance of the reference. Consider this:
class Test():
def __init__(self, a):
self.a = a
>>> t1 = Test(1)
>>> t1.a
1
>>> t2 = t1
>>> t2.a
1
>>> t2.a = 2
>>> t2.a
2
>>> t1.a
2
See how changing t2 also changed t1?
So you have a few options. You could create new points instead of reusing old ones, you could use a copy() method, or you could write a method into your Point class that exports something immutable, like a tuple of the (x, y) values and append that to your list, instead of appending the entire object to your list.
You are working with only a single Point. Construct a second one. See commented line below.
point = Point(0, 0)
point.x=2
point.y=2
combs.append(point)
point = Point(0, 0) # add this
point.x=4
point.y=4
combs.append(point)
By the way, your __init__ ignores its parameters -- throws them away. A better version is below. We assign self.x=x to make use of the parameter. (Likewise y).
def __init__(self, x, y):
self.x=x
self.y=x
You need to pass one value into point
How to add an item to your list
combs = []
point = 1 # For example
combs.append(point)
Use command lines to study
Try to use BASH or CMD... Use command lines... They will have instant feedback of your code
Good place to find basic stuff
Try to see the examples on w3scholl. It is a great place. Here is the link for W3Scholl - Python - List
Understand basics first
Before you jump into classes, try to understand lists very well! You will learn more and build a solid knowledge if you take a step by step growing! Keep pushing!!!
If a class method creates a data frame within it when an object of that class calls the method, will the data for the data frame persist after the execution of the method?
Taking the code below as an example:
class some_class():
def some_method(self):
some_data = pd.DataFrame({"a":[1,2,3,4],
"b":[5,6,7,8]
})
return some_data
a = some_class()
b = a.some_method()
After the execution of the call to a.some_method() will the dataframe be stored in the object?
I want to be able to create multiple objects and use them to return data based on the methods defined in those objects but I'm concerned that if the object stores the data as well then in effect I'll be storing the same data twice (in data frame b and in the object an in the example above).
If you want to store a value inside a class, then a method must assign to self. For example:
class some_class():
def some_method(self):
self.some_data = pd.DataFrame({"a":[1,2,3,4],
"b":[5,6,7,8]
})
return self.some_data
a = some_class()
b = a.some_method()
This will store a "label" to the data within your instance of some_class (which you should capitalize as SomeClass btw if you want to follow the popular convention) with the label some_data. The variable b is also an alias to this data - both a.some_data and b refer to the exact same data. There is no copy.
This is useful and saves memory but you need to be aware that you're working with labels (references) to the same data. If you want a.some_data and b to be separate instances of data, you'll need to explicitly copy the data.
Python variables behave differently to many other popular languages. The name of the variable, e.g. b, is really just a label attached to some value. Therefore if you assign c = b, you haven't copied the data, you've simply assigned a new label to the original value. For immutable types like primitive numeric types, this isn't much different to copying the value, but for more complex types (lists, dicts, data frames, etc) you need to be aware that you're dealing with labels.
The class you do won't, since you have self while there isn't __init__, so do:
class some_class():
def some_method():
some_data = pd.DataFrame({"a":[1,2,3,4],
"b":[5,6,7,8]
})
return some_data
print(some_class.some_method())
Output:
a b
0 1 5
1 2 6
2 3 7
3 4 8
I'm trying to create some simple objects that are defined dynamically through a class - to allow me to rapidly iterate through the creation of all possibilities of these objects.
class NSObjects:
def __init__(self, shape, position, shading):
self.shape = shape
self.position = position
self.shading = shading
def __str__(self):
return '{} - {} - {}'.format(self.shape(), self.position(), self.shading())
def NSGenerator_1():
for i in range (0,3):
obj_1_i = NSObjects(shape_init_top + i, posn_init_top+i, shading_init_top+i)
for i in range (3,6):
obj_1_i = NSObjects(shape_init_mid + i, posn_init_mid+i, shading_init_mid+i)
for i in range (6,9):
obj_1_i = NSObjects(shape_init_mid + i, posn_init_mid+i, shading_init_mid+i)
NSGenerator_1()
print(obj_1_2)
At the moment it is telling me that obj_1_2 doesn't exist. For the purpose of this you can assume that I have defined all the init variables to start at 0, 1 or 2 elsewhere in the code. I am basically trying to create a series of objects which will have properties as defined by a mathematical formula.
Thanks in advance for any help you can provide (I only started coding a few weeks ago so this might be a very silly question!)
You only ever assigned to obj_1_i, not obj_1_2, and it was local to the function. There is no way for Python to tell that the _i was meant as a separate variable instead of part of the longer variable name. For a quick fix, try replacing the
obj_1_i = parts with globals()[f'obj_1_{i}'] =.
But rolling numeric indexes into the variable names like that (_1_2) is a code smell. A better design is to actually use them as indexes to a data structure, like a list or dict.
For example, define
obj = {} at the top level (outside of any class or function).
Then you can replace obj_1_2 everywhere with obj[1, 2], etc. If you wrote them that way,obj[1, i] would work as you expect inside those for loops.
My code is like this:
size = 50
class cube:
def setsize(sizechoice):
size = sizechoice
However, when I do cube.setsize(200) and then print(size) It says 50 not 200. Idk what the problem is, I'm new to python and I just learned how classes work so I hope you can help me!
size = 50
Here size is a global variable; there's only one instance of it and it's the same throughout the whole module.
class cube:
def setsize(sizechoice):
size = sizechoice
Here however size refers to a local variable, also named size, which shadows (hides) the global one. This comes from the fact that Python assumes that if you are inside a function and assign to a variable without explicitly saying that you refer to a global, you want to create a local with that name. So, as it is, if you do
c = cube()
c.setsize(100)
print(size)
print(c.size)
you'll get 50 for the first print (as the global size won't be affected), and an error on the second one (as you didn't create any instance attribute named size).
Now, if you want to explicitly refer to the global size you would have to do:
class cube:
def setsize(sizechoice):
global size
size = sizechoice
so if you do
c = cube()
c.setsize(100)
print(size)
it'll print 100; but most probably this is not what you want: an instance method generally is expected to affect instance-specific attributes, not globals - IOW, in this implementation all cubes would have the same size!
c = cube()
d = cube()
c.setsize(100)
print(size) # prints 100
d.setsize(200)
print(size) # prints 200
# why did I even bother creating two instances?
What you probably want is to have a size specific of each cube; to do this, you must esplicitly refer to self.size, as in Python there's no "implicit this" rule (such as in Java, C#, C++, ...). You may also want to provide an easy way to set it on construction, so you should probably do:
class cube:
def __init__(self, size):
self.size = size
def setsize(self, size):
self.size = size
and get rid of the now useless global size.
c = cube(100)
d = cube(200)
print(c.size) # 100
print(d.size) # 200
d.setsize(300)
print(c.size) # 100
print(d.size) # 300
Finally, you may even get rid of the setsize setter: it doesn't add anything useful over straight assigning to the size member, and if you want to add validation logic on assignment later you can always change size to be a property.
I see, that you are really new in Python. Take your time and go thru python class tutorial.
Because you are new I wrote simple code, that should work for you. With classes you always initialize class, and with that you get an object.
size = 50 # Does not influence the class
class cube: # Class name
def __init__(self, size): # Initialization
self.size = size # Sets the size of cube
def setSize(self, sizechoice): # Set method (sets size of cube)
self.size = sizechoice
def getSize(self): # Get method (gets size of cube)
return self.size
c = cube(200) # Creation of object cube with size of 200
print(c.getSize()) # Gets the cube size and prints it out
I hope it helps :)
Best of luck with future programming.
You need to set size as a class or instance attribute.
To set a class or instance attribute, you need to do this:
class cube:
def __init__(self):
self.size = 0
def setsize(self, sizechoice):
self.size = sizechoice
so you have to set class or instance attributes inside the __init__ method with the self. keyword.
Now you can do this:
cube = cube()
cube.setsize(200)
print(cube.size)
and it would print 200.
I would recommend to change the class name to Cube (upper C) or something
Welcome to S.O. #pytusiast. The issue is that you are setting the size locally within that function and anywhere else the value is what it was before.
You can define size as a global variable:
class cube:
def setsize(sizechoice):
global size
size = sizechoice
but I'm not sure this is the best way as size now is a global variable.
So I'm trying to store some lists into a list that belongs to a "person". I tried to do this with some classes:
class data():
# Contains list of x, y, z, time lists
x = []
y = []
z = []
time = []
class data_main():
# Contains data for each Pi
data_plot = data()
data_overflow = data()
piList = ["Lui", "Wing"]
rpi_data = {}
for pi in piList:
rpi_data[pi] = data_main()
rpi_data["Lui"].data_plot.x = 10
rpi_data["Wing"].data_plot.x = 99
print(rpi_data["Lui"].data_plot.x)
print(rpi_data["Wing"].data_plot.x)
Problem is, I won't actually know how many people there will be. Therefore I want to create a dictionary of the class "data_main" belonging to different people.
When I try to do this, the console results are:
99
99
When I'd rather it do: 10 and 99 respectively. I'm worried that in the for loop:
for pi in piList:
rpi_data[pi] = data_main()
I'm really just designating the same instance of data_main() to the dictionary entries, when really I'd prefer unique ones, so that they can each have their own values.
How do I achieve this?
EDIT: I did more digging and it turns out data_plot for both instances of data_main() is pointing to the same address. How do I avoid this (ie, every time I init a new data_main() class, that I create new data_plot() and data_overflow() classes too?)
Your data_plot and data_overflow are class attributes not instance attributes ,so they are initialized when the class gets defined , not when the instance is created, and they get shared between all the instances of the class . You should create them as instance attributes in __init__() method. Example -
class data_main:
def __init__(self):
# Contains data for each Pi
self.data_plot = data()
self.data_overflow = data()
Also in your data class , the attributes in it are also class attributes, you should make them instance attributes as well. Example -
class data:
def __init__(self):
# Contains list of x, y, z, time lists
self.x = []
self.y = []
self.z = []
self.time = []
Finally, first in data class you are defining x as a list, and then you are changing the x for rpi_data["Lui"].data_plot.x to a number , when you do -
rpi_data["Lui"].data_plot.x = 10 #or the 99 one.
If you intended to append the number to the x list , you should use -
rpi_data["Lui"].data_plot.x.append(10)