Python updating list from constructor by object.attribute - python

If a class is created with the attributes: name and list and the name attribute has a default value and the list is appending the name. Is it possible in somehow when I create an object "a" and type
"a.name = 'x' " that this 'x' will appear in the list given that the list is appending in the constructor?
class Person:
list = []
def __init__(self, name="Zed"):
self.name = name
self.list.append(name)
def printList(self):
print(self.list)
a = Person()
a.name = "Yasuo"
a.printList() #outputs Zed but Yasuo is expected.

You can make name a property, and implement a setter that updates the list.
class Person:
list = []
def __init__(self, name="Zed"):
self._name = name
self.list.append(name)
#property
def name(self):
return self._name
#name.setter
def name(self, name):
if self._name in self.list:
# remove the old name
index = self.list.index(self.name)
self.list[index] = name
else:
self.list.append(name)
self._name = name
def printList(self):
print(self.list)
a = Person()
a.name = "Yasuo"
a.printList() # prints ['Yasuo']

Related

Can i use 'self' in classes on its own?

I have a list of objects of the Person class. This list includes myself, so I need to remove "myself" from the list.
It means I need to remove the object from the list that calls this method.
class Population:
def __init__(self):
self.people = list()
class Person:
def __init__(self):
self.friends_list = list()
def make_friends(self, population, n):
temp_list = population.copy()
temp_list.remove(self)
self.friends_list.extend(random.sample(temp_list,n))
my test:
per = Person()
per2 = Person()
per3 = Person()
per4 = Person()
per5 = Person()
pop = [per,per2,per3,per4,per5]
for per in pop:
per.make_friends(pop, 2)
print('ME: ',per)
print(per.friends_list)
My tests run well, but there are general tests that check the code and they generate an error on this line:
try:
stud_res = person.make_friends(population, count)
except Exception:
print("\tPerson.make_friends() generated error")
return
Can I use self in this way, and if not, how can I better remove "myself" from the list?
It is a perfectly fine use case. By the way, note that you're overriding the builtin list.
To use self, you have to share a list collection between instances of a Person-class. In that case this collection should be declared as a class attribute or global list variable (not an instance attribute).
These samples of code are working:
with global list variable:
class Person:
def __init__(self, name):
self.name = name
def make_friends(self, list):
list.remove(self)
def __repr__(self):
return self.name
p1 = Person("Joe")
p2 = Person("Barack")
the_list = []
the_list.append(p1)
the_list.append(p2)
p1.make_friends(the_list)
print(the_list)
With class attribute:
class Person2:
class_list = []
def __init__(self, name):
self.name = name
Person2.class_list.append(self)
def make_friends(self):
Person2.class_list.remove(self)
def __repr__(self):
return self.name
p1 = Person2("Joe")
p2 = Person2("Barack")
print(Person2.class_list)
p1.make_friends()
print(Person2.class_list)
EDIT:
Variable 3 when a list of people is inside another class.
For accessing a list inside another class you could use attribute name or public method to get it if implemented:
class ClassWithList:
def __init__(self):
self.list_collection = []
def get_list(self):
return self.list_collection
class_with_list = ClassWithList()
class Person:
def __init__(self, name):
self.name = name
def make_friends(self, list):
list.remove(self)
def __repr__(self):
return self.name
p1 = Person("Joe")
p2 = Person("Barack")
# using implemented get-method of instance list attribute
class_with_list.get_list().append(p1)
class_with_list.get_list().append(p2)
print(class_with_list.get_list())
p1.make_friends(class_with_list.get_list())
print(class_with_list.get_list())
# instance list attribute of class`ClassWithList
print(class_with_list.list_collection)
p2.make_friends(class_with_list.list_collection)
print(class_with_list.list_collection)

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"

Mutable variable in a class

If I have a class like this.
class Person():
def __init__(self, name):
self._name = name
self._name_list = []
if(self._name not in self._name_list):
self._name_list.append(self._name)
father = Person("Michael")
mother = Person("Sharon")
>>>self._name_list
["Michael", "Sharon"]
How can I do this without creating a global variable? Everytime I instantiate a new person it creates their own list. But I need a list inside the scope of the class that appends a name everytime a new person is created.
You can save it inside the class itself, like this:
class Person():
_name_list = []
def __init__(self, name):
self._name = name
if self._name not in self._name_list:
self._name_list.append(self._name)
father = Person("Michael")
mother = Person("Sharon")
print(Person._name_list)
outputs:
['Michael', 'Sharon']
You can try this:
people = []
class Person():
def __init__(self, name):
self._name = name
if self._name not in people:
global people
people.append(self._name)
person = Person('name1')
person1 = Person('name2')
print(people)
Output:
['name1', 'name2']

How to convert all elements automatically in a list to a given object in Python

I want to create a list child class that can convert all elements automatically in it to an object no matter the element is create by init or append or extend. So by using both for loop or getitem. Here's a simple example code. What kind of magic method should I use?
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
c = CustomerList('a')
c.append('b')
c[0] # Object A with name a
c[1] # Object A with name b
for ele in c:
print(c)
# Object A with name a
# Object A with name b
are you asking how to override __append__?
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
def append(self,letter):
super(CustomerList, self).append(A(letter))
I guess???.. but as mentioned in the comments if you want
my_custom_list.extend(["A","B","V"])
my_custom_list[2] = "A"
to work you will need to override
def __setitem__(self,key,value): # cover a[2]='A'
super(CustomerList,self).__setitem__(key,A(value))
def extend(self,other):
super(CustomerList,self).extend([A(val) for val in other])
of coarse you probably then need to override both __add__,__iadd__ at a minimum as well
I think what you're trying to do is: When you append a new item into the list, it is an object of class A. What you can do is override list.append function:
class A():
def __init__(self, name):
self.name = name
def __repr__(self):
return 'Object A with name {}'.format(self.name)
class CustomerList(list):
def __init__(self, *args):
super(CustomerList, self).__init__(*args)
def append(self, arg):
new_obj = A(arg)
self.insert(len(self), new_obj)

Python - Class methods with input

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)

Categories