python property decorator in OOP [duplicate] - python

This question already has an answer here:
Setting property causes maximum recursion depth exceeded
(1 answer)
Closed 6 months ago.
I was testing the idea of using #property in OOP:
class Person(object):
#property
def name(self):
return self.name
#name.setter
def name(self, newname):
self.name = newname
james = Person()
james.name = 'James Bond'
print(james.name)
>>> RecursionError: maximum recursion depth exceeded
Somehow this gives me an Error for recursion??
but if I change self.name to self._name, it seems to solve the problem. So I guess I can not set a self.name while I am using name() as a #property?

The reason for the error is that you are attempting to return an attribute with exactly the same name as the method being decorated by property. Thus, when you call the method name, the call triggers the method again, because self.name is the method declared in the class. This triggers the non-terminating recursion. Instead, change the attribute name:
class Person(object):
#property
def name(self):
return self._name
#name.setter
def name(self, newname):
self._name = newname

Also, you can create the internal proxy object if you think that _ notation is ugly (example for python 3.3+):
import types
class Person(object):
def __init__(self):
self.me = types.SimpleNamespace(name = "")
#property
def name(self):
return self.me.name
#name.setter
def name(self, newname):
self.me.name = newname

Related

TypeError: Parent.__init__() missing 1 required positional argument: 'name' [duplicate]

This question already has answers here:
__init__() missing 1 required positional argument: 'quantity'
(4 answers)
Closed last month.
class Parent:
def __init__(self, name):
self.name = name
def printName(self):
print(self.name)
class Child(Parent):
def __init__(self, name):
Parent.__init__(name)
bob = Child('Bob')
bob.printName()
It's working with super().__init__(name) but not with the class name, why?
It doesn't work since Parent.__init__ is defined to take two arguments: self and name, and you're passing just a single argument to it. Thus, if you want to call it like that, you need to use Parent.__init__(self, name). But there really is no point, and you should instead just use super().__init__(name), as you already know.
The init() method always requires the self parameter, which is a reference to the object itself. Without the self parameter, the init() method would not have access to the object's attributes and would not be able to initialize them properly. The fix can be found here.
class Parent:
def __init__(self, name):
self.name = name
def printName(self):
print(self.name)
class Child(Parent):
def __init__(self, name):
Parent.__init__(self, name)
bob = Child('Bob')
bob.printName()
That is because Parent.__init__() always requires self.
However, super().__init__() does not require self because it implicitly finds the self by MRO (Method Resolution Order). You can see the order using Class.__mro__, as follows:
class Parent:
def __init__(self, name):
self.name = name
def printName(self):
print(self.name)
class Child(Parent):
def __init__(self, name):
Parent.__init__(self, name)
bob = Child('Bob')
print(Child.__mro__)
# (<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>)
Here, if you use super.__init__(name) instead of Parent.__init(self, name), it will find Parent by the order.
For more information: see https://docs.python.org/3/library/functions.html#super

How Can a function act like a descriptor?

def typed_property(name, expected_type):
storage_name = '_' + name
#property
def prop(self):
return getattr(self, storage_name)
#prop.setter
def prop(self, value):
if not isinstance(value, expected_type):
raise TypeError('{} must be a {}'.format(name, expected_type))
setattr(self, storage_name, value)
return prop
class Person:
name = typed_property('name', str)
age = typed_property('age', int)
def __init__(self, name, age):
self.name = name
self.age = age
Function typed_property() acts like a descriptor. Why prop() is called when executing this code line (name = typed_property('name', str))?
I don't know what you mean by "descriptor". typed_property allows a property to call a function for additional processing. prop() is not called when executing the line you mentioned. It is called when executing self.name = name. The #prop.setter makes it so the object can respond to property calls like that.
When you call typed_property to set the value of the class properties name and age, you are really defining those to be methods to use to access the instance values self.name and self.age. This is the same as below omitting age for simplicity:
class Person:
def __init__(self, name):
self.name = name
#property
def name(self):
print("=== ACESSING")
return self.name
#name.setter
def name(self, name):
print("=== MUTATING")
self.name = name
This marks the name(self) method as the accessor for self.name, and name(self, val) as the mutator. The mutator is called whenever you try to change (mutate) the value of its assigned property, in this case self.name. This includes when you are calling it in the __init__ method. However, using the class as defined above will result in an infinite recursion because I am calling the mutator from inside the mutator. So "=== MUTATING" will be printed ending in a recursion error. So a small adjustment is needed:
class Person:
def __init__(self, name):
self._name = name
#property
def name(self):
print("=== ACCESSING")
return self._name
#name.setter
def name(self, val):
print("=== MUTATING")
self._name = val
Now that underlying property is name _name rather than name the mutator will set the value of _name rather than setting it for name and recur into itself infinitely. For example, using the class as defined above:
>>> p = Person("joshmeranda")
>>> p.name
=== ACCESSING
"joshmeranda"

How can i print the attributes of an object list that stores objects? [duplicate]

This question already has answers here:
Printing a list of objects of user defined class
(3 answers)
Closed 2 years ago.
i have a class for lists that creates list objects and deals with list actions, and i have another class 'Dog' to create Dog objects. However, i canĀ“t manage to print the atributes of class 'Dog'(name and age)after appending an object of type 'Dog' to a list of type 'Dogslist'. Instead by printing the list it prints the addresses of the objects. How can i print name and age of all the dogs i have in the list?
class Dogslist():
def __init__(self):
self.lista = []
def append(self, object):
self.lista.append(object)
def delete(self):
pass
def showlist(self):
pass
class Dog:
def __init__(self, name, age):
self.__name = name
self.__age = age
def description(self):
return self.__name, self.__age
a=Dog("Geralt", 10)
b=Dog("Vesemir", 13)
doglist = Dogslist()
doglist.append(a)
doglist.append(b)
print(doglist.lista)
The output shows the following wich refers to objects' addresses:
[<__main__.Dog object at 0x7f25ed30c0a0>, <__main__.Dog object
at 0x7f25ed2f18b0>]
Implement __str__ or __repr__ for Dog so Python knows how to represent it.
class Dog:
def __init__(self, name, age):
self.__name = name
self.__age = age
def __repr__(self):
return f"Dog({self.__name}, {self.__age})"
def __str__(self):
return f"Woof! I'm {self.__name}, and I'm {self.__age} years old!"
More information about __str__ and __repr__ can be found here
You should be able to access the objects like below( because those attributes were defined as 'private' and you cant access them directly) .
for item in doglist.lista:
print(item.__dict__['_Dog__name'])
print(item.__dict__['_Dog__age'])
Simpler to just not make those attributes private.Then you would be able to access it like item.name
class Dogslist():
def __init__(self):
self.lista = []
def append(self, objectt):
self.lista.append(objectt)
def delete(self):
pass
def showlist(self):
return(self.lista)
Dogslist.lista technically only exists within the class so you can't call it from outside. You need a function in that class that returns Dogslist.lista
class Dog:
def __init__(self, name, age):
self.__name = name
self.__age = age
def description(self):
return self.__name, self.__age
a=Dog("Geralt", 10)
b=Dog("Vesemir", 13)
doglist = Dogslist()
Same with the dogs.
doglist.append(a.description())
doglist.append(b.description())
print(doglist.showlist())
Result:
[('Geralt', 10), ('Vesemir', 13)]

Property setter method not working upon class creation

I'm trying to set an attribute name on my class Attachment. The name attribute needs to be set based on whether it's a zip file or not. If it is a zip file I need to return the unzipped filename rather than the zip filename. Here is the class:
from os.path import splitext
class Attachment(object):
def __init__(self, name):
self.__name = name
if self.__name.endswith(".zip"):
self.zip_contents = {"content":"test", "name":"testing.txt"}
#property
def extension(self):
_, ext = splitext(self.__name)
return ext.lower()
#property
def name(self):
print('Called getter')
return self.__name
#name.setter
def name(self, name):
print('Calling setter')
if name.endswith(".zip"):
self.__name = self.zip_contents["name"]
else:
self.__name = name
#name.deleter
def name(self):
del self.__name
test = Attachment("testing.zip")
print test.name
I am receiving the following when I try printing test.name
Called getter
testing.zip
Can someone explain what I am doing wrong here and why the setter is not being called? Thanks!

Python: how to print instance variable of type string

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)

Categories