Attempting to compare two objects' data members; however, the error message has no specific details, which leaves me with little information on how to go about correcting it
class Person:
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id
def same_person(Person lhs, Person rhs):
return lhs.id == rhs.id
person1 = Person("David Joyner", 30, 901234567)
person2 = Person("D. Joyner", 29, 901234567)
person3 = Person("David Joyner", 30, 903987654)
# print calls provided as part of an exercise: not my implementation
print(same_person(person1, person2))
print(same_person(person1, person3))
Python 3.6.5
Command: python person.py
Error message
If it were an indentation level the following error is displayed
same_person is a method of the class Person and should take just an argument as input. It should be defined as:
def same_person(self, other):
return self.id == other.id
and called as
person1.same_person(person2)
or you could override the __eq__ method (i.e., ==).
def __eq__(self, other):
return self.id == other.id
in order to be able to do it as person1 == person2
The other answers are correct and provide the best way to do it, but I realized that you wrote:
print calls provided as part of an exercise: not my implementation
print(same_person(person1, person2))
print(same_person(person1, person3))
The exercise probably wants you to define a function outside the class. You can do that by removing that function from the class and writing it un-indented outside the class (without providing class type too). For example:
class Person:
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id
def same_person(lhs, rhs):
return lhs.id == rhs.id
person1 = Person("David Joyner", 30, 901234567)
person2 = Person("D. Joyner", 29, 901234567)
person3 = Person("David Joyner", 30, 903987654)
print(same_person(person1, person2))
print(same_person(person1, person3))
class Person:
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id
def same_person(self, lhs, rhs):
return lhs.id == rhs.id
you dont have to define lhs and rhs type in python unless you are using typings.
Quite a few mistakes:
The arguments in the method cannot be preceded by the Person classname
You have not defined instances person1, person2 and person3
If you define an instance method (same_person), it should be used ON an instance.
This is what I would do:
class Person:
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id
def same_person(self, other):
return self.id == other.id
person1 = Person("Bob", 25, 1)
person2 = Person("Mike", 33, 1)
person3 = Person("Maria", 28, 2)
print(person1.same_person(person2))
print(person1.same_person(person3))
Output:
True
False
You'd better rewrite eq to compare objects:
class Person:
def __init__(self, name, age, id):
self.name = name
self.age = age
self.id = id
def __eq__(self, other):
return self.id == other.id
person1 = Person("Bob", 25, 1)
person2 = Person("Mike", 33, 1)
person3 = Person("Maria", 28, 2)
print(person1 == person2)
print(person1 == person3)
>>> True
>>> False
https://devinpractice.com/2016/11/29/python-objects-comparison/
Related
person.py
class Person:
"""---A class representing a person---"""
# Person constructor
def __init__(self,n,a):
self.full_name = n
self.age = a
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
Person.__init__(self,n,a)
self.school = s
driver.py
from person import *
a = Student("Alice", 19, "Univ")
It throws TypeError: __init__() takes 3 positional arguments but 4 were given
I tried to change Student class to the following:
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
super().__init__(n,a)
self.school = s
The error still exists.
Why does this happen? Is super() keyword required to add new attributes?
EDIT: The problem is solved. There was an indentation issue in the source code rendering this strange behavior, hence the question should be closed.
This line:
Person.__init__(self,n,a)
Is the problem. Recall that methods are automatically passed a reference to themselves, so you just passed a second one.
There's also a well-established pattern for this:
class Person
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, school, *args):
super().__init__(*args)
self.school = school
student = Student('Washington Elementary', "Johnny Go'gettem", 10)
although note that simply removing your reference to self in the Person.__init__ call inside Student.__init__ would be sufficient.
Note that you can override the default method behavior with a couple of decorators that become quite useful in certain situations. Neither apply here, but just a bit of knowledge to tease your brain a bit:
def SomeClass:
attr = "class-scoped"
def __init__(self):
self.attr = "instance-scoped"
def some_method(self):
return self.attr == "instance-scoped"
#classmethod
def some_classmethod(cls):
return cls.attr == "class-scoped"
#staticmethod
def some_staticmethod():
return "I'm not given a \"self\" parameter at all!"
classmethods are particularly useful as alternate constructors
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
#classmethod
def from_tuple(cls, tup) -> "Person":
"""Expects a tuple of (name, age) and constructs a Person"""
name, age = tup
return cls(name, age)
#classmethod
def from_dict(cls, dct) -> "Person":
"""Expects a dictionary with keys "name" and "age" and constructs a Person"""
try:
name = dct['name']
age = dct['age']
except KeyError:
raise ValueError(f"Dictionary {dct} does not have required keys 'name' and 'age'")
else:
return cls(name, age)
This question already has answers here:
How does the #property decorator work in Python?
(15 answers)
Closed 2 years ago.
I tried adding property decorators to my class but something went wrong.
I got 6 errors!!!
my code:
class person:
def __init__ (self, name, age):
self.name = name
self.age = age
#property
def age(self):
return self.age
#age.setter
def age(self, new_age):
if isinstance(new_age, int):
self.age = new_age
def __str__ (self):
return f"{self.name} is {self.age}"
p1 = person('moe',34)
print(person)
You are using same names and the property then shadows the member. This makes these recursion issue as self.age calls itself again and again in setter.
You need to use different attribute name, like this:
class person:
def __init__ (self, name, age):
self.name = name
self._age = age
#property
def age(self):
return self._age
#age.setter
def age(self, new_age):
if isinstance(new_age, int):
self._age = new_age
def __str__ (self):
return f"{self.name} is {self.age}"
p1 = person('moe',34)
print(p1)
You defined age both as a class method and a class variable. When you refer to self.age, the interpreter has no way of knowing what you meant.
Change the code to this to fix it:
class person:
def __init__ (self, name, age):
self.name = name
self._age = age
#property
def age(self):
return self._age
#age.setter
def age(self, new_age):
if isinstance(new_age, int):
self._age = new_age
def __str__ (self):
# Here you can either use the property or the real variable
return f"{self.name} is {self.age}"
p1 = person('moe',34)
print(person)
There may be two mistakes in your code.
First, methods and attributes shouldn't have the same name age.
You should print the instance p1, if I understand your intention correctly.
Something like this:
class person:
def __init__ (self, name, age):
self.name = name
self._age = age
#property
def age(self):
return self._age
#age.setter
def age(self, new_age):
if isinstance(new_age, int):
self._age = new_age
def __str__ (self):
return f"{self.name} is {self._age}"
p1 = person('moe',34)
print(p1)
You get:
moe is 34
I want to call a method when an attribute of an object is written. For example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def isAdult(self):
print(True if self.age>=21 else False)
If i want to call .isAdult() when an object's age is written a value, how can I achieve this?
I heard of people suggesting to use decorators, but they didn't give more details.
What you want to have is called a setter. You must use new-style classes and extend object to use them:
class Person(object):
def __init__(self, name, age):
self.name = name
self._age = age
def isAdult(self):
print(self.age >= 21)
#property
def age(self):
return self._age
#age.setter
def age(self, value):
self._age = value
self.isAdult()
Then, for example:
p = Person("John Doe", 12)
p.age = 16
p.age = 25
will print
False
True
you can use decorator in class like below.
In below code, isAdultDeco will be call when you create Person object.
class Person:
def isAdultDeco(func):
def foo(self, name, age):
func(self, name, age)
print(True if self.age>=21 else False)
return foo
#isAdultDeco
def __init__(self, name, age):
self.name = name
self.age = age
Person('a',22) # True
Person('a',20) # False
class Artist:
def __init__(self, name, dob):
self.name = name
self.dob = dob
def get_name(self):
return self.name
def get_dob(self):
return self.dob
def age(self):
if get_date_today() < (2013, 12, 27):
return self.age
else:
return self.age + 1
def get_date_today():
return (2013, 10, 30) #'today'
hw = Artist("Hayley Williams", (1988, 12, 27))
print(hw.age()) # 24 if 'today' is < (2013, 12, 27), 25 if 'today' is >= (2013, 12, 27)
How do I do an addition in the else loop. It can't work here because I can't add 1 to the method. So what is wrong ?
from datetime import datetime
class Artist:
def __init__(self, name, dob):
self.name = name
self.dob = dob
def get_name(self):
return self.name
def get_dob(self):
return self.dob
def age(self):
return ((datetime.today() - datetime(*self.dob)).days)/365
datetime is module in python which use to perform date realted operation. datetime.today will return you current date.
You do not have a self.age variable in your __init__ loop. Try this instead:
class Artist:
def __init__(self, name, dob, age):
self.name = name
self.dob = dob
self.age = age
def get_name(self):
return self.name
def get_dob(self):
return self.dob
def get_age(self):
if get_date_today() < (2013, 12, 27):
return self.age
else:
return self.age + 1
You are trying to return an age in your method age() which you have no data about. Decide where to get that data from and store it, then you can return it in the method.
One way to achieve this is to get the data upon object construction:
def __init__(self, birthday):
self.birthday = birthday
# store also your other data like name and dob
then you can use it to compute the age:
def age(self):
if get_date_today() < (2013, 12, 27):
return get_date_today() - self.birthday
else:
return get_date_today() - self.birthday + 1
# this assumes of course that you are using a date type which understands subtraction
But there are other ways like getting the age from a data base or just returning the age given on construction (see sshashank124's answer for that).
suppose I have a class:
class Cat:
def __init__(self, name = "default", age = 0):
self.name = name
self.age = age
I also have a list of Cats:
l = [Cat('Joe')]
Now I can't call the following:
if 'Joe' in l: # the right syntax would be if Cat('Joe') in list
Which operator do I need to overload to be able to identify objects of class Cat by their member variable name?
You have to define the __eq__ method, as shown below:
class Cat:
def __init__(self, name = "default", age = 0):
self.name = name
self.age = age
def __eq__(self, other):
if isinstance(other, str):
return self.name == other
elif isinstance(other, Cat):
return self.name == other.name
So that when you run your check:
l = [Cat('Joe')]
'Joe' in l
#True
__contains__ on list objects is implemented by checking for equality, so override __eq__:
class Cat:
def __init__(self, name = "default", age = 0):
self.name = name
self.age = age
def __eq__(self, other):
return self.name == other
This works irrespective of ordering because equality checking swaps its arguments when the left side does not support the operation.
If you want it to work with hash-based containers (e.g. set, dict) you'll have to override __hash__ as well:
def __hash__(self):
return hash(self.name)