how to combine singleton and factory design pattern with python? - python

Suppose there are several presses, each one published many books
I am going to handle many records like 'press name, book name'
I want create the class 'Press' by a unique name(string), but only different string can produce different instances of class 'Press'.
so, I need a class work like 'factory', create a new instance when a record contains a new press, but it also work as singleton if there are already exists which has the same names.

You could create a dict with the unique names as keys and Press objects as values. This doesn't need to be a global dict. You can wrap it in some class like that:
class Press:
def __init__(self, name):
self.name = name
self.books = []
class PressManager:
presses = {}
#classmethod
def get_press(cls, name):
if name not in cls.presses:
cls.presses[name] = Press(name)
return cls.presses[name]
example_press = PressManager.get_press("Test")
I implemented get_press() as a class method, because I think this is what you had in mind.

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.

Python inner class variable, namespace

class myouterclass(object):
def __init__(self,ID):
self.myouterclassID=myouterclassID
self.myinnerclass=self.MYINNERCLASS()
class MYINNERCLASS:
def __init__(self):
self.myinnerclassID=myinnerclassID
I am trying to create an inner class and create some variables including an ID. For simplicity I would like to also use ID to define different instances of my outer class as well.
I am lacking some understanding on what I am doing wrong.
What I am trying to accomplish is to use the same name variable "ID" to define an identification for the different instances of the outer class AND also to use the same name variable "ID" to keep track of the different instances of the inner class object "myinnerclass". I am planning to create more than one instances of the innerclass and I need to have different ID to keep track of them
Thanks
Maybe you wanted that?
class myouterclass(object):
def __init__(self, the_id):
self.myouterclassID = the_id
self.myinnerclass = self.MYINNERCLASS(the_id)
class MYINNERCLASS:
def __init__(self, inner_id):
self.myinnerclassID = inner_id
def __init__(self, inner_id):
self.myinnerclassID = inner_id

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.

Python class - attribute inheritance

I am trying to implement a project using class inheritance, where parent class is:
class radio:
def __init__(self, artist=None, track=None, genre=None):
self.artist = artist
self.track = track
self.genre = genre
then I create methods for each attribute (non working examples):
def seed_artist(self):
results = api.search(q=self.artist, type='artist')
return results
def seed_track(self):
results = api.search(q=self.track, type='track')
return results
def seed_genre(self):
results = api.search(q=self.genre, type='genre')
return results
the user is going to pick a seed above (only one), being it either artist, track, genre or mood, and that's why I initialize all arguments with None, leaving the argument value to be inserted at inheritance level.
the inherited class is:
class playlist(radio):
def __init__(self,user):
radio.__init__(self, artist, track, genre)
lets say user inputs at command line a track seed, and my script ends up starting with a global variable:
track = 'karma police'
this way, all other attributes (artist, genre) remain None.
when I create an instance, say:
jeff = playlist('Jeff Smith')
It will throw an error saying that genre and artist are not defined, and I would have to inherit only trackattribute, like so:
radio.__init__(self, track)
I know I could start the script with global variables defined:
track = None
genre = None
artist = None
and this would allow me to have:
radio.__init__(self, artist, track, genre)
but this seems to me rather redundant...
Is there a workaround this, with no need for initial values of global variables set to None, keeping all the code within class scope definitions?
Instead of using a separate global variable for each field, use a single dictionary for all fields. Then you can pass the dictionary as keyword arguments.
That is, instead of creating a global variable called artist, create a dictionary called radio_fields or something. When the user inputs their query, set a key on this dict:
radio_fields = {}
...
# when you get the user's command
radio_fields['track'] = 'Karma Police'
Now you can call radio.__init__(self, **radio_fields). This will pass as arguments whatever keys are in radio_fields. If there is only one, only that one will be passed, and the others will take their default values as you defined them when you defined radio.__init__.
There might be a better way to structure your overall program, but it's hard to say based on what you've shown here. For instance, it's unclear why playlist only accepts one user argument. If playlist accepted all the field arguments that radio accepted, it could pass them on to radio without any problem.
You don't have those variables to pass, so don't try and pass them. The parent class already has defaults for them, so just pass the one you need:
radio.__init__(self, artist=artist)

Categories

Resources