Say for example I have some classes which all inherent from the same parent class and have the same parameters. A common example;
class Pet():
...
class Cat(Pet):
__init__(self,name,colour):
Pet.__init__(self,name,colour)
....
class Cactus(Pet):
__init__(self,name,colour):
Pet.__init__(self,name,colour)
....
And then say I want to instantite some type of pet later in the program based on user input. What I would think of doing at first is;
if(pet_type == 'Cat'):
animal = Cat(name,colour)
elif(pet_type == 'Cactus'):
animal = Cactus(name,colour)
etc...
But is there a better way that does not require an if? For example if the program was developed to include over 1000 animals which all descend from pet then it would not be feasilbe.
Create a dictionary of allowable classes:
classes = {
'Cat': Cat,
'Cactus': Cactus,
}
try:
cls = classes[pet_type]
except KeyError:
# handle invalid pet_type here
else:
animal = cls(name, colour)
Depending on your response to a KeyError, you may want to use a finally clause instead of an else clause, or simply use the get method of the dict, e.g.
animal = classes.get(pet_type, Pet)(name, colour)
What you look for is known as factory pattern. There is a great many ways of achieving this, ranging from explicit if-cascades as you show them to meta-class magic.
A rather straightforward way is a class-decorator:
PET_CLASSES = {}
def pet_class(cls):
PET_CLASSES[cls.__name__.lower()] = cls
def create_pet(name):
return PET_CLASSES[name.lower()]()
#pet_class
class Cat:
pass
#pet_class
class Dog:
pass
print(create_pet("dog"))
You can get the class by its string name with getattr()
my_class = getattr(module, "class_name")
and then
my_object = my_class(...)
The module object can be accessed by module = __import__("module_name")
Related
I have a situation where i can make an object as a class:
class BenjaminFranklin(Cat):
def __init__(self):
super().__init__()
self.ishungry = False
self.legs = 4
self.name_eng = 'Benjamin Franklin'
or an instance of a class:
benjamin_franklin = Cat()
benjamin_franklin.ishungry = False
benjamin_franklin.legs = 4
benjamin_franklin.name_eng = 'Benjamin Franklin'
The 'Correct' options seems to be using an instance of a class because there is no group of cats with the name "Benjamin Franklin"; there is only one and only cat. But it is much less readable, especially when using long names.
Tell me please:
"You can use class in this case for better readability, because..."
"The only correct option is to use an instance, because..."
Something else
Thanks
I assume, Benjamin Franklin is a single Cat. Therefore it should be an instance of the class cat.
One way you could do this would be:
class Cat:
def __init__(self, name_eng, is_hungry=False, legs=4):
self.ishungry = is_hungry
self.legs = legs
self.name_eng = name_eng
And then initialize your instance by:
benjamin_franklin = Cat("Benjamin Franklin") # is_hungry and legs do not to be passed, as they have the values you asked for defined as default
Rather than building benjamin_franklin the way you are, I would change your Cat constructor so it can take the arguments necessary to properly build out the object. Then, you could do something like benjamin_franklin = Cat('Benjamin Franklin') which would be much more readable. Such a constructor would look like:
def __init__(self, name_eng, is_hungry = False, legs = 4):
self.ishungry = is_hungry
self.legs = legs
self.name_eng = name_eng
However, if that is not an option, you could embed your code into a factory method:
def BenjaminFranklin():
benjamin_franklin = Cat()
benjamin_franklin.ishungry = False
benjamin_franklin.legs = 4
benjamin_franklin.name_eng = 'Benjamin Franklin'
return benjamin_franklin
If you have an object with many attributes on it, you could also try encapsulating the object into sub-objects and passing those in on your constructor. For instance, suppose your Cat had an identity with a name_eng and name_cha:
class Identity:
def __init__(self, name_eng, name_cha):
self.name_eng = name_eng
self.name_cha = name_cha
class Cat:
def __init__(self, id, ishungry = False, legs = 4):
self.identity = id
self.is_hungry = ishungry
self.legs = legs
which you could initialize like this:
benjamin_franklin = Cat(Identity('Benjamin Franklin', '猫'))
this is somewhat more verbose but it means that you can spread out your construction over several objects (and maybe create some constant objects that are the same over your most instantiations of your class).
Neither option feels right. The name BenjaminFranklin suggests that this is an instance of something. However, the question is why no_legs, is_hungry etc are specific to Benjamin Franklin, and not to all cats. It seems that those attributes should me moved up to the parent Cat class, and Benjamin Franklin should be an instance of it.
Moreover, if only benjamin franklin has a num_legs variable, what does he do with it? There is no added function that uses the new fields. In that sense, this seems more like a dict, or a namedtuple than an instance of a class.
However, it's not possible to tell what the best way is without seeing the functionality of the Cat class, and how you use the extra attributes of benjamin franklin.
I understand how to create an unsophisticated class based on examples culled from the Web but I hit a wall when trying to access the members on it, to wit:
Let's say this is my class:
class Fruit(object):
def __init__(self, name, color, flavor):
self.name = name
self.color = color
self.flavor = flavor
def description(self):
print('I am a %s %s and my taste is %s and I am %s' % self.color, self.name, self.flavor))
To create and object I use:
lemon = Fruit('lemon', 'yellow', 'sour')
and to create a new attribute for lemon I use:
lemon.peel = 'easy'
I would like to define a method inside (or outside) of the class that will be called printall that will iterate though all the existing members of the class and print all of them with their attributes even if the attributes are variable (more than de ones initially defined). I think this is called "overloading"
but I am not sure of the proper terminology.
The term you are looking for is type introspection. Overloading is something entirely different, where you provide different implementations of a method.
You can access all instance attributes with the var() function; it returns a dictionary you can then iterate over to print your variables:
def printall(self):
for name, value in vars(self).items():
print('self.{} = {!r}'.format(name, value))
And if you're not sure then you can use the below loop to find details of all members
import gc
#garbage collector should do the trick
#all the other code
for obj in gc.get_objects():
if isinstance(obj, Fruit):
print "object name :",obj.name
printall(obj)
perhaps this is what you're looking for, though the printall method is not a part of the class, it is able to access the class when you pass an object to it and the following code should print the attribute name and value of the object lemon in Fruits class.
def printall(lemon):
for a in dir(lemon):
if not a.startswith('__') :
print a,":",getattr(lemon, a)
#rest of the code
lemon = Fruit('lemon', 'yellow', 'sour')
lemon.peel = 'easy'
printall(lemon)
This is going to look like class inheritance but I think it is not and there must be an easy way of doing the following. Take a look at this simple code:
class Land:
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
class Farm:
def __init__(self):
print "a new farm"
self.animals = []
def addanimal(self,name):
self.animals.append(Animal(name))
class Animal:
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
USA = Land()
USA.addfarm()
USA.farms[0].addanimal('George')
USA.farms[0].addanimal('Martin')
USA.addfarm()
USA.farms[1].addanimal('Polly')
USA.farms[1].addanimal('Ralph')
Is there an easy way of getting all animals without doing?:
for eachfarm in USA.farms:
for each in eachfarm.animals:
print each.name
I am asking this because if for instance the user wants to add a new George to farm 0 I would like to quickly be able to say that name is taken. I would also be able to quickly run a function that gives me all animals in the land or all farms. Should I be writing functions for all that or Python got its own?
I am also interested on knowing if my nested class structure is not correct and could end up causing issues.
For instance, lets say I have a function that given an animal tells me the perfect food mix for it. I would like to be able to run that function on all my animals and write back into their object. If they are nested I am afraid the function may get confused!
Thanks!
Using nested classes like this is perfectly fine and is not about inheritance at all. However you may want to choose slightly different data structures.
You say that in each farm you only want to be able to have one animal of each name. However, you use a list to store them. A list allows you to have as many animals of the same name inside as you want to, so you'd need to perform that check yourself when you add another one.
However, you could use a dict. A dict is an unordered data structure that links a key to a value. In your case you could use the name of the animal as the key and the Animal object for the value. Checking if a key exists can be done in constant time (as compared to linear time with a loop), since internally a dict is a hash table.
Example code might look like this:
class Land:
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
class Farm:
def __init__(self):
print "a new farm"
self.animals = {}
def addanimal(self,name):
if not name in self.animals:
self.animals[name] = Animal(name)
return True
return False
class Animal:
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
USA = Land()
USA.addfarm()
USA.farms[0].addanimal('George')
USA.farms[0].addanimal('Martin')
USA.addfarm()
USA.farms[1].addanimal('Polly')
USA.farms[1].addanimal('Ralph')
This would prevent you from adding two animals of the same name to one farm, returning a boolean depending on whether the animal could be added to the farm or not.
To get all animals on all farms you will still need nested loops. But enabling iteration over the objects itself can be much nicer. If you do the following:
class Land(object):
def __init__(self):
print "a new Land"
self.farms = []
def addfarm(self):
self.farms.append(Farm())
def __iter__(self):
for farm in self.farms:
yield farm
class Farm(object):
def __init__(self):
print "a new farm"
self.animals = {}
def addanimal(self,name):
if not name in self.animals:
self.animals[name] = Animal(name)
return True
return False
def __iter__(self):
for name, animal in self.animals.iteritems():
yield animal
class Animal(object):
def __init__(self, name):
print "hi, I am %s" % name
self.name = name
You could then:
for farm in USA:
for animal in farm:
pass #do something here
According to your comment, you also want to be able to do land.getAllAnimals() and farm.getAllAnimals(). The latter is easily accomplished because farm works as an iterator over all animals. If you want a list you can simply call list(farm).
For land.getAllAnimals() there are two nice options. Both are to be added to the previous declaration.
Option 1
class Land(object):
def getAllAnimals(self):
for farm in self:
for animal in farm:
yield animal
Option 2
from itertools import chain
class Land(object):
def getAllAnimals(self):
return chain(*self)
Both will return iterators over all animals. To cast these into a list, simply call list on them. The former is easier to understand, but the latter is more concise and, in my opinion, nicer.
There is nothing wrong with nesting your loops, and it's just the way to do it. You might want to look into a more declarative approach, or you might want to store your data differently, but that's all just implementation detail and primarily a matter of taste.
Is it possible to create a class that, when instantiated, would never throw AttributeError, but instead would call own method to define the attribute?
Currently, I can do:
class O: pass
o = O()
o.car = O()
o.car.type = "family"
o.car.color = "red"
o.car.trail = O()
o.car.trail.color = "yellow"
o.house = O()
o.house.size = "small"
o.house.number = 87
I'm trying to create an arbitrary hierarchy out of a set of generic objects. Only purpose is to hold arbitrary data i.e. no need to define class Car: or class House:.
Now I wonder if it's possible to define a "magical" method that would be called each time an attribute is not found:
class O:
def __magically_define_attribute__(self, name):
setattr(self, name, O())
so I could omit all the O()s and simply do:
o = O()
o.car.type = "family"
o.car.color = "red"
o.car.trail.color = "yellow"
o.house.size = "small"
o.house.number = 87
So is it possible? And if not, is there a specific reason for that?
(Yes, it's about data, so I know I should probably use hierarchy of dictionaries here, but I still wonder if it's possible since compared to dict syntax, dot syntax is obviously way easier to read and write.)
Do you mean something like this?
class Obj(object):
def __getattr__(self,x):
setattr(self,x,Obj())
return getattr(self,x)
o = Obj()
o.car.type = "family"
o.car.color = "red"
print o.car.color
the __getattr__ method looks like it should fufill your needs. It is invoked when
an attribute lookup has not found the attribute in the usual places
(i.e. it is not an instance attribute nor is it found in the class
tree for self). name is the attribute name
Suppose you have two classes X & Y. You want to decorate those classes by adding attributes to the class to produce new classes X1 and Y1.
For example:
class X1(X):
new_attribute = 'something'
class Y1(Y):
new_attribute = 'something'
new_attribute will always be the same for both X1 and Y1. X & Y are not related in any meaningful way, except that multiple inheritance is not possible. There are a set of other attributes as well, but this is degenerate to illustrate.
I feel like I'm overcomplicating this, but I had thought to use a decorator, somewhat likeso:
def _xywrap(cls):
class _xy(cls):
new_attribute = 'something'
return _xy
#_xywrap(X)
class X1():
pass
#_xywrap(Y)
class Y1():
pass
It feels like I'm missing a fairly common pattern, and I'd be much obliged for thoughts, input and feedback.
Thank you for reading.
Brian
EDIT: Example:
Here is a relevant extract that may illuminate. The common classes are as follows:
from google.appengine.ext import db
# I'm including PermittedUserProperty because it may have pertinent side-effects
# (albeit unlikely), which is documented here: [How can you limit access to a
# GAE instance to the current user][1].
class _AccessBase:
users_permitted = PermittedUserProperty()
owner = db.ReferenceProperty(User)
class AccessModel(db.Model, _AccessBase):
pass
class AccessExpando(db.Expando, _AccessBase):
pass
# the order of _AccessBase/db.* doesn't seem to resolve the issue
class AccessPolyModel(_AccessBase, polymodel.PolyModel):
pass
Here's a sub-document:
class Thing(AccessExpando):
it = db.StringProperty()
Sometimes Thing will have the following properties:
Thing { it: ... }
And other times:
Thing { it: ..., users_permitted:..., owner:... }
I've been unable to figure out why Thing would sometimes have its _AccessParent properties, and other times not.
Use 3-arguments type:
def makeSomeNicelyDecoratedSubclass(someclass):
return type('MyNiceName', (someclass,), {'new_attribute':'something'})
This is indeed, as you surmised, a reasonably popular idiom.
Edit: in the general case if someclass has a custom metaclass you may need to extract and use it (with a 1-argument type) in lieu of type itself, to preserve it (this may be the case for your Django and App Engine models):
def makeSomeNicelyDecoratedSubclass(someclass):
mcl = type(someclass)
return mcl('MyNiceName', (someclass,), {'new_attribute':'something'})
This also works where the simpler version above does (since in simple cases w/no custom metaclasses type(someclass) is type).
Responding to your comments on voyager's answer:
from google.appengine.ext import db
class Mixin(object):
"""Mix in attributes shared by different types of models."""
foo = 1
bar = 2
baz = 3
class Person(db.Model, Mixin):
name = db.StringProperty()
class Dinosaur(db.polymodel.PolyModel, Mixin):
height = db.IntegerProperty()
p = Person(name='Buck Armstrong, Dinosaur Hunter')
d = Dinosaur(height=5000)
print p.name, p.foo, p.bar, p.baz
print d.height, d.foo, d.bar, d.baz
Running that results in
Buck Armstrong, Dinosaur Hunter 1 2 3
5000 1 2 3
Is that not what you had in mind?
Why can't you use multiple inheritance?
class Origin:
new_attribute = 'something'
class X:
pass
class Y:
pass
class X1(Origin, X):
pass
class Y1(Origin, Y):
pass