I was trying to set some property to a class via decorator but its not working as expected. How can I get the age via property decorator.
class Person:
def __init__(self):
self.name = ""
self.age = ""
self.dob = ""
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
#property
def age(self):
return self._age
#age.setter
def age(self, value):
self._age = value
#property
def dob(self):
return self._dob
#dob.setter
def dob(self, value):
self._dob = value
self._age = 20 #Utility.getAge(value)
if __name__ == '__main__':
p = Person()
p.name = "Andrew"
p.dob = "10-10-1980"
print p.name
print p.dob
print p.age
Output:
John
10-10-1980
#20 <-missing
I am not getting the age. Am I missing something?
Ok, this took me a while to actually find out why the above code was not working in python 2.7.
If you look at the property documentation for python2.7, you would find that the class that has the property decorators used is actually inheriting object class and your code doesn't.
Now, when you don't inherit, the property decorator actually doesn't work and setting or getting properties don't work either
(Put a print statements in getter or setter functions and they wont be printed since they were never invoked while setting p.name or getting p.name).
Question : So how come get/set for p.name and p.dob works?
Since, you are not inheriting object class in your class, the property decorators are useless, they are not being invoked but have created those property on the Person object.
But, when you use below code, you are explicitly setting those value (without the use of setters), hence thy are printed and p.age never got assigned any value.
p.name = "Andrew"
p.dob = "10-10-1980"
Code Fix : Update your class declaration to -->
class Person(object):
and setters/getters would work (check using print statements) and self.age would also work.
Bonus : Python3 onwards, all classes, by default, inherit object class.
Related
i am new to python and i'm trying to understand the use of the 'getter'. it's use case is not obvious to me.
if i use a property decorator on a method and im able to return a certain value, what exactly would i use 'getter' for.
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) and 18 < new_age < 120:
self._age = new_age
The #property decorator adds a default getter on a given field in a Python class
that triggers a function call upon accessing a property.
The #property decorator turns the age() method into a “getter” for a read-only attribute with the same name. If want a “setter” then add #age.setter as you did in your question.
p = Person("John", 22)
print(p.age)
Output:
22
The property type can take up to 4 separate arguments (a getter, a setter, a deleter, and a doc string) when instantiating it. The first argument is a function that will be used as a getter. The second is a function that will be used as a setter. You could have written your class as
class Person:
def __init__(self,name, age):
self._name = name
self._age = age
def _age_getter(self):
return self._age
def _age_setter(self, new_age):
...
age = property(_age_getter, _age_setter)
This is cumbersome to write, so property objects have a number of methods for building the property up piece by piece. Using property as a simple decorator creates a read-only property with the decorated function as the getter.
# Equivalent to
# def age(self):
# return self._age
# age = property(age)
#property
def age(self):
return self._age
age.setter is a method of the property instance which creates a new property that is essentially a copy of age, but with its argument used to replace whatever setter the original property had. (Decorator syntax is why all the methods involved have to have the same name, so that we are constantly replacing the original property with the new, augmented property, rather than defining multiple similar properties with different names instead.)
#age.setter
def age(self, new_age):
...
Desugaring this requires the use of a temporary variable, to avoid losing the old value of age prematurely.
old_property = age
def age(self, new_age):
...
age = old_property.setter(age)
A silly, but legal, way of defining the property takes advantage of the fact that property can be defined with no arguments, and that there is a getter method that can be used as a decorator as well. (I don't think I've ever seen it used in the wild, though.)
class Person:
def __init__(self, name, age):
...
age = property()
#age.getter
def age(self):
...
#age.setter
def age(self, new_age):
...
Note that the order in which we use getter, setter, (and deleter) methods doesn't matter. The conventional order comes from the order in which property expects its positional arguments to be supplied, as well as the fact that new properties are virtually always defined by decorating the getter directly, rather than adding a getter to a property after it is created.
I'm trying to create a Class with a list of fields. See code below.
class Character:
# Private Fields:
__age = 18
__weight = 200
__height = 72
def __init__(self, name):
self.__name = name
#property
def get_age(self):
return self.__age
#property
def get_name(self):
return self.__name
#property
def get_weight(self):
return self.__weight
#property
def get_height(self):
return self.__height
person = Character("someone")
print("name =", person.get_name,",", "age =", person.get_age)
Is there a way to avoid writing the #property for every private field you want to access? For instance is there a way to pass an attribute into a more general getter function like:
def get_attr(self,attr):
#set attr to __attr
#return self.attr
I tried using the join function, but it didn't work
Thanks for any help
To answer the question as asked, the simple solution is to compensate for the name mangling that is done with private members. e.g. to get the __age attribute you'd use: person._Character__age
But, this would be a terrible idea and I wouldn't recommend it. If you need them to be easily accessible, just remove the underscores. If they really need to be private, they shouldn't be easily accessible from outside the class anyway, so putting in a way to make them accessible defeats the purpose.
[This is in Python 3.6.8] So my impression was that a subclass inheriting from a superclass would be able to access the superclass' attributes freely, even if the attribute was "private". For example:
class A():
def __init__(self, name):
self.__name = name
def __str__(self):
return f'my name is {self.__name}'
class B(A):
def __init__(self, name, number):
A.__init__(self, name)
self.__number = number
def __str__(self):
return f'my name is {self.__name}\nmy number is {self.__number}'
def main():
name = input('name: ')
number = input('number: ')
obj = B(name, number)
print(obj)
main()
Apparently, when I try to use self.__name in the __str__ function of the subclass B, it throws an AttributeError exception, stating: AttributeError: 'B' object has no attribute '_B__name'. However, the program works as intended when I use a accessor (getter) method for getting the name instead of self.__name. So was my initial impression wrong, or am I making a misunderstanding somewhere?
class human(object):
def __init__(self, name=''):
self.name = name
#property
def name(self):
return self._name
#name.setter
def name(self, value):
self._name = value
class superhuman(human):
#property
def name(self):
return 'super ' + name
s = superhuman('john')
print s.name
# Doesn't work :( "AttributeError: can't set attribute"
s.name = 'jack'
print s.name
I want to be able to override the property but be able to use the super parent's setter without having to override the setter in the child class.
Is that pythonicaly possible?
Use just the .getter decorator of the original property:
class superhuman(human):
#human.name.getter
def name(self):
return 'super ' + self._name
Note that you have to use the full name to reach the original property descriptor on the parent class.
Demonstration:
>>> class superhuman(human):
... #human.name.getter
... def name(self):
... return 'super ' + self._name
...
>>> s = superhuman('john')
>>> print s.name
super john
>>> s.name = 'jack'
>>> print s.name
super jack
The property descriptor object is just one object, even though it can have multiple methods associated with it (the getter, setter and deleter). The .getter, .setter and .deleter decorator functions provided by an existing property descriptor return a copy of the descriptor itself, with that one specific method replaced.
So in your human base class what happens is that you first create the descriptor with the #property decorator, then replace that descriptor with one that has both a getter and a setter with the #name.setter syntax. That works because python decorators replace the original decorated function with the same name, it basically executes name = name.setter(name). See How does the #property decorator work? for the details on how that all works.
In your subclass you simply use that trick to create a new copy of the descriptor with just the getter replaced.
I am trying to print a string variable returned by name() function, which in this case should print "Jim, but Python is printing
`<bound method Human.name of <__main__.Human object at 0x7f9a18e2aed0>>`
Below is the code.
class Human:
def __init__(self):
name = None
def setName(self, _name):
name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
UPDATE:
After reading the answers, i updated the code as shown below, but, now i am getting a new error TypeError: 'str' object is not callable
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
def name(self):
return self.name
jim = Human()
jim.setName("Jim")
print(jim.name())
self.name is the method itself. You have no attributes storing the name. Nowhere do you actually set the name as an attribute. The following works:
class Human:
def __init__(self):
self.name = None
def setName(self, _name):
self.name = _name
# NOTE: There is no more name method here!
Now you have an actual attribute, and you don't need to call the method here:
jim = Human()
jim.setName("Jim")
print(jim.name) # directly using the attribute
You could even just set the attribute directly:
jim = Human()
jim.name = "Jim"
print(jim.name)
Alternatively, use self._name to store the name on the instance:
class Human:
_name = None
def setName(self, _name):
self._name = _name
def name(self):
return self._name
Here we used a class attribute Human._name as a default, and only set self._name on the instance in the Human.setName() method.
The problem is that name is the name of the internal variable in your object and also the name of the method.
The namespace for variables and methods is the same. Change the name of your method to something other than name. This will fix your getter. On first glance I thought that that would be all you have to do, but the recommendation in Martijn's answer also applies -- you need to assign to self.name and not just name in order to get your setter to work as well.
As an aside, this getter/setter pattern is not usually appropriate for Python. You should ask yourself why you want to use a getter/setter pattern over simply accessing the object's variable directly. See the section on getters and setters in this article for more detail.
You can use setter and getter properties instead of your custom defined methods.
class Human():
def __init__(self):
self._name = None
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
And then, use them:
jim = Human()
jim.name = "Jim"
print(jim.name)