What does append(self) mean in Python classes? - python

I am new to OOP in Python and working on inheritance concept. I came across the following code:
class ContactList(list):
def search(self, name):
'''Return all contacts that contain the search value in their name.'''
matching_contacts = []
for contact in self:
if name in contact.name:
matching_contacts.append(contact)
return matching_contacts
class Contact:
all_contacts = ContactList()
def __init__(self, name, email):
self.name = name
self.email = email
self.all_contacts.append(self)
I'm wondering why do we use self.all_contacts.append(self) and how does for contact in self work ?. If I understood correctly, self appoints to the instance of a class (object), and appending to a list is not trivial to me.

all_contacts is a class variable -- it is unique to the class, not to each instance. It can be accessed with Contact.all_contacts. Whenever you create a new contact, it is appended to this list of all contacts.
ContactList inherits from list, so for contact in self works the same way as for i in [1,2,3] -- it will loop through all the items that it contains. The only thing that it does differently from a list is implement a new method, search.

Well, basically you create a list of Contact and appending self add the current contact in the all_contacts list.
Now for your questions,
I'm wondering why do we use self.all_contacts.append(self)
We would use that because all_contacts is a class variable which means that the list will be shared among all Contact instances.
how does for contact in self work?
Well, as you said, since self represents the current instance, calling for contact in self is allowing you to iterate on the current Contacts list.
In other words, your code sample let you create Contact instance which is appended in a class variable (shared) automatically. Now, by providing a ContactList class that inherits from list, they allow you to use the implemented search method which will return you another list of Contact based on your search filter.

all_contacts is a class variable of Contact, initialized as an instance of ContactList, a subclass to list, so when a new Contact instance is instantiated via the __init__ method, self is assigned with the new instance being instantiated and self.all_contacts.append(self) would add the new Contact instance to the all_contacts list. This way, Contact.all_contacts will maintain a list of all Contact instances that have been instantiated.

Related

How to get class instanced using variable inside of the class

I am trying to access a class instance. I can't assign the class to a variable when I load it and then use it because I need to access the class based on what the user enters.
i.e: user goes to link website.com/classes/y, I need to access the instance with the name y.
I already handle the link and can get "y" or whatever the user entered by itself.
I have the class code as follows:
class LoadModel:
existing_models = []
def __init__(self, model_path):
self.name = model_path.parent.name
self.__class__.existing_models.append(self.name)
For now, I can verify if the class exists using the existing_models list, but how will I be able to access it using the self.name?
I want to access it using LoadModel.name.
It sounds like you want to keep a dictionary of model names to instances. You could do that with something like:
class LoadModel:
modelsByName = {}
def __init__(self, model_path):
self.name = model_path.parent.name
self.modelsByName[self.name] = self
Furthermore if you wanted to access an instance named name as LoadModel.name you could could add
setattr(self.__class__, self.name, self)
to __init__. Or if you were looking up by string (which it sounds like you might be) then you would just do LoadModel.modelsbyName[name].
Note also that you don't need to use self.__class__ when accessing members of the class that you have not assigned within the instance, and since you're only accessing the dictionary object defined in the class, you can use the reference inherited by the instance (self.modelsByName) instead of accessing the class explicitly (self.__class__.modelsByName).

Edit parent object's attributes in a nested object

I am trying to solve a problem where I have a class which is used to store objects of other classes. I want to be able to create a list in that parent object of certain types of nested objects. (sorry if I'm phrasing this badly)
For my example I am taking a real world problem of a House. In the house we may have many different 'things'. We could have people, pets, furniture, computers etc.
I want to be able to store this in a nested format so I create a House with any of those sub-objects within.
E.g
old_rectory = House(
Person('Barry'),
Person('Bob'),
Pet('Bill'),
Pet('Brenda')
)
I have created 3 classes: the first is for the House itself, then I have a class for people and a class for pets.
For the house class I use *argv so that I can add as many other objects as necessary.
Now in the example above I would like to be able to access old_rectory.people and see a list of any instances in old_rectory that are of the type Person.
class House:
def __init__(self,*argv):
self.people = []
self.pets = []
for arg in argv:
if isinstance(arg, Person):
self.people.append(arg)
elif isinstance(arg, Pet):
self.pets.append(arg)
class Person:
def __init__(self,name):
self.name = name
class Pet:
def __init__(self,name):
self.name = name
You can see that I have achieved this by hard coding the lists and some if conditions into the House class. But this means that whenever I create a new class I also need to add in a new list and the logic to the House class.
I would like to be able to access the house object's attributes from the individual classes (Person, Pet, etc.) but I am unsure of how to proceed or if it is even possible.
class Doors:
NOW CREATE THE NEW LIST IN HOUSE CLASS
def __init__(self,name):
self.name = name
NOW APPEND THIS OBJECT TO THE NEW LIST IN THE HOUSE CLASS
I can see two clear patterns on how to try to overcome this:
Create methods for getting the instances of a specific class
Simply put every instance inside a big list and add methods to get each "sublist":
class House:
def __init__(self,*argv):
self.things = [*argv]
#property
def pets(self):
return [thing for thing in self.things if isinstance(thing, Pet)]
#property
def people(self):
return [thing for thing in self.things if isinstance(thing, Person)]
This doesn't really solve your initial problem, but at least it's easier and cleaner to implement for new classes - if a list attribute does not exist, it's because you haven't implemented the method for it.
Use hasattr, setattr and getattr
Use these functions on the __init__ method to programatically check if each list exists, create them if needed and append each instance to the corresponding list:
class House:
def __init__(self,*argv):
for arg in argv:
name = arg.__class__.__name__.lower()
if not hasattr(self, name):
setattr(self, name, [])
getattr(self, name).append(arg)
I personally think this is worse, since your attributes will be named exactly like the class name (i.e. person, not people), and you can't clearly see which lists are initialized as attributes or not since it's done on the fly, but it should work for your described use case.
Whichever way you decide to go with, note that I personally feel like your design isn't very effective for dealing with this problem. I'd rather create empty lists for people, pets etc on the House's __init__ method and add specific methods like add_person, add_pet, add_group etc for appending objects to the House's list attributes. It may not seem like much, but this design:
a) clearly defines supported classes that can interact with the House class; and
b) lets you see more clearly exactly who is getting put into the House, since you need to explicitly call the method in order to do so.
I have restructured your code. Check it out :)
class House:
def __init__(self,*argv):
self.house_classes = {"people": Person, "pets": Pet}
self.house_objects = {}
for object in argv:
self.add_house_object(object)
def add_house_class(self, class_id, class):
self.house_classes["class_id"] = class
def add_house_object(self, object):
for class_id in self.house_classes:
if isinstance(object, self.house_classes[class_id]):
if class_id in self.house_objects:
self.house_objects["class_name"].append(object)
return
self.house_objects["class_id"] = [object]
class Person:
def __init__(self,name):
self.name = name
class Pet:
def __init__(self,name):
self.name = name
To add new classes (e.g Doors) to a house object (as i think you want)
my_house = House(house_objects...) #initialise a house object
class Doors: #your new class to add to house object
def __init__(self,name):
self.name = name
my_house.add_house_class(self, "doors", Doors) #added to house object
new_door = Door("my door") #door object
my_house.add_house_object(new_door)
I hope that helps :)
You can check if House has a doors list with getattr(House, 'doors', None) and create the list if it is not existing. This solution assumes that you intend to create the list as a class variable (I am assuming this, since you do NOT pass any House-instance reference do Doors when instantiating a Doors instance).
class Doors:
def __init__(self,name):
if getattr(House, 'doors', None) is None:
House.doors = []
self.name = name
House.doors.append(self)
BUT I strongly advise you to NOT USE THIS PATTERN. This looks like a good case for class inheritance, for example creating the class Doors with class Doors(House):.
Furthermore I've got the feeling that you should take a look at the definitions and meanings of class variables and instance variables.
Imho the best way to deal with this task would be to make Doors a class which inherits from House and to require an existing instance of house to be able to create a Doors instance (for example check with if isinstance(new_house, House):). Then the Doors __init__ method could create and/or append a list doors as instance variable to House.
This way you can create many different houses. Whereas when using a class variable for the doors list, each house would have all doors created in all houses.
Thus I recommend using this pattern:
class Doors(House):
def __init__(self, name, house):
if not isinstance(house, House):
raise ValueError('`house` is not an existing instance of the `House` class')
if getattr(house, 'doors', None) is None:
house.doors = []
else:
print('exi')
self.name = name
house.doors.append(self)
As jfaccioni pointed out: Inheritance is not mandatory here, but this kind of construct looks like you are going to need it in the long term for method-access etc.

How do class methods affect instance variables in Python?

I've read throughly many posts here on the difference between class methods and instance methods. I understand them conceptually, but now I'm trying to figure out the more subtle nuances. In the example below, if I call User.createUser('xyz'), where does userId get stored? does it go to (*) below, i.e. on the instance? Or would it be as if I inserted userId = None where I have the comment # placeholder and the userId passed in from User.createUser('xyz') then assigns the class variable userId with 'xyz'?
class User():
# placeholder
def __init__(self, userId):
self.userId = userId # (*)
#classmethod
def createUser(cls, userId):
if not isValid(userId): # isValid may or may not be part of the class
return False
else:
return cls(userId)
# ... other methods
Your classmethod createUser returns a new instance of the User object. The parameter passed to it is stored as an attribute on this new User instance.
The userId parameter is stored in the __init__ method.
The call to cls(userId) is equivalent to a call to User(userId)
In your case, neither. The return cls(userId) returns a new object*, and the userId is assigned to the self of the new object. So, it's neither a class nor an instance variable, it's an instance variable of another object.

Python, why do we have to inherit from 'list' class

I'm learning object oriented python and came across this issue where python is forcing me to inherit from built-in 'list' class to use append method.
class ContactSearch(list): #Why inherit from 'list'?
def search(self, name):
matching_contact = []
for contact in self:
if name in contact.name:
matching_contact.append(contact)
return matching_contact
Why can't I simply declare an empty list and append to it? For example it works find in the following code without inheriting from 'list class':
class Contact:
all_contacts = []
def __init__(self, name, email):
self.name = name
self.email = email
Contact.all_contacts.append(self)
In ContactSearch, matching_contact is temporary variable for result of searching. A instance of ContactSearch is list. So search method can use self in iterator. matching_contact is over when search method is done.
In Contact, all_contacts is class variable and all instance of Contact share this. Instance of Contact is NOT list. But you can access all contact using like Contact.all_contacts, and yes, it is list.
The difference is only where the data is stored. The one stores data in itself, and the another stores data in its variable.

Is it possible in Python to make a set of methods of a class dependent on input of constructor?

I am reading this Genshi Tutorial and see there the following example:
from formencode import Schema, validators
class LinkForm(Schema):
username = validators.UnicodeString(not_empty=True)
url = validators.URL(not_empty=True, add_http=True, check_exists=False)
title = validators.UnicodeString(not_empty=True)
As far as I understand this example, we create a new class that inherits Schema class and this class contain three methods: username, url, title. However, I am not sure about the last because before I only saw methods created with def.
Anyway, my question is not about that. I would like to know if it is possible to make the definition of the class dynamic. For example, sometimes I do not want url or title to be in the class. It seems to be doable (I just use if and assign a value to url only if-statement is satisfied.
But what if I do not know in advance what fields I would like to have in the form? For example, now I have username, url and title. But what if later I would like to have city or age. Can I do something like that:
from formencode import Schema, validators
class LinkForm(Schema):
__init__(self, fields):
for field in fields:
condition = fields[field]
field = validators.UnicodeString(condition)
I think it will not work. Is there a work around in this case?
Yes, you can add methods to an instance dynamically. No, you can't do what you want.
You can bind methods to the instance in the initializer. Unfortunately what you have there are descriptors and those must be bound to the class.
I would go the other way round—first define all form fields that might be used, and delete unneeded ones later.
Provided that you have:
from formencode import Schema, validators
class LinkForm(Schema):
username = validators.UnicodeString(not_empty=True)
url = validators.URL(not_empty=True, add_http=True, check_exists=False)
title = validators.UnicodeString(not_empty=True)
you could do either this:
def xy():
my_form = LinkForm()
del my_form.url
…
… or this:
def xy():
class CustomLinkForm(LinkForm):
pass
if …:
del CustomLinkForm.url
…
Disclaimer: I am not familiar with FormEncode, so it might depend on its inner workings which of these two versions actually works.
of course you can have a constructor with some arguments after self and these arguments will be the value for some members of your class if you have for instance
__init__(self, fields):
self.fields = []
for field in fields:
self.fields = self.fields + field
see this in Dive into Python
class FileInfo(UserDict):
"store file metadata"
def __init__(self, filename=None):
UserDict.__init__(self)
self["name"] = filename
Classes can (and should) have doc strings too, just like modules and
functions.
init is called immediately after an instance of the
class is created. It would be tempting but incorrect to call this the
constructor of the class. It's tempting, because it looks like a
constructor (by convention, init is the first method defined for
the class), acts like one (it's the first piece of code executed in a
newly created instance of the class), and even sounds like one (“init”
certainly suggests a constructor-ish nature). Incorrect, because the
object has already been constructed by the time init is called,
and you already have a valid reference to the new instance of the
class. But init is the closest thing you're going to get to a
constructor in Python, and it fills much the same role.
The first
argument of every class method, including init, is always a
reference to the current instance of the class. By convention, this
argument is always named self. In the init method, self refers to
the newly created object; in other class methods, it refers to the
instance whose method was called. Although you need to specify self
explicitly when defining the method, you do not specify it when
calling the method; Python will add it for you automatically.
init methods can take any number of arguments, and just like
functions, the arguments can be defined with default values, making
them optional to the caller. In this case, filename has a default
value of None, which is the Python null value.
Note that in the later example you learn how to deal with inherited class, calling __init()__ for this inherited class.
To answer your not-a-question about class or instance variables, see this
Variables defined in the class definition are class variables; they
are shared by all instances. To create instance variables, they can be
set in a method with self.name = value. Both class and instance
variables are accessible through the notation “self.name”, and an
instance variable hides a class variable with the same name when
accessed in this way. Class variables can be used as defaults for
instance variables, but using mutable values there can lead to
unexpected results. For new-style classes, descriptors can be used to
create instance variables with different implementation details.

Categories

Resources