How to call a method when an attribute is written - python

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

Related

Python Inheritence from constructor

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)

How would I print "bark" using python classes in this situation?

How would I print "bark" using python classes.
class pet:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
class dog(pet):
def __init__(self, name, age):
super().__init__(name, age)
def bark(self):
print("bark")
max = pet("max", 5)
max.dog.bark()
To call the dog class's bark method, you need an instance of dog.
max = dog("max", 5)
max.bark()
Here you go.
class pet:
def __init__(self, name, age):
self.name = name
self.age = age
def get_name(self):
return self.name
def get_age(self):
return self.age
class dog(pet):
def __init__(self, name, age):
super().__init__(name, age)
def bark(self):
print("bark")
my_pet = dog("max",3)
my_pet.bark()

what is wrong with this property decorator (python) [duplicate]

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

Is there another way to get value from object method into object variable

I made code that goes like this
class Dog():
def breed():
return "rottwiller"
def __init__(self, name, age, breed=breed()):
self.name = name
self.age = age
self.breed = breed
Is there another way to achieve "self.breed" value instead of breed=breed(), but through function method as well.
for example something similar to this( I have tried out this that does not work):
def __init__(self, name, age):
self.name = name
self.age = age
self.breed = breed()
You shouldn't name an attribute and a method the same thing. That being said, my suspicions for why you are asking this question tell me I should point you towards the #property decorator.
class Dog():
def __init__(self, name, age, breed):
self.name = name
self.age = age
self._breed = breed
#property
def breed(self):
return self._breed
This is overkill for your example, but if you have a Dog class and a specific breed, I imagine you are thinking about subclasses and possibly more complicated things than you are doing here. Essentially, the #property decorator will allow you to access breed as an attribute, and will return the value of the call to the function you define under it by the same name.
There are a number of issues with the code posted.
The breed method is overwritten in __init__ method as you set the breed attribute. So the method should be renamed.
The breed method needs to specify the first argument self.
class Dog:
def get_breed(self):
return "rottwiller"
def __init__(self, name, age):
self.name = name
self.age = age
self.breed = self.get_breed()

python custom class operator overloading

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)

Categories

Resources