Override property - python

In a base class I'm creating a property with property() function:
from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
#abstractmethod
def __init__(self, name, telephone):
self.name = name
self.telephone = telephone
def get_name(self) -> str:
return self.__name
def set_name(self, value: str):
self.__name = value
name = property(fget=get_name, fset=set_name)
And in the derived class I need to override the set_name method, that is, Person.name.fset should point to a modified version of the method. I tried this but it did not work:
class Employee(Person):
def __init__(self, name, telephone, email):
super().__init__(name, telephone)
self.email = email
def set_name(self, value):
super().set_name('override')
Person.name.fset = set_name
Is there any way to override property in python?

Related

Python can't use setter

here is my issue, when i'm using #property decorator I can't use setter
class Worker:
def __init__(self,name):
self.__name = name
#property
def name(self):
return self.__name
#name.setter
def set_name(self,new_name):
self.__name = new_name
worker1 = Worker('A')
print(worker1.name)
worker1.name = 'B'
print(worker1.name)
It gives AttributeError: can't set attribute 'name', when I use setter
Let's rewrite this without decorator syntax.
class Worker:
def __init__(self,name):
self.__name = name
def name(self):
return self.__name
name = proprety(name)
def set_name(self,new_name):
self.__name = new_name
set_name = name.setter(set_name)
This makes it easier to see that you now have two similar properties: name, which only provides read-only access to the __name attribute, and set_name, which has a getter and a setter for the same attribute. name.setter takes a method, and returns a new property that replaces the old setter (if any) of name with the given function.
You want a single property named name, so you must use the same name when defining the setter.
class Worker:
def __init__(self,name):
self.__name = name
#property
def name(self):
return self.__name
name = proprety(name)
#name.setter
def name(self,new_name):
self.__name = new_name
The decorators are a way to simplify code like the following:
class Worker:
def __init__(self,name):
self.__name = name
def get_name(self):
return self.__name
def set_name(self,new_name):
self.__name = new_name
name = property(get_name, set_name)
del get_name, set_name
Change set_name to name.
Python docs for #property:
Be sure to give the additional functions the same name as the original property (x in this case.)

How would I write a metaclass, "Place", which can be used to define classes Region, State, and City?

I am basically trying to accomplish the following, but with a metaclass.
class Region():
def __init__(self, name):
self.name = name
class State():
def __init__(self, name, region):
self.name = name
class City():
def __init__(self, name, state):
self.name = name
self.state = state

transform class methods using parent class' decorator method

def greeting_decorator(original_function):
def return_function(*args):
name = 'John'
return f'Hi, I\'m {name}, fullname: {original_function(*args)}'
return return_function
#greeting_decorator
def greeting(name, surname):
return f'{name} {surname}'
print(greeting('John', 'Doe'))
Above, I have a simple decorator function that works as intended.
I'd like to do something similar, but with an inherited class.
How might I go about inheriting a decorator function like this:
class Guy:
def __init__(self, name):
self.name = 'John'
def greeting_decorator(self, original_function):
def return_function(*args):
return f'Hi, I\'m {self.name}, fullname: {original_function(*args)}'
return return_function
class GuyWithSurname(Guy):
def __init__(self, name, surname):
super().__init__(name)
self.surname = surname
#greeting_decorator # <----- here
def __str__(self):
return f'{self.name} {self.surname}'
JohnDoe = GuyWithSurname('John', 'Doe')
print(JohnDoe)
If you are certain that the parent class will always be Guy, you can simply annotate via #Guy.greeting_decorator:
class Guy:
def __init__(self, name):
self.name = 'John'
def greeting_decorator(original_function):
def return_function(self, *args):
return f'Hi, I\'m {self.name}, fullname: {original_function(self, *args)}'
return return_function
class GuyWithSurname(Guy):
def __init__(self, name, surname):
super().__init__(name)
self.surname = surname
#Guy.greeting_decorator # <----- here
def __str__(self):
return f'{self.name} {self.surname}'
JohnDoe = GuyWithSurname('John', 'Doe')
That way, when you call print(JohnDoe) it will output Hi, I'm John, fullname: John Doe.
Note that I had to change the greeting_decorator and the return_function parameters to properly handle self.

Overriding an inherited property setter (without fset)

This question arises from reading the answer to Overriding an inherited property setter. Assume the case where I have the base Person class:
class Person:
def __init__(self, name):
self.name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
Following the top-voted answer the correct way to override the setter would be:
class Superhero(Person):
def __init__(self, name):
super().__init__(name)
#Person.name.setter
def name(self, value):
Person.name.fset(self, "Super " + value)
Would there be a difference if instead, the Superhero class updated the _name attribute directly? i.e.
class Superhero(Person):
def __init__(self, name):
super().__init__(name)
#Person.name.setter
def name(self, value):
self._name = "Super " + value
(I know, a comment in the referenced question would have been enough but due to reputation I do not have the privileges to ask.)

TypeError: 'str' object is not callable oop

Im new to oop with python. Why am I getting this error? Shouldnt it print tom and 12?
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def name(self):
return self.name
def age(self):
return self.age
dog = Dog("tom", 12)
print(dog.name())
print(dog.age())
Instance attributes take precedence over class attributes when one of each exists and they have the same name. If you are going to have a method that returns the value of an attribute, a common convention is to make the name of the instance attribute a "private" version of the method name by prefixing an underscore to the name.
class Dog:
def __init__(self, name, age):
self._name = name
self._age = age
def name(self):
return self._name
def age(self):
return self._age
However, until you have a good reason to hide the attribute behind a getter, just expose the attribute as part of the public interface.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
d = Dog("tom", 12)
print(dog.name)
If you later decide to hide the attribute behind a getter and/or setter, you can use a property to do so without changing the public interface.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
#property
def name(self):
return self._name
#name.setter
def name(self, v):
self._name = v
d = Dog("tom", 12)
print(dog.name) # Works the same as before
name is a variable and name is also a function.
Therefore this error.
Just do print(self.name)
you have to create class and change the name of funtion like this
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def Name(self):
return self.name
def age(self):
return self.age
dog = Dog("tom", 12)
print(dog.name())
print(dog.age())

Categories