Unintuitive output given by classes in Python - python

So I've got this class:
class Student(object):
def __init__(self, studentID, name):
self.__studentID = studentID
self.__name = name
def set_studentID(self, value):
self.__studentID = value
def get_name(self):
return self.__name
and running this code:
x = Student
x.set_name(x, input("Name: "))
x.set_studentID(x, len(students))
students.append(copy.deepcopy(x))
x.set_name(x, input("Name: "))
x.set_studentID(x, len(students))
students.append(copy.deepcopy(x))
for i in (students):
print(i.get_name(i))
gives an unexpected output:
For the input:
a
b
the output is:
b
b
The expected output is:
a
b
If you answer please give me a short explanation of why it doesn't work

The reason your code isn't working is because you never instantiate your class, instead, you assign the class object itself to the name x
x = Student
When you really needed
x = Student()
Then you call the methods on the class object, whilst passing the class object itself as the first parameter, thus your getters and setters act on the class object.
Finally, classes are meant to be singletons, and the copy module special cases them. So if x is a class
copy.deepcopy(x) is x
Is always True, thus you never actually make a copy.
As a side note, your class definition looks like it was written by a Java developer using Python for the first time. The Pythonic way to do it is not to use getters and setters use properties, and only when you need to. Also, don't use double-underscores name-mangling unless you actually want that, which in this case, you dont.

The other answer explains why your code doesn't work as you expect it to. Here's how you could rewrite your code in a more pythonic way.
class Student(object):
def __init__(self, studentID, name):
self.studentID = studentID
self.name = name
students = []
name = input("Name: ")
students.append(Student(len(students), name))
name = input("Name: ")
students.append(Student(len(students), name))
for student in students:
print(student.name)
You don't need to write getter and setter methods unless you have to do some special processing.

Related

python: modify array from parent method in child method

I have a parent class as follows:
class student:
def __init__(self, name, age, field):
self.name = name
self.age = age
self.field = field
def abilities(self):
abs = ["study", "drink", "party"]
action = np.random.choice(abs, replace = True)
return(action)
Now upon graduation this student becomes a grown-up and his abilities change:
class adult(student):
def abilities(self):
super().abilities()
abs.append("work")
abs.remove("party")
if action == work:
print("paying off that student loan")
This does not work. The error I get is: name 'abs' is not defined.
Now I tried to access the abilities method in the parent class by using super().abilities.abs but then I am referring to the object the function returns and with self.abilities.abs, but then I refer to the class.
Any suggestions as to how to access the array within the parent method from the child are much appreciated.
There are a few flaws in the code you wrote:
You are overwritting a built in function abs in your code. This should not be done.
You are calling super().abilities(), which returns a value without storing and using it.
When calling abs.append("work") you are trying to assign a value to a build in function and not the local variable abs from the parent. Extracting this to a object variable solves the issue. See, self.abs in the constructor
The adult abilities method is not returning anything.
You are not calling the super constructor in adult, which results in adult not storing any of the values passed in the constructor.
A fixed version could look as follows:
import numpy as np
class student:
def __init__(self, name, age, field):
self.name = name
self.age = age
self.field = field
self.abs = ["study", "drink", "party"]
def abilities(self):
action = np.random.choice(self.abs, replace = True)
return action
class adult(student):
def __init__(self, name, age, field):
super(adult, self).__init__(name, age, field)
self.abs.append("work")
self.abs.remove("party")
def abilities(self):
action = super().abilities()
if action == "work":
print("paying off that student loan")
return action
In addition to solving this issue, you should review the object inheritance structure as mentioned in the comment by #chepner.

Python3 Class method inputs; clean solution

I'am using more class based programs, however in some cases it's not handy to provide all self.paramets into a class.
In those cases I want to use a regular input into a function in a class. I figured out a way to achieve both inputs, let me show this in following script:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(a):
if (type(a) == str):
name = a
else:
name = a.name
print("Hello my name is " + name)
p1 = Person("John", 36)
p1.myfunc()
print("---------------------")
Person.myfunc("Harry")
Output:
Hello my name is John
---------------------
Hello my name is Harry
First, the name is initialized by the classes self.params.
Second, the name is provided in the method within the class as a string.
So a type check is necessary.
However I don't think this is a clean approach, because when I have >30 methods I need to implement these type checks again, including upcoming type-error results.
Does anyone know a better approach?
The simplest solution is to implement a __str__ method for your class. This method will be called whenever something tries to convert an instance of the class to a string.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('Jane', 25)
print('Hello', p)
'Hello Jane'

Accessing a class instance's attributes using user input

So I have this code:
class vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
class car(vehicle):
def __init__(self):
vehicle.__init__(self)
self.type = 'car'
car1 = car()
print car1.make, car1.colour, car1.cost, car1.type, car1.age
n = raw_input()
dic = {'name' : n}
dic['name'] = car()
print dic['name'].make
In the last bit, I was able to resolve a previous issue I had: Creating an instance of the car class with its name being one that the user inputs in n
Now, say I wanna ask the user to input a name and now I have to find the class' instance that has that name.
For example if at one point an instance with the name car2 was created. Now user wants to get info about car2 and inputs 'car2'. how can I use this input to access attributes of the instance called car2?
I tried:
a = raw_input()
dic['fetch'] = a
dic['fetch'].make
does not work.
It seems to me you have a fair bit of misunderstanding. The way you're assigning the input into the dictionary doesn't make sense. Your description indicates that you want a dictionary that maps a "name" to a car description.
Your initial creation of the dictionary is off. The way you're currently doing it, you're actually losing the name the user inputs when you assign the car data. Some better variable naming might help you. Create your dictionary like this:
cars_by_name = dict()
name = raw_input()
cars_by_name[name] = car()
So now you have a name (given by the user) that maps to a car description.
Now you need to fetch that same car instance again. You do it by using the name as the key in the dictionary:
name2 = raw_input()
print cars_by_name[name2].make
Next, let's look at your classes. My first question: why do you need a vehicle and a car class? If you're never going to have classes other than car inheriting from vehicle, you don't really need them both. Even if you do plan the have more subclasses, I would probably still recommend against inheritance here. Your vehicle has no behavior (methods) for subclasses to inherit. It's just a data object. With duck typing so strongly encouraged in Python, inheriting from a class with no methods doesn't buy you anything. (What a base class would buy you with methods is that you'd only have to define the method in one place, making it easier to modify later on.) Particularly in your case, there doesn't seem to be any motivation to create a subclass at all. A single class for all vehicles will work just fine. So let's simplify your classes:
class Vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
self.type = 'car'
(Also, note that class names are usually given in camel case in Python.) Now there's one more problem here: those constants. Not all Vehicles are going to have those same values; in fact, most won't. So lets make them arguments to the initializer:
class Vehicle(object):
def __init__(self, age, make, colour, cost, type):
self.age = age
self.make = make
self.colour = colour
self.cost = cost
self.type = type
Then you create one like this:
v = Vehicle(6, 8, 'colour', 'cost', 'car')
Good luck in your endeavors learning. Hope this helps.
If I understand you correctly and you want to map string names to instances:
n = raw_input()
dic = {n: car()}
print dic[n].make
print(dic)
dic[n].cost = 10000
print(dic[n].cost)
Another option would be to take a name for each car instance and have a class attribute dict mapping names to self.
In [13]: paste
class vehicle(object):
def __init__(self):
self.age = 6
self.make = 8
self.colour = 'colour'
self.cost = 'cost'
class car(vehicle):
dic = {}
def __init__(self, name):
vehicle.__init__(self)
car.dic = {name: self}
self.type = 'car'
self.name=name
car1 = car("car1")
car2 = car("car2")
car2.colour="blue"
print car1.make, car1.colour, car1.cost, car1.type, car1.age
n = raw_input()
print car.dic[n]
print car.dic[n].make
print car.dic[n].colour
## -- End pasted text --
8 colour cost car 6
car2
<__main__.car object at 0x7f823cd34490>
8
blue

TypeError in Python 3.x

I have no idea what is wrong! This is a very simple program and I have done a lot head banging! Please someone enlighten me!
This a lab problem from the CSE 111 - Programming Language II course. They teach Java at the university and the code I wrote in Java works fine.
I just have to create a Student class with some fields to hold the basic information about a student with methods to get and set the attributes. Then create an instance of that class and tryout the methods.
But every time I run this program the following error occurs:
TypeError: set_name() takes exactly 1 positional argument (2 given)
Here is the code I wrote.
class Student:
'''Student class'''
name = None
id = 0
address = None
cgpa = None
def get_name():
return name
def set_name(n):
name = n
def get_id():
return id
def set_id(i):
id = i
def get_address():
return address
def set_address(a):
address = a
def get_cgpa():
return cgpa
def set_cgpa(c):
cgpa = c
#An object of Student class
jack = Student()
jack.set_name('jacky')
print(jack.get_name())
You're not accepting a reference to your instance as the first argument to that method, i.e. your set_name() should be written:
def set_name(self, n):
self.name = n
This is somewhat different from other languages where there is a built-in keyword (such as this) that refers to the current object. Python passes that reference explicitly, as an argument to the method.
All your other methods must be modified similarly.
Note that just setting name = n sets a local variable name which goes away when the method ends; it does not set anything on the instance. You have to explicitly set self.name if you want an instance attribute.
Also, and this is a matter of style, but you do not usually write set and get methods in Python. It is normal practice to set and get attributes directly. If you want to do validation of values, use a property instead. So basically, none of your methods are actually necessary in good style.
However, you don't have an __init__() method. Usually you would pass the desired attributes of the instance when instantiating the class and save these on the instance.
class Student:
def __init__(self, name, id, address, cgpa):
self.name = name
self.id = id
self.address = address
self.cgpa = cgpa
herman = Student("Herman Munster", 12345, "1313 Mockingbird Lane", 4.0)
Try this:
import sys
class Student:
'''Student class'''
self.name = None
self.id = 0
self.address = None
self.cgpa = None
def get_name(self):
return self.name
def set_name(self, n):
self.name = n
def get_id(self):
return self.id
def set_id(self, i):
self.id = i
def get_address(self):
return self.address
def set_address(self, a):
self.address = a
def get_cgpa(self):
return self.cgpa
def set_cgpa(self, c):
self.cgpa = c
You need to pass self as the first argument to each member function of the class. Member variables must then be referred to with self, i.e. self.name. Furthermore, you may wish to include an __init__() function; this serves usually to initialize any member variables, and is called at the instantiation of the class.
Take a look at the Python documentation here for some examples on well-formed classes: http://docs.python.org/tutorial/classes.html#random-remarks
In Python, you need to pass in self for each of your member functions. You also need to reference class variables as self.x, if you want them to take an effect.
Here are a couple examples that you need to apply to the rest of your code.
def set_name(self, n):
self.name = n
def get_cgpa(self):
return self.cgpa
There is some explanation for why this is the case in the documentation.
This is because first argument of methods is self - the class instance.
See What is the purpose of self?
and http://docs.python.org/tutorial/classes.html#class-objects

Access class instance "name" dynamically in Python

In plain english: I am creating class instances dynamically in a for loop, the class then defines a few attributes for the instance. I need to later be able to look up those values in another for loop.
Sample code:
class A:
def __init__(self, name, attr):
self.name=name
self.attr=attr
names=("a1", "a2", "a3")
x=10
for name in names:
name=A(name, x)
x += 1
...
...
...
for name in names:
print name.attr
How can I create an identifier for these instances so they can be accessed later on by "name"?
I've figured a way to get this by associating "name" with the memory location:
class A:
instances=[]
names=[]
def __init__(self, name, attr):
self.name=name
self.attr=attr
A.instances.append(self)
A.names.append(name)
names=("a1", "a2", "a3")
x=10
for name in names:
name=A(name, x)
x += 1
...
...
...
for name in names:
index=A.names.index(name)
print "name: " + name
print "att: " + str(A.instances[index].att)
This has had me scouring the web for 2 days now, and I have not been able to find an answer. Maybe I don't know how to ask the question properly, or maybe it can't be done (as many other posts seemed to be suggesting).
Now this 2nd example works, and for now I will use it. I'm just thinking there has to be an easier way than creating your own makeshift dictionary of index numbers and I'm hoping I didn't waste 2 days looking for an answer that doesn't exist. Anyone have anything?
Thanks in advance,
Andy
Update: A coworker just showed me what he thinks is the simplest way and that is to make an actual dictionary of class instances using the instance "name" as the key.
Sometimes keeping it simple is best. Having a dict that stores your instances with their names as the keys would be both straightforward and fairly simple to implement.
class A:
instances={}
def __init__(self, name, attr):
self.name=name
self.attr=attr
A.instances[name] = self
and then to get the proper instance, just...
instance = A.instances[name]
No need to put the instance dict inside the class. Just create a dict, inst in the local scope:
class A:
def __init__(self, name, attr):
self.name=name
self.attr=attr
inst={}
names=("a1", "a2", "a3")
x=10
for name in names:
inst[name]=A(name, x)
x += 1
Then, whenever you want to access a certain instance by name, just use inst[name]:
for name in names:
print inst[name].attr
Yes, the dictionary approach should work well, and can be dovetailed into the class itself.
class A:
_instances = {}
#classmethod
def get(cls, name):
return A._instances[name]
def __init__(self, name, attr):
self.name=name
self.attr=attr
A._instances[name] = self
a = A('foo', 10)
aa = A.get('foo')
If you want to play around with __new__, you can even make this transparent:
a = A('foo', 10)
aa = A('foo') # 'a' and 'aa' refer to the same instance.
This is a bit complicated, so I'll leave it to you to research (and of course ask another question on SO if you get stuck).

Categories

Resources