Reputation:
I've been struggling for a couple of days now with the following...
I'm trying to find a way to instantiate a number of objects which I can name via a raw_input call, and then, when I need to, look at its attributes via the 'print VARIABLE NAME' command in conjunction with the str() method.
So, to give an example. Let's say I want to create a zoo of 10 animals...
class Zoo(object):
def __init__(self, species, legs, stomachs):
self.species = species
self.legs = legs
self.stomachs = stomachs
for i in range(9):
species = raw_input("Enter species name: ")
legs = input("How many legs does this species have? ")
stomachs = input("...and how many stomachs? ")
species = Zoo(species, legs, stomachs)
The idea is that the 'species' variable (first line of the for loop) e.g species = Bear becomes the object 'Bear' (last line of loop), which in conjunction with a str() method and the 'print Bear' command would give me the bears attributes.
Like I say, I've struggled for a while with this but despite looking at other posts on similar themes still can't figure out a way. Some say use dictionaries, others say use setattr() but I can't see how this would work in my example.
Upvotes: 0
Views: 2479
Reputation: 9004
>>> class Bear():
... pass
...
>>> class Dog():
... pass
...
>>>
>>> types = {'bear': Bear, 'dog': Dog}
>>>
>>> types['dog']()
<__main__.Dog instance at 0x75c10>
Upvotes: 0
Reputation: 881595
It's really, truly, SERIOUSLY a bad idea to create barenamed variables on the fly -- I earnestly implore you to give up the requirement to be able to just print FOOBAR
for a FOOBAR barename that never existed in the code, much as I'd implore a fellow human being who's keen to commit suicide to give up their crazy desires and give life a chance. Use dictionaries, use a function that takes 'FOOBAR'
as the argument and looks it up, etc.
But if my fellow human being is adamant about wanting to put an end to their days, I might switch to suggestions about how to do that with the least collateral damage to themselves and others. The equivalent here would be...:
class Zoo(object):
def __init__(self, species, legs, stomachs):
self.species = species
self.legs = legs
self.stomachs = stomachs
import __builtin__
setattr(__builtin__, species, self)
By explicitly using the __builtin__
module, you ensure you can "print thespeciesname" from ANY module -- not just the one defining Zoo
, nor just the one instantiating it.
It's STILL a terrible idea, but this is the least-horror way to implement it.
Upvotes: 1
Reputation: 107608
class Zoo(object):
def __init__(self, name):
self.name = name
self.animals = []
def __str__(self):
return ("The %s Zoo houses:\n" % self.name) + "\n".join(str(an) for an in self.animals)
class Animal( object ):
species = None
def __init__(self, legs, stomach):
self.legs = legs
self.stomach = stomach
def __str__(self):
return "A %s with %d legs and %d stomachs" % ( self.species, self.legs, self.stomach )
class Bear( Animal ):
species = "Bear"
class Karp( Animal ):
species = "Karp"
## this is the point ... you can look up Classes by their names here
## if you wonder show to automate the generation of this dict ... don't.
## ( or learn a lot Python, then add a metaclass to Animal ;-p )
species = dict( bear = Bear,
karp = Karp )
zoo = Zoo( "Strange" )
while len(zoo.animals) < 9:
name = raw_input("Enter species name: ").lower()
if name in species:
legs = input("How many legs does this species have? ")
stomachs = input("...and how many stomachs? ")
zoo.animals.append( species[name]( legs, stomachs ) )
else:
print "I have no idea what a", name, "is."
print "Try again"
print zoo
Upvotes: 0
Reputation: 12829
If you just want to introduce new named variables in a module namespace, then setattr
may well be the easiest way to go:
import sys
class Species:
def __init__(self, name, legs, stomachs):
self.name = name
self.legs = legs
self.stomachs = stomachs
def create_species():
name = raw_input('Enter species name: ')
legs = input('How many legs? ')
stomachs = input('How many stomachs? ')
species = Species(name, legs, stomachs)
setattr(sys.modules[Species.__module__], name, species)
if __name__ == '__main__':
for i in range(5):
create_species()
If you save this code to a file named zoo.py
, and then import it from another module, you could use it as follows:
import zoo
zoo.create_species() # => enter "Bear" as species name when prompted
animal = zoo.Bear # <= this object will be an instance of the Species class
Generally, though, using a dictionary is a more "Pythonic" way to maintain a collection of named values. Dynamically binding new variables has a number of issues, including the fact that most people will expect module variables to remain fairly steady between runs of a program. Also, the rules for naming of Python variables are much stricter than the possible set of animal names -- you can't include spaces in a variable name, for example, so while setattr
will happily store the value, you'd have to use getattr
to retrieve it.
Upvotes: 6