I have this class Person created:
class Person(object):
def __init__(self, name, age, gender):
self._name = name
self._age = age
self._gender = gender
self._friend = None
def __eq__(self, person):
return str(self) == str(person)
def __str__(self):
if self._gender == 'M':
title = 'Mr'
elif self._gender == 'F':
title = 'Miss'
else:
title = 'Ms'
return title + ' ' + self._name + ' ' + str(self._age)
def __repr__(self):
return 'Person: ' + str(self)
def get_name(self):
return self._name
def get_age(self):
return self._age
def get_gender(self):
return self._gender
def set_friend(self, friend):
self._friend = friend
def get_friend(self):
return self._friend
I now need to have 3 functions:
Using the Person class, write a function print_friend_info(person) which accepts a single argument, of type Person, and:
prints out their name
prints out their age
if the person has any friends, prints 'Friends with {name}'
A function create_fry() which returns a Person instance representing Fry. Fry is 25 and his full name is 'Philip J. Fry'
A function make_friends(person_one, person_two) which sets each argument as the friend of the other.
And this is what I've got:
def print_friend_info(person):
person= Person
person_name=person.get_name(person)
person_age=person.get_age(person)
person_gender=person.get_gender(person)
person_friends=person.get_friend(person)
return person_name, person_age, person_gender, person_friends
"""print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')"""
def create_fry():
fry=Person("Philip J. Fry", 23, "M")
return fry
def make_friends(person_one,person_two):
return person_one.set_friend(person_two)
And the error handler says "type object 'Person' has no attribute '_name'"
Don't understand what you try to put into print_friend_info, if the parameter "person" is an instance already
I think your problem is this line
person = Person
by doing that, you just override person which is an instance and replace it with just a definition of class Person
Try to remote that line, and see what happend
You want to remove this line
person= Person
from the print_friend_info(person): function.
That line doesn't create a new Person instance, and even if it did you wouldn't need to. You already get the person passed in as parameter, all you need to do there is retrieve his information.
Since classes are objects in python, by doing person= Person you are just saying that person is the same as Person. Then when you try to get_name on person, that doesn't work because person is not an instance of a class, it is a class object. Therefore it has no _name attribute. It will only have a _name attribute after you instantiate it with the proper parameters.
it still dosn't print the info needed
return person_name, person_age, person_gender, person_friends
"""print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')"""
This code doesn't seem right. Instead of printing the data, it returns the data. The text between """ does nothing because you have already left the function with return
Should probably be replaced with
print (person_name)
print (person_age)
print ('Friends with {'+person_friends+'}')
return # this is not necessary, but it's better to be explicit.
Once you have a Person object, say with a label person, you can access its functions like so:
>>> person = Person('Pete', 43, 'Male')
>>> person.get_age()
43
You don't need to pass person to the member function get_age:
>>> person.get_age(person)
Traceback (most recent call last):
...
TypeError: get_age() takes exactly 1 argument (2 given)
But the definition of get_age does have a parameter, self:
def get_age(self):
return self._age
This is implicitly passed to the function when you call it on an object. So person is being passed as self. You can see in the error message above that we were passing two parameters, the implicit self person, and the (wrong, unneeded) explicit person.
self._age is the same as person._age, but is encapsulated, and hidden behind your 'getter' interface.
So, to fix your code, get rid of:
person = Person
and change calls like person.get_age(person), to person.get_age().
Related
I am writing a code to document an employee system, the class can print full name, email:
class Employee:
def __init__(self,first,last):
self.first=first
self.last=last
def fullname(self):
print('{} {}'.format(self.first,self.last))
def email(self):
print('{}.{}#email.com'.format(self.first,self.last))
emp_1=Employee('John','Smith')
emp_1.first='Jim'
print(emp_1.first)
print(emp_1.email())
print(emp_1.fullname())
Output is like this:
I don't understand why when I call methods (email(), fullname()), I have a None within the output?
The output is:
Jim
Jim.Smith#email.com
None
Jim
Smith
None
You are using the method call inside a print function. So it will try to print the value which is returned from the method. Since you are not returning anything from the method, it prints None.
You can always return the value instead of printing them inside. Example.
class Employee:
def __init__(self, first, last):
self.first = first
self.last = last
def fullname(self):
# just printing the name will not return the value.
return '{} {}'.format(self.first, self.last)
def email(self):
# same, use a return statement to return the value.
return '{}.{}#email.com'.format(self.first, self.last)
emp_1 = Employee('John', 'Smith')
emp_1.first = 'Jim'
print(emp_1.first)
print(emp_1.email()) # print what is returned by the method.
print(emp_1.fullname())
It will give a proper output like
Jim
Jim.Smith#email.com
Jim Smith
I am writing a program that will render the name and gender of people entered. The program should ask for a name and then for the gender until the user only presses Enter as the name. Only after Enter has been pressed, the text should be output for all persons. I have tried to implement some code before, but unfortunately I am not getting anywhere.
class Person():
def __init__(self):
self.name = input('Name: ')
self.gender= input('Gender: ')
def give_name(self):
return self.name
def give_gender(self):
return self.gender
def show_name(self):
return self.give_name() + ' is ' + self.give_gender()
my_person = Person()
print(my_person.show_name())
At the end should output the following:
Name: Maya Mitermacht
Gender: female
Name: Max Mustermann
Gender: male
Name:
Maya Mitermacht is female
Max Mustermann is male
Inspired by #Pac0, I'm submitting another answer, though his is already enough and works well!
The one thing that kept tickling me at the back of my head was only a matter of concept, not efficiency or effectiveness. The idea is: if we want to stop creating Persons, we should do so before creating the next one, not after its creation.
For that, I present an alternative using __new__. Perhaps not the best use of it, but it works as intended and has that 'feature' to stop the creation of objects between instances, not after the last one.
class Person:
name: str
gender: str
def __new__(cls):
cls.name = input('Name: ')
if cls.name:
cls.gender = input('Gender: ')
return super(Person, cls).__new__(cls)
else:
raise ValueError("A name is required to create a Person instance")
def __init__(self):
self.name = Person.name
self.gender = Person.gender
def give_name(self):
return self.name
def give_gender(self):
return self.gender
def show_name(self):
return self.give_name() + ' is ' + self.give_gender()
def main():
people = []
while True:
try:
new_person = Person()
except ValueError:
break
else:
people.append(new_person)
for person in people:
print(person.show_name())
if __name__ == "__main__":
main()
The idea is to use __new__ to capture input, then check if a name was provided. If it was, move on to get gender and create the new Person instance. Else, raise an exception, as a person requires a name.
The usage is simple, we can create as many people as we want (and here I'm storing them all in people list. When we are done, provide no name to the next Person, which will raise ValuError, that we catch and break from the loop. Then, print all names.
Since I'm already here, I'll also make some general suggestions to the code. First, getters and setters are not common in Python, and usually are discouraged. Therefore, a person.give_name() could simply be swapped by person.name, and even more when that's only used inside the class. Using that idea, we could simplify the methods give_name, give_gender and show_name to simply:
class Person:
name: str
gender: str
.
.
.
def show_name(self):
return self.name + ' is ' + self.gender
Next, I believe we can make good use of one more "magical" method from Python, and that is __str__. Since we are using name and gender to identify a Person instance, and we want to show that info in a particular way, we can simply define how a Person is represented as a string. We do that using __str__ (inside the class), such as:
def __str__(self):
return f"{self.name} is {self.gender}"
Note: f-strings are very neat and easy to use, but if you're not a fan, replace it with self.name + ' is ' + self.gender, originally.
That way, when we call str() on a Person instance, it will return that same string. If we directly try to print() a Person, we'll have the same result. So the loop to print the names doesn't need print(person.show_name()) anymore, it can be simply print(person) -- which seems more readable to me, and a bit shorter!
So, to wrap it all up, everything would look like this:
class Person:
name: str
gender: str
def __new__(cls):
cls.name = input('Name: ')
if cls.name:
cls.gender = input('Gender: ')
return super(Person, cls).__new__(cls)
else:
raise ValueError("A name is required to create a Person instance")
def __init__(self):
self.name = Person.name
self.gender = Person.gender
def __str__(self):
return f"{self.name} is {self.gender}"
def main():
people = []
while True:
try:
new_person = Person()
except ValueError:
break
else:
people.append(new_person)
for person in people:
print(person)
if __name__ == "__main__":
main()
Since the input will decide to create object or not, it can't be the role of the object/class itself to automatically initialize it. You must have a way to know if the input was empty or not.
Hence, you should delegate this to another class method, call it from your main program and check return value. For instance, in the code below, I chose to return True or False in my function.
class Person():
def initialize(self):
name_entered = input('Name: ')
if name_entered == '':
return False
gender_entered = input('Gender: ')
if gender_entered == '':
return False
self.name = name_entered
self.gender = gender_entered
return True
def give_name(self):
return self.name
def give_gender(self):
return self.gender
def show_name(self):
return self.give_name() + ' is ' + self.give_gender()
persons = []
while True:
my_person = Person()
isCreated = my_person.initialize()
# if it returned False, then user has entered nothing for gender or name, stop the loop
if (not isCreated):
break
#other wise, person is initialized, let's add it to the list and continue
persons.append(my_person)
for person in persons:
print(person.show_name())
I've tried to make an OOP based program in python. I gave it an object to work with and tried to make it print the name, but its not working.
class human:
def __init__(self, name):
print("this is a human")
def name(self, name):
print("this is {}".format(bob.name))
bob = human("bob")
Anyone know what the problem could be?
Beyond the answers you already received (which solve your problem), I'd suggest not having a method that prints the name. Rather, you should have a __str___ dunder method that defines the object's behavior when an instance is printed.
class human:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
person = human("bob")
print(person)
'bob'
You can also define the object's behavior when the instance name is entered in the console, for instance just running the line
>>> person
You can do it with __repr__:
def __repr__(self):
return f'when entering the instance name in the console: {self.name}'
This will print:
when entering the instance name in the console: bob
This appears more pythonic to me than having a method that simply prints the name.
You're never storing the name on the instance, where would it get the name from? Your __init__ needs to do something along the lines of self.name = name
the name method and attribute are going to conflict, the latter will shadow (hide) the former, and it should look up whatever attribute its using on self
You never assigned the passed name to the object. Try:
class human:
def __init__(self, name):
print("this is a human")
self.name = name
def print_name(self):
print("this is {}".format(self.name))
bob = human("bob")
bob.print_name()
there are couple of things to update in the code:
bob is an instance which is not defined at human class
notice that init, name functions expect external param but you never use it in the function. (in self. = name)
in order to use it:
define a var in the class named 'name' and update you function to:
class human:
_name = ""
def __init__(self, name):
print("this is a human")
self._name = name
def name(self):
print("this is "+ self._name)
bob = human("bob")
bob.name()
bob = human("bob") only init function and you should call bob.name() in order to call the print-name function
I am learning Python using "Learn Python the Hard Way". Programming is very new to me. I'm at exercise 42. I'm asked to do the following:
Make some new relationships that are lists and dictionaries so you can also have "has-many" relationships.
I want the program to print the names of all of Frank's pets. However, I get an error saying 'AttributeError: 'list' object has no attribute 'callpet'.
I understand the problem is that I'm using a list. When I write frank.pet = satan there is no problem at all. But I want Frank to have more than one pet, and I want the program to print the names of those pets.
This is the full code I am using:
class Person(object):
def __init__(self, name):
self.name = name
self.pet = None
def callme(self):
return self.name
def petsyay(self):
namepet = self.pet.callpet()
return namepet
class Pets(object):
def __init__(self, petname):
self.petname = petname
def callpet(self):
return self.petname
frank = Person("Frank")
poekie = Pets("Poekie")
satan = Pets("Satan")
frank.pet = [poekie, satan]
print frank.petsyay()
What I understand is that I need to split the list or something. So I've tried the following:
def callpet(self):
for eachpet in petname:
return self.petname
Put that just gets the same error. I'm confused, what am I doing wrong here?
Not true after edits (do note that his return is sometimes a petname and sometime a list of petnames, this is not good): [If you call Mr. E's answer for a single Pets only you will most likely recieve an error TypeError: 'Pets' object is not iterable. His answer will only work as long as self.pets is a list and will fail when they're not.]
If you want to support singular and multiple Pets at the same time, either always make sure your self.pet is a list or make sure you check before you process them. Kepp in mind that it's important that you have a constant type output exiting your function because otherwise it makes other people's lives hard.
I recommend that you make self.pets a mandatory list in __init__
class Person(object):
def __init__(self, name):
self.name = name
self.pet = None
def callme(self):
return self.name
def petsyay(self):
if isinstance(self.pet, list):
namepet = []
for pet in self.pet:
namepet.append(pet.callpet())
elif isinstance(self.pet, Pets):
namepet = []
namepet.append(self.pet.callpet())
else:
raise ValueError("Not a correct type. Send Pets or list of Pets")
return namepet
class Pets(object):
def __init__(self, petname):
self.petname = petname
def callpet(self):
return self.petname
frank = Person("Frank")
poekie = Pets("Poekie")
satan = Pets("Satan")
frank.pet = [poekie, satan]
print frank.petsyay()
frank.pet = poekie
frank.petsyay()
print frank.petsyay()
I debugged your program and what was happening was that you did not have a setPets method in your person object so self.pets was never getting set with the list of pets. It was constantly None. I added a setPets method as well as updating the petsYay method to return a list. You could also use yield and return a generator to loop through outside of the class. Heres the updated code:
class Person(object):
def __init__(self, name):
self.name = name
self.pets = None
def setPets(self, p):
self.pets = p
def callme(self):
return self.name
def petsyay(self):
tempL = []
if self.pets is not None:
for pet in self.pets:
tempL.append(pet.callpet())
return tempL
class Pets(object):
def __init__(self, petname):
self.petname = petname
def callpet(self):
return self.petname
frank = Person("Frank")
poekie = Pets("Poekie")
satan = Pets("Satan")
frank.setPets([poekie, satan])
print frank.petsyay()
The problem is that frank.pet is a list of Pets. To do what you want you must redefine petsyay to work with a list of Pets, and return a list of names
def petsyay(self):
return [p.callpet() for p in self.pet]
If you want to work with both lists of pets or single pet, you could handle the exception
def petsyay(self):
try:
return self.pet.callpet()
except AtributeError: #Just handle the AttributeError to prevent hiding another exception
return [p.callpet() for p in self.pet]
The first part of the program defines the class and the second part calls on the class though a different program as it follows but I keep getting a "TypeError: check_mood() missing 1 required positional argument: 'self' " for line 5 in the second part
import random
class Animal:
#initialize attributes
def __init__(self,animal_type, name, mood):
self.__animal_type = animal_type
self.__name = name
self.__mood = mood
# set methods
def set_animal_type(self, animal_type):
self.__animal_type = animal_type
def set_name(self, name):
self.__name = name
def set_mood(self, mood):
self.__mood = mood
#check mood
def check_mood(self):
integer = random.randint(1,3)
if integer == 1:
self.__mood = "happy"
elif integer == 2:
self.__mood = "hungry"
elif integer == 3:
self.__mood = "sleepy"
#return methods
def get_animal_type(self):
return self.__animal_type
def get_name(self):
return self.__name
def get_mood(self):
return self.__mood
And I made a simple test program to run it through:
import Animal
an = input("enter aninal:")
na = input("enter name:")
mo = Animal.Animal.check_mood()
animals = Animal.Animal(an,na,mo)
print(animals.get_animal_type(), animals.get_name(), animals.get_mood())
You're trying to run a class method without instantiating your class first:
Animal.Animal.check_mood()
This will not work, unless check_mood() is a class method or a static method.
You need to instantiate your class and run your method on an instance
dog = Animal()
dog.check_mood()
or use a static method or a class method.
A static method is a function that does not receive an implicit first argument (like self in an instance method) and is thus just a regular function.
A class method is a function that receives the class itself, rather than an instance, as its first argument. This is usually only useful if you want to create an instance of a class from non-standard arguments. This is not likely be useful in this case, since you don't actually need the class within the method.
An example of how you would use a static method:
class Animal(object):
#staticmethod
def check_mood():
#yourfunction
and a class method:
class Animal(object):
#classmethod
def check_mood(cls):
#yourfunction
However, since you're talking about the mood of an animal, I'm assuming you want to instantiate an animal first, then check its mood.
check_mood() is an instance method - it needs a particular animal whose mood it checks. But here
mo = Animal.Animal.check_mood()
You're asking for the mood of the Animal class itself. Instead, call it from __init__ like this:
self.check_mood()
You would then delete the line giving you the current error, pass only two arguments to Animal(), and modify the def __init__ line so that it no longer expects a mood (since the Animal itself generates it randomly).