Python: Can't copy pygame surface objects - python

I have a class:
class personInfo:
def __init__(self,name,age,height,hairColour,face):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
self.face = face
I have several images here that I load in using the pygame module.
yellowFace = pygame.image.load("happy.png")
blueFace = pygame.image.load("sad.png")
redFace = pygame.image.load("angry.png")
I created an array that holds instances of that class. Here I am populating it.
personArray = []
while len(array) != 10:
personArray.append(personClass("John",32,6,"Blond",yellowFace))
I would like to store the actual variables of that class (name, age height,hairColour, etc) in a variable I will call "personStorage". However, I can't have that var be mutatable since I need to access that var's values and change them. Doing this can't change the values of any instances inside the personArray. How would I do this?
EDIT:
I also can't seem to be able to Copy this class because I get a type error that says: "can't pickle pygame.surface objects" since I have a value of a surface object within that class.

If I understood what you're trying to do :
class PersonInfo:
def __init__(self,name,age,height,hairColour):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
def toList(self):
return [self.name, self.age, self.height, self.hairColour]
By the way, class names' first letter is always uppercase.
EDIT : To achieve what you're trying to do :
old = PersonInfo("old", 1, 2, "blue")
new = PersonInfo(*old.toList())
new.name = "new"
print(old.name) #'old'

Use the copy module:
The function copy() shallow copy operation on arbitrary Python objects.
import copy
class PersonInfo:
def __init__(self, name, age, height, hair_colour):
self.name = name
self.age = age
self.height = height
self.hairColour = hair_colour
personArray = []
for x in range(20, 24):
personArray.append(PersonInfo("John", x, 6, "Brown"))
personStorage = copy.copy(personArray[2])
personStorage.name = "Ana"
print("\rpersonArray[2].name is %s\n"
"\rpersonStorage.name is %s"
% (personArray[2].name, personStorage.name))

I have created setters and getters to get the data.Also you can create a copy of the instance that holds the data.
from copy import deepcopy
class PersonInfo:
def __init__(self,name,age,height,hairColour):
self.name = name
self.age = age
self.height = height
self.hairColour = hairColour
x = PersonInfo("Mario",34,1.70,"blue")
print(x.height) # prints 1.70
x1 = deepcopy(x)
print(x1.age)

Related

How to split received data into classes based on content

I am trying to write a chunk of code that will organize different types of data into classes. I can split them as of now, but I'm not sure how to get Python to look at the string and automatically sort them into either class based on the content of the string. For example, I have the following and would like to pass the string to either class depending on which type of data is being given to me:
#The data comes in by two different types continuously and is displayed as such below:
animal=dog, age=13, colour=brown, name=Jeff
animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy
class Dogclass():
def __init__(self,age,colour,name):
self.age = age
self.colour = colour
self.name = name
class Catclass():
def __init__(self,age,colour,declawed,friendly,name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def splitter():
m = re.split('[, =]', data),
if "dog" in m:
I would like my splitter function to not only have the ability to split the strings, but also go on to sort the split data into classes. This is what I had before (did not work) but would like to figure out a way to utilize OOP more and understand the use of classes.
dog = []
cat = []
def splitter(data):
m = re.split('[, =]', data)
if 'dog' in m:
dog['age'] = (m[7])
dog['colour'] = (m[11])
dog['name'] = (m[13])
elif 'cat' in m:
cat['age'] = (m[7])
cat['colour'] = (m[9])
cat['declawed'] = (m[11])
cat['friendly'] = (m[13])
cat['name'] = (m[15])
else:
return()
I have also tried to create dictionaries to store the data I want to call to, but everything I have tried does not successfully take the splitted data and assign it to a value within my dictionary. Any help would be appreciated.
Lets say you got a string that represent data like this :
"animal=dog, age=13, colour=brown, name=Jeff"
The fisrt thing you would have to do is to parse it to a dictionary like object with a simple function like this one :
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
Then you would get an object and you could get its corresponding class by any of the corresponding attribute (lets say your class is based on the "animal" attribute, you could define a simple function to to that :
def getConstructor(classname):
return globals()[classname]
All in one :
import json
class dog():
def __init__(self, age, colour, name):
self.age = age
self.colour = colour
self.name = name
class cat():
def __init__(self, age, colour, declawed, friendly, name):
self.age = age
self.colour = colour
self.declawed = declawed
self.friendly = friendly
self.name = name
def parser(stringToParse):
remove_space = stringToParse.replace(" ", "")
addQuotes = {i.split('=')[0]: i.split('=')[1]
for i in remove_space.split(',')}
return addQuotes
def getConstructor(classname):
return globals()[classname]
def buildIt(any_animal):
my_animal = parser(any_animal)
my_animal_constructor = getConstructor(my_animal["animal"])
if my_animal_constructor.__name__ == "dog":
return dog(my_animal["age"], my_animal["colour"], my_animal["name"])
my_new_animal = buildIt("animal=dog, age=13, colour=brown, name=Jeff")
print(my_new_animal.colour)
In this example i build a dog from the input. If you try to print its coulour you get : "brown"
Of course you will have to implement the if statement for the other class in order to get the cat (and other) class work too...
EDIT
Also, if you want to improve your code you should implement it as an Object oriented one as suggested in Yaron Grushka's answer (create an Animal parent class and makes cat and dog inherit from it)
First of all, I would suggest that in general for cases like these that you use inheritance. You can have a parent class called Animal which has all the common attributes such as age, name and color. Then you can create the Cat and Dog classes that inherit from the parent class, each having unique attributes (such as declawed for cats). Like so:
class Animal():
def __init__(self, name, age, colour):
self.name = name
self.age = age
self.colour = colour
class Cat(Animal):
def __init__(self, name, age, colour, declawed, friendly):
super().__init__(name, age, colour)
self.declawed = declawed # Can make this a boolean with an if/else
self.friendly = friendly # Same
For splitting, you can actually use the split function that Python offers, and use commas as the separator. Then make it into a dictionary. e.g:
def create_animal(data):
details = data.split(",");
attributes = {}
for detail in details:
pair = detail.split("=")
attributes[pair[0]] = pair[1]
print(attributes)
if attributes["animal"] == "cat":
animal = Cat(attributes[" name"], attributes[" age"], attributes[" colour"], attributes[" declawed"], attributes[" friendly"])
else: # Dog creation, same idea...
return animal
a = create_animal("animal=cat, age=9, colour=white, declawed=yes, friendly=yes, name=Jimmy")
print(a.name)
# =>"Jimmy"

Why python first prints out the class method?

I have written a sample code like below. I wonder why the method prints out first even it is on lower lines...
class Dog():
species = "Mammals"
legs = 4
def __init__ (self,breed, name, spots, height):
self.breed = breed
self.name = name
self.spots = spots
self.height = height
def bark(self,number):
for i in range(number):
print("My name is {}".format(self.name))
MyDog = Dog(breed = "Huskie", name = "MyDog", spots = True, height = "Tall")
print(MyDog.species,MyDog.legs,MyDog.breed,MyDog.name,MyDog.spots,MyDog.bark(3),MyDog.height,sep="\n")
print is just a function like any other, and Python evaluates all arguments to a function before calling that function. Otherwise the function wouldn't know the values of its arguments and wouldn't know what to do. So MyDog.bark(3) must be evaluated before it's possible to call print(..., MyDog.bark(3), ...).

How can i add my objects into a list to choose them randomly to blit onto the screen?

When i try to put my objects into a list, i can not get an output with object names, it gives a weird output like "_ main _.object at 0x029E7210". I want to select my objects randomly to blit ONE of them onto the screen. But i could not figure this out.
car_main = pygame.image.load("car_main.png")
car_red_ = pygame.image.load("car_red.png")
car_blue = pygame.image.load("car_blue.png")
class cars:
def __init__(self,x,y,car_type,w=50,h=100,s=5):
self.x = x
self.y = y
self.w = w
self.h = h
self.s = s
self.car_type = car_type
def draw(self):
dp.blit(self.car_type,(self.x,self.y))
car1 = cars(x,y,car_main)
car2 = cars(x,y,car_red)
car3 = cars(x,y,car_blue)
car_list = [car1,car2,car3]
rc = random.choice(car_list)
print(rc)
# output> __main__.object at 0x02A97230
When I change
car_list = [car1,car2,car3] with;
car_list = [car1.car_type,car2.car_type,car3.car_type]
# output > Surface(50x100x32 SW)
But I want to see an output as my object names. Not as a string type ("car_main"). I want to get an output as the object name (car_main) directly. Because in the main loop, i will choose one of them to blit onto the screen everytime when the loop renews itself.
You need to define __str__ for your class Car to let it properly handle object to string:
class Car:
def __str__(self):
for k, var in globals().items():
if var == self:
return k
# default
return "Car"
Note1: Usually use uppercased Car for a class and car for an instance.
Note2: Look up variable strings in globals is not reliable. You may not want to make all variables global, and manually search them in scope is tedious. Actually why don't you give your Car a name attribute? Then you nicely have:
class Car:
def __init__(self, name):
self.name=name
def __str__(self):
return self.name
car = Car(name='first car')
print(car) # 'first car'
More read about "magic methods": https://rszalski.github.io/magicmethods/#representations
Add a __str()__ magic method to your car class like so:
def __str__(self):
return f'car with x of {self.x}, y of {self.y}, and type of {self.car_type}'

use/execute a string within a list as an object name (python)

It looks like this:
I define a class:
class Boy():
def __init__(self):
self.age = input()
self.height = input()
Then I define a list with the names of the boys that I want to be object instances of the above 'class Boy':
boys = [input(), input()]
(for example: john & frank, so that boys = ['john', 'frank'])
I want now to iterate over my list 'boys' and use each name to make an object of the 'class Boy':
for value in boys:
value = Boy()
Of course, it does not work :-) but is there a way to achieve it ??
I have been using Python since 1 week, if the question sounds silly :-)
If someone could help me, will be very thankful
Thank you all for the help, I implemented the proposed solutions:
thank_you_people = ['Makoto','L3viathan','Rcynic','Pythonic','Paul Rooney', 'st.eve']
:-)
for person in thank_you_people:
print('Thank you, %s' % person)
I would highly recommend changing your class up a bit, to remove input() calls from the constructor. You could use an __init__ method that has optional arguments for age and height, and a forced one for name:
class Boy():
def __init__(self, name, age=None, height=None):
self.age = age
self.height = height
self.name = name
You then can instantiate with a name, and assign the attributes later:
boys = [Boy(input("New boy: ")), Boy(input("New boy: "))] # e.g. "John", "Frank"
for boy in boys:
boy.age = input("Age of",boy.name + "?")
boy.height = input("Height of",boy.name + "?")
edit: To have the boys in a dictionary for easier access:
boys = {}
for _ in range(2):
name = input("New boy:")
boys[name] = Boy(name)
for boy in boys:
boys[boy].age = input("Age of",boys[boy].name + "?")
boys[boy].height = input("Height of",boys[boy].name + "?")
Don't use the input function in your __init__ method, as it restricts how your Boy class can be used. Instead define a separate function that creates a Boy and pass those parameters to your Boy constructor. Then you could store your Boys in a dict, keyed by the boys name.
e.g.
class Boy():
def __init__(self, age, height):
self.age = age
self.height = height
def __str__(self):
return 'age=%d height=%d' % (self.age, self.height)
def create_boy(name):
age = int(input('enter age for %s: ' % name))
height = int(input('enter height for %s: ' % name))
return Boy(age, height)
boynames = []
while(True):
name = input('enter boy name: ')
if name == '':
break
boynames.append(name)
boys = {}
for boyname in boynames:
boys[boyname] = create_boy(boyname)
for name, boyinfo in boys.items():
print('name=%s %s' % (name, boyinfo))
Later on you could query a particular boys name like so
if 'paul' in boys:
print(boys['paul'].age)
print(boys['paul'].height)
else:
print('no boy called paul')
This isn't designed to work if you can have Boys with the same name. In that case you could have each dictionary entry hold a list of Boys and find some other parameter to distinguish between the Boys of the same name.
To not use just anything as an object name, you can use the input as keys for a dict. Something like this:
class Boy():
def __init__(self):
self.age = input()
self.height = input()
boys = [input(), input()] # this is probably a bad idea
b = {}
for boy in boys:
b[boy] = Boy()
I'd suggest to i) use a dictionary for creating variables with the name contained in a string and ii) pass age and height as args, **kwargs or *args in the class, as already suggested by Makoto. Something along these lines:
class Boy():
def __init__(self, name, age=0, height=0):
self.age = age
self.height = height
boys = ['finn', 'jake']
ages = [16, 33]
height = [165, 120]
boys_objects = {}
for i, b in enumerate(boys):
boys_objects[b] = Boy(age=ages[i], height=heights[i])
Use the zip function. You'll have to change your class constructor a little bit though.
class Boy(object):
def __init__(self, (name, age, height)):
self.name = name
self.age = age
self.height = height
Then get the input values into lists
names = ['a','b','c']
ages = [10, 20, 30]
heights = [155, 160, 165]
boys = zip(names, ages, heights)
for guy in boys:
b = Boy(guy)
print b.name, b.age, b.height
I don't know wha you want to do with the objects - but you can change that in the for loop.
EDIT: In response to the error OP is getting in the comments:
I cannot reproduce the init error. I tried with python and iPython both.
Alternatively you could try
def __init__(self, guy_tuple):
self.name = guy_tuple[0]
self.age = guy_tuple[1]
self.height = guy_tuple[2]
If that doesn't work either, you can change the constructor to take in name, age and height individually. Like is working for you.
def __init__(self, name, age, height):
self.name = name
...
then the for loop must change accordingly. Since guy is a tuple with all three elements, you'll have to get each one by index within the tuple.
for guy in boys:
b = Boy(guy[0], guy[1], guy[2])
Yes object b will be overwritten, you'll have to change it to do what you need it to do once you get the object. So after b is instantiated, pass it to another function to do what you want with it, or append it to a list. Once b is an object, you continue with the logic you want.

python getting a specific attribute from a list of objects

I have a python array of objects
class ball(self, size, color, name):
self.size = size
self.color = color
self.name = name
then a user will inputs a name and an attribute via the command line. For example a user could input "name1" and then "color" or "weirdName" then "size"... I then want to find the object based on the name and print get either the color object or the size object. Can I do it like this or will I need to use a switch case?
Thanks
If you know there is exactly one match, you can do this:
the_ball = next(b for b in list_of_balls if b.name == ???)
If there are multiple then you can get a list:
the_balls = [b for b in list_of_balls if b.name == ???]
If you are primarily looking up balls by their name, you should keep them in a dictionary instead of a list
To retrieve an attribute by name use getattr
getattr(the_ball, "size")
Doing this can be a bad idea
getattr(the_ball, user_input)
what if user_input is "__class__" or something else you didn't expect?
If you only have a few possibilities it's better to be explicit
if user_input == "size":
val = the_ball.size
elif user_input in ("colour", "color"):
val = the_ball.color
else:
#error
I think you're trying to do two different things here.
First, you want to get a particular ball by name. For that, gnibbler already gave you the answer.
Then, you want to get one of the ball's attributes by name. For that, use getattr:
the_ball = next(b for b in list_of_balls if b.name == sys.argv[1])
the_value = getattr(the_ball, sys.argv[2])
print('ball {}.{} == {}'.format(sys.argv[1], sys.argv[2], the_value)
Also, your class definition is wrong:
class ball(self, size, color, name):
self.size = size
self.color = color
self.name = name
You probably meant for this to be the __init__ method inside the ball class, not the class definition itself:
class ball(object):
def __init__(self, size, color, name):
self.size = size
self.color = color
self.name = name
However, you may want to reconsider your design. If you're accessing attributes dynamically by name more often than you're accessing them directly, it's usually better just to store a dict. For example:
class Ball(object):
def __init__(self, size, color, name):
self.name = name
self.ball_props = {'size': size, 'color': color}
list_of_balls = [Ball(10, 'red', 'Fred'), Ball(20, 'blue', 'Frank')]
the_ball = next(b for b in list_of_balls if b.name == sys.argv[1])
the_value = the_ball.ball_props[sys.argv[2]]
Or you may even want to inherit from dict or collections.MutableMapping or whatever, so you can just do:
the_value = the_ball[sys.argv[2]]
Also, you may want to consider using a dict of balls keyed by name, instead of a list:
dict_of_balls = {'Fred': Ball(10, 'red', 'Fred'), …}
# ...
the_ball = dict_of_balls[sys.argv[1]]
If you've already built the list, you can build the dict from it pretty easily:
dict_of_balls = {ball.name: ball for ball in list_of_balls}
If I understood properly, you need to get a particular ball from a list of balls, based on the value of an attribute. A solution would be:
attribute_value = sys.argv[1]
attribute_name = sys.argv[2]
matching_balls = [ball_item for ball_item in list_balls if \
getattr(ball_item, attribute_name) == attribute_value]

Categories