create python factory method - python

I have the following simple example:
class CatZoo(object):
def __init__(self):
raise NotImplemented
#classmethod
def make_zoo_cat(cls, name, age, gender, location):
cls._names = name
cls._ages = age
cls._genders = gender
cls._location = location
return cls
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
cls._names = names
cls._ages = ages
cls._genders = genders
cls._location = location
return cls
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
And I am trying to create an object of this class by using the following:
cat_zoo = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print "The name is {}".format(cat_zoo.names)
This is just an example, I am just trying to make my factory methods work (make_zoo_cat, make_zoo_cats). The first will be passed one name, age, gender and location where the second would be passed a list of names, ages and genders and one location. If I run this code, I get the following output:
The name is <property object at 0x7fe313b02838>
Thanks,

Remove the NotImplemented initializer and actually create instances of your class, instead of mutating the class itself:
class CatZoo(object):
def __init__(self, name, age, gender, location):
self._names = name
self._ages = age
self._genders = gender
self._location = location
#classmethod
def make_zoo_cat(cls, name, ages, genders, location):
return cls.mak_zoo_cats([name], age, gender, location)
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
return CatZoo(names, age, gender, location)
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
Note that there was no real difference other than the method name between make_zoo_cat and make_zoo_cats, the difference in argument names doesn't change the functionality here.
Instead, I presumed that ._names should always be a list and that make_zoo_cat (singular) should create a CatZoo with one cat name in it.
Just remember that Python is not Java; you really don't need all those property objects, not where you could just access the attribute directly.

You didn't create any object in your code.
In your make_zoo_cats you return cls, so you still have a class not an instance of this class.
This code will print the yes
if CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC') == CatZoo:
print 'yes'
You agree than you can't do that, since name its a property it will only exist if you have an instance of that class.
CatZoo.names
to be able to use the property you need on instance of that class
something like that (this will raise in your code):
cat = CatZoo()
cat.names # I can do this now
An other point in your make_zoo_cat you create Class variables, those variables are accessible from the class (no need to have an instance on that class) but are "common" to all.
c1 = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print c1._names
print c1._ages
print c1._genders
print c1._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
c2 = CatZoo.make_zoo_cat('other', 42, 'F', 'FR')
print c2._names
print c2._ages
print c2._genders
print c2._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
print c1._names
print c1._ages
print c1._genders
print c1._location
the result will be someting like that:
squeakers
12
M
KC
**********
squeakers
12
M
KC
**********
other
42
F
FR
**********
other
42
F
FR
**********
other
42
F
FR
The first two give me the same result, and the last three as well, this is because they are class variables and you always have the same class so modifying one of those variable will affect the other

Related

General setter for a class using strings

Let's say I have a class called "Employee" which was a bunch of different attributes. I can create a general getter which would basically get every attribute based on a string of its name like this but I don't know how to create a setter of the sort so I wouldn't have to do something like employee1.age = 22 every time. And creating multiple setter for every attribute would be pretty messy.
class Employee:
def __init__(self, name, age):
self.name = name
self.age = age
self.address = "Somewhere"
self.job = None
def getter(self, name):
return getattr(self, name, None)
def setter(self, name, amount):
pass
You can use setattr() and wrap it in your setter method like this:
def setter(self, name, amount):
return setattr(self, name, amount)
So you'd call it like this
e = Employee("Albert", 169)
e.setter("age", 16)
and when you check now for e.age you will see
>>> e.age
16
Thanks to Joe Carboni's answer, I managed to create a function I'd like to call the ultimate_getter(). I'll put it here for you all to use.
def ultimate_getter(obj, name, limit = 12):
if name.count(".") == 0:
return getattr(self, name, None)
else:
count = 0
previous = None
current = None
previous = obj
while name.count(".") > 0 and count < limit:
index = name.find(".")
previous = getattr(previous, name[0:index])
name = name[index + 1:]
count += 1
if name.count(".") == 0:
current = getattr(previous, name)
return current
It's basically a more advanced version of setattr() which can loop through the text so you wouldn't have to use setattr() a bunch of times when wanting to use sort of a nested attribute/method.

How to assign from input to object from a class

I have multiple classes and I have instances from each class e.g: Student class. every instance (a student) has their own courses. Now when a user signs in (by input) I want to print their list of courses. Or even just their age to show that I have the correct object.
Is there a better way than eval() to get an object from class based on input
like the following example:
class Student:
def __init__(self, name, age):
self._name = name
self._age = age
blablue = Student('bla blue', '23')
name = input('enter your name')
name = name.split(' ')
stundent = eval(name[0] + name[1])
print(student)
print(student.age)
output:
enter your name: bla blue
<__main__.Foo object at 0x000001B2978C73C8>
23
I assume this is for educational purpose (production code would use a SQL database and some ORM):
try:
# python 2.x
input = raw_input
except NameError:
# python 3.x
pass
class AlreadyExists(ValueError):
pass
class DoesNotExist(LookupError):
pass
class FooCollection(object):
def __init__(self):
self._foos = {}
def add(self, foo):
if foo.name in self._foos:
raise AlreadyExists("Foo with name '{}' already exists".format(foo.name))
self.update(foo)
def update(self, foo):
self._foos[foo.name] = foo
def get(self, name):
try:
return self._foos[name]
except KeyError:
raise DoesNotExist("no Foo named '{}'".format(name))
class Foo(object):
def __init__(self, name, age):
self._name = name
self._age = age
# we at least need to be able to read the name
#property
def name(self):
return self._name
def __repr__(self):
return "Foo({}, {})".format(self._name, self._age)
def main():
foos = FooCollection()
blablue = Foo('bla blue', '23')
foos.add(blablue)
name = input('enter your name: ').strip()
try:
print("found {}".format(foos.get(name)))
except DoesNotExist as e:
print(e)
if ___name__ == "__main__":
main()
The principle here is to have a storage for your instances. I chose a dict for fast lookup with the Foo.name as key, in real life you'd probably want an opaque unique identifier for each instance and multiple indexes (i.e. one by id, one by name etc) - but actually in real life you would use a SQL database that already provide all those features in a much more optimized way ;-)
Also, I wrapped the dict in a dedicated class with its own interface. This allows to decouple the interface from the implementation (if you later decide you want more indexes than just name for example), and encapsulate domain logic too (i.e. checking you don't accidentally overwrite an existing Foo).

Dictionary keys not present though they are being added

This is my code :
class member:
def __init__(self, name):
self.name = name
def get_name(self, name):
self.name = name
def __str__(self):
return self.name
class create_graph:
def __init__(self):
self.some_dict = dict()
def add(self, name):
if name is None:
raise TypeError
print(name not in self.some_dict)
if name not in self.some_dict:
self.some_dict[name] = []
else:
print(str(name) + "is already present")
def link(self, p1, p2):
if p1 in self.some_dict:
self.some_dict[p1].append(p2)
else:
self.some_dict[p1] = [p2]
some_graph = create_graph()
list_person = ['abc', 'xyz', 'mno', 'pqr']
for person in list_person:
some_graph.add(member(person))
print(len(some_graph.some_dict))
for i in range(len(list_person)-1):
some_graph.link(i,i+1)
print(len(some_graph.some_dict))
I am not able to find the error in this code.
When the add function is called, I get the True message indicating it is added. The first print statement prints that the number of keys are 4 but after adding the links, it says the keys are 7.
I want to have just 4 even after adding the link.
Thanks for the help !
Print out the dictionary in question.
print(some_graph.some_dict)
produces
{<__main__.member object at 0x7fe8326abe80>: [], <__main__.member object at 0x7fe8326abeb8>: [], <__main__.member object at 0x7fe8326abe48>: [], <__main__.member object at 0x7fe8326abef0>: []}
The keys of this dictionary are instances of the class member, not the strings in the list list_person.
I you did:
persons_in_graph_dict = {k.name for k in some_graph.some_dict}
for person in list_person:
print(person)
print(person in persons_in_graph_dict)
print()
You would get:
abc
True
xyz
True
mno
True
pqr
True
You can fix the problem by adding a __contains__() method to your CreateGraph class that expects a string argument called name. How to do this and then use it shown in the code below.
Note: I have changed all your class names to the CapitalizedWords-style to conform to the PEP8 coding guidelines (in its Naming Conventions section).
class Member:
def __init__(self, name):
self.name = name
def get_name(self, name):
self.name = name
def __str__(self):
return self.name
class CreateGraph:
def __init__(self):
self.some_dict = dict()
def add(self, name):
if name is None:
raise TypeError
if name not in self.some_dict:
self.some_dict[name] = Member(name)
else:
print("{} is already present".format(name))
def __contains__(self, name): # <-- METHOD ADDED.
return name in self.some_dict
some_graph = CreateGraph()
list_person = ['abc', 'xyz', 'mno', 'pqr']
for person in list_person:
some_graph.add(person)
print("checking these names in list_person:", list_person)
for person in list_person:
if person in some_graph:
print("Present")
else:
print("Not present")
Here's the output:
checking these names in list_person: ['abc', 'xyz', 'mno', 'pqr']
Present
Present
Present
Present
You are storing instances as key. call name()to get the name
try testing like below
for i in some_graph.some_dict:
print ((i.name) in list_person)

python: passing variable name to a class

I have a python class defined in a module1.py file:
class MBVar():
def __init__(self, var_type, expression):
self.var_type = var_type
self.expression = expression
... ecc ...
I would like to be able to write in a main *.py file:
from module1 import MBVar
X = MBVar('integer', 6)
and add to my MBVar class:
self.name = ???
in such a way that: self.name = 'X'.
Is it possible to do this??
Thanks
So I Assume you want to pass variable name and value as parameter and assign it to an object, to do that we don't need the type of the variable since python uses duck typing we just have to add the string representation of the variable name in the inbuilt dictionary __dict__ as key and the integer as value.
class MBVar():
def __init__(self, var_name, expression):
self.__dict__[var_name] = expression
def add_later(self, var_name, expression):
self.__dict__[var_name] = expression
def get_name(self):
return self.name
X = MBVar('name', 6)
print X.get_name() # prints 6
X.add_later('secint',4);
print X.secint #prints 4
X.__dict__['thirdint'] = 7
print X.thirdint #prints 7
I have a solution but i don't think that this is a very good coding practice. Moreover, it is a 2 steps process: it can't be done inside the __init__ method because till the end of this method, the object has not been yet associated to a variable.
class Foo:
def __init__(self):
self.__name = ""
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
if __name__ == "__main__":
a = Foo()
b = Foo()
c = Foo()
dict_v = locals()
v = ""
# this line initialize the variable of name "v" because of its entry in the locals() dict
#-> prevent "RuntimeError: dictionary changed size during iteration "
for v in dict_v.keys():
if isinstance(dict_v[v], Foo):
# the process only happens for the objects of a specific class
dict_v[v].set_name(v)
#proof
print(a.get_name())
print(b.get_name())
print(c.get_name())

I want to add an object to a list from a different file

I have 3 files. The first is a Runners file which is abstract. The other two are CharityRunner and ProfessionalRunners. In these I can create runners.
Runners:
class Runner(object):
def __init__ (self, runnerid, name):
self._runnerid = runnerid
self._name = name
#property
def runnerid(self):
return self._runnerid
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
def get_fee(self, basicfee, moneyraised):
raise NotImplementedError("AbstractMethod")
CharityRunners:
from Runner import *
class CharityRunner(Runner):
def __init__ (self, runnerid, name, charityname):
super().__init__(runnerid, name)
self._charityname = charityname
#property
def charityname(self):
return self._charityname
#charityname.setter
def charityname(self, charityname):
self._charityname = charityname
def get_fee(self, basicfee, moneyraised):
if moneyraised >= 100:
basicfee = basicfee * 0.25
elif moneyraised >= 50 and moneyraised < 100:
basicfee = basicfee * 0.5
else:
basicfee = basicfee
return basicfee
ProfessionalRunners:
from Runner import *
class ProfessionalRunner(Runner):
def __init__ (self, runnerid, name, sponsor):
super().__init__(runnerid, name)
self._sponsor = sponsor
#property
def sponsor(self):
return self._sponsor
#sponsor.setter
def sponsor(self, sponsor):
self._sponsor = sponsor
def get_fee(self, basicfee):
basicfee = basicfee * 2
return basicfee
Now I have also created a club object that has a club id and club name. There is also a list called self._runners = []. I'm trying to get a add function that will add the runners created in the list. But it must make sure that the runner is not already in the list.
The object printing method should be in the format of:
Club: <club id> <club name>
Runner: <runner id 1> <runner name 1>
Runner: <runner id 2> <runner name 2>
At the moment I only have this for the club object:
from Runner import *
class Club (object):
def __init__(self, clubid, name):
self._clubid = clubid
self._name = name
self._runners = []
#property
def clubid(self):
return self._clubid
#property
def name(self):
return self._name
#name.setter
def name(self, name):
self._name = name
def add_runner(self):
self._runner.append(Runner)
I'm guessing the part you're missing is:
im trying to get a add function that will add the runners created in the list.
Your existing code does this:
def add_runner(self):
self._runner.append(Runner)
This has multiple problems.
First, you're trying to modify self._runner, which doesn't exist, instead of self._runners.
Next, you're appending the Runner class, when you almost certainly want an instance of it, not the class itself.
In fact, you almost certainly want an instance of one of its subclasses.
And I'm willing to bet you want a specific instance, that someone will pass to the add_runner function, not just some random instance.
So, what you want is probably:
def add_runner(self, runner):
self._runners.append(runner)
And now that you posted the UML diagram, it says that explicitly: add_runner(Runner: runner). In Python, you write that as:
def add_runner(self, runner):
Or, if you really want:
def add_runner(self, runner: Runner):
… but that will probably mislead you into thinking that this is a Java-style definition that requires an instance of Runner or some subclass thereof and checks it statically, and that it can be overloaded with different parameter types, etc., none of which is true.
To use it, just do this:
doe_club = Club(42, "Doe Family Club")
john_doe = CharityRunner(23, "John Doe", "Toys for John Doe")
doe_club.add_runner(john_doe)
Next:
But it must make sure that the runner is not already in the list.
You can translate that almost directly from English to Python:
def add_runner(self, runner):
if runner not in self._runners:
self._runners.append(runner)
However, this does a linear search through the list for each new runner. If you used an appropriate data structure, like a set, this wouldn't be a problem. You could use the same code (but with add instead of append)… but you don't even need to do the checking with a set, because it already takes care of duplicates for you. So, if you set self._runners = {}, you just need:
def add_runner(self, runner):
self._runners.add(runner)

Categories

Resources