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).
Related
I am trying to use property decorator to validate python object.
Following is my class
'''Contains all model classes. Each class corresponds to a database table'''
from validation import company_legal_type, max_len
class Company():
'''A class to represent a company and its basic information'''
def __init__(self, ref, name, currency, legal_type, business):
self._ref = int(ref)
self._name = name
self._currency = str(currency) # i.e. The functional currency
self._legal_type = str(legal_type)
self._business = str(business)
def __str__(self):
return f"Company object for '{self._name}'"
# Validate object attributes
#property # Prevents change to ref after company object has been created
def ref(self):
return self._ref
#property # The following two functions permit changes but set validation checks for name
def name(self):
return self._name
#name.setter
def name(self, value):
if type(value) != str:
raise TypeError("Company name must be a string.")
if len(value) > max_len['company_name']:
raise ValueError(f"Company name must not be longer than {str(max_len['company_name'])} characters.")
Following is validation.py
# data length validation
max_len = {
'company_name': 200,
'company_business': 200,
}
And finally here is how I am using the class:
# Import custom modules
from models import Company, Currency
company = Company(23, 'ABC Limited', 'PKR', 'pvt_ltd', 'manufacturing')
company.name = 'ABCD Limited'
print(company.name)
This prints 'ABC Limited' instead of 'ABCD Limited'.
If I break a validation condition like use an integer instead of a string when updating company.name, it correctly results in an error. But if I break it when creating the object, no error is raised.
What am I doing wrong?
The problem is your setter doesn't set anything. It merely raises errors on bad input. So you have to actually set something, or else how do you expect it to modify self._name? So:
#name.setter
def name(self, value):
if type(value) != str:
raise TypeError("Company name must be a string.")
if len(value) > max_len['company_name']:
raise ValueError(f"Company name must not be longer than {str(max_len['company_name'])} characters.")
self._name = value
If I break a validation condition like use an integer instead of a string when updating company.name, it correctly results in an error. But if I break it when creating the object, no error is raised.
Because you don't use the property in __init__. Generally if you have validation property setters, you want to use those in __init__. So your __init__ should be something like:
def __init__(self, ref, name, currency, legal_type, business):
self._ref = int(ref)
self.name = name
self._currency = str(currency) # i.e. The functional currency
self._legal_type = str(legal_type)
self._business = str(business)
I'am using more class based programs, however in some cases it's not handy to provide all self.paramets into a class.
In those cases I want to use a regular input into a function in a class. I figured out a way to achieve both inputs, let me show this in following script:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(a):
if (type(a) == str):
name = a
else:
name = a.name
print("Hello my name is " + name)
p1 = Person("John", 36)
p1.myfunc()
print("---------------------")
Person.myfunc("Harry")
Output:
Hello my name is John
---------------------
Hello my name is Harry
First, the name is initialized by the classes self.params.
Second, the name is provided in the method within the class as a string.
So a type check is necessary.
However I don't think this is a clean approach, because when I have >30 methods I need to implement these type checks again, including upcoming type-error results.
Does anyone know a better approach?
The simplest solution is to implement a __str__ method for your class. This method will be called whenever something tries to convert an instance of the class to a string.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return self.name
p = Person('Jane', 25)
print('Hello', p)
'Hello Jane'
I have written some python code:
class key(object):
def __init__(self,name):
object.__init__(self,age)
this.name = name
this.age = age
def somefunction(self):
print "yay the name is %d" % self.name
baby = key('radan',20)
baby.somefunction()
When I create an instance of key with baby = key('radan',20), I got a TypeError. I don't know why I am getting this error. Is it because of object.__init__(self,age)?
If yes, please help me in explaining why we use object.__init__(self,age) and what the purpose of that is and help me solve this code.
Some pointers:
class Key(object): # in Python, classes are usually named with a starting capital
def __init__(self, name, age): # name all your arguments beside self
self.name = name # use self.name, not this.name
self.age = age
def somefunction(self):
print "yay the name is %s" % self.name
baby = Key('radan',20)
baby.somefunction()
# output: yay the name is radan
Actually, you can can name the self instance parameter whatever you like in Python (even this), but it makes the code harder to read for other people, so just use self.
You don't have to use object.__init__(self, name, age) here. If you remove that line and implement the changes above, your code will work just fine.
Your code contains several errors:
class key(object):
def __init__(self, name, age): # where's your age?
self.name = name # no need to call object.__init__
self.age = age # there is no such thing called "this", use self
def somefunction(self):
print "yay the name is %s" % self.name # use %s as the comment says
baby = key('radan', 20)
baby.somefunction()
output:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan
When you do baby = key('radar', 20) you are actually passing three arguments: the instance, the name and the age. However your initialiser is defined to take exactly two arguments so you get a TypeError.
self is the argument implicitly passed when referring to an instance of an object.
For your __init__ function, I would just do:
def __init__(self, name, age):
self.name = name
self.age = age
So we can assign the arguments passed as attributes to the current instance, conventionally called self.
It makes no sense here to call object.__init__ at all, just remove that line.
Apart from that, everything works fine (except use %s instead of %d).
Testing:
>>> baby = key('radan', 20)
>>> baby.somefunction()
yay the name is radan
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
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)