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!
Related
How to override details() method in child class Doctor
.I want to override the details method to return id, name and Regno
current code gives an error
class Member:
def __init__(self, id, name):
self.__id = id
self.__name = name
def details(self):
return self.__id, self.__name
class Doctor(Member):
def __init__(self, id, name, drNumber):
super().__init__(id, name)
self.__drNumber = drNumber
def details(self):
return self.__id, self.__name, self.__regNo
doc = Doctor(1123, "Tim", "xxx5678")
print(doc.details())
You can't access the private attributes, because the name mangling adds the current class to the name, so it won't find the attribute with the parent's name.
Instead, call the parent method and add your value to the result.
class Doctor(Member):
def __init__(self, id, name, drNumber):
super().__init__(id, name)
self.__drNumber = drNumber
def details(self):
parent_details = super().details()
return parent_details + (self.__drNumber,)
How '#decorator' actually works?
I just knew it works like this:
def deco(func):...
#deco
def func():...
# same
def func():...
func = deco(func)
But it doesn't work well in '#class.method'
Below is an example:
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
class Person:
def __init__(self, name):
self._name = name
## #propery
def name(self):
return self._name
name = property(name)
## #name.setter
def name(self, value):
self._name = value
name = name.setter(name) <----- error
The error here is because name has been changed to point to "def name(self.value):..."
I'd like to know how '#property.method' actually works.
I just guessed like that:
class Person:
def __init__(self, name):
self._name = name
## #propery
def name(self):
return self._name
name = property(name)
## #name.setter
x = name.setter
def name(self, value):
self._name = value
name = x.setter(name)
del x
Is it right or is there other way to work that I don't know?
Thank you for reading!
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"
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
How can I create a method in a class with a user input?
What argument should I pass when I am calling the method ?
class Student:
def __init__(self):
self._name = ''
def getName(self):
return self._name
def setName(self, newName):
newName = input ('Inserire nome:')
self._name = newName
studente = Student()
studente.setName(newName)
This should work:
class Student:
def __init__(self):
self._name = ''
def getName(self):
return self._name
def setName(self, newName):
self._name = newName
studente = Student()
newName = input('Inserire nome:')
studente.setName(newName)
You were defining the input inside the method itself but passing the variable outside. So the variable newName wasn't defined outside. Let me know if it doesn't work. I haven't tested it, but seems like the conspicuous error here.
If I understood what you want correctly,
why dont you try to ask for an input when initialsing class instance?
class MyClass(object):
def __init__(self):
self.name = input('Enter name> ')
X = MyClass() # when executing that line, you'll be asked for a name to input
Then you'll be able to acces name attribute by X.name and set it to whatever you'd like to by X.name = foo
You could also play with the builtin setattr and dismiss the explicit setters/getters:
class Student:
pass
student = Student()
newName = input('Inserire nome:')
setattr(student, 'name', newName)