Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
My understanding of python is zero to none... Been exhausting myself reading what seems like a hundred different ways to approach this. Below I put the assignment description and my code... As of now I am having trouble using the 'getAnimal()' command. I'm not sure what it does or how it works. Thanks in advance :D
Assignment description: "Write a class definition for a class 'Zoo.' It should have instance variables for animal type, herbivore/carnivore/omnivore, and inside/outside. It should also have a getAnimal() method to show the animals information. Write a separate "ZooDriver" class to a) create three instances of zoo animals, b) get user input on which animal to view (1,2,3)c) show the animal info suing getAnimal()."
~~~~~~~~ My code:
class Zoo:
def __init__(self, animal, animal_type, habitat):
self.animal = animal
self.animal_type = animal_type
self.habitat = habitat
user_input=raw_input("Enter the number 1, 2, or 3 for information on an animal.")
if user_input == "1":
my_animal = Zoo("Giraffe", "herbivore", "outside")
elif user_input == "2":
my_animal = Zoo("Lion", "carnivore", "inside")
elif user_input == "3":
my_animal = Zoo("Bear", "omnivore", "inside")
print "Your animal is a %s, it is a %s, and has an %s habitat." % (my_animal.animal, my_animal.animal_type, my_animal.habitat)
A class defines a type of thing. An instance is one thing of that type. For example, you could say Building is a type of thing.
Let's start with a class named Building:
class Building():
pass
Now, to create an actual building -- an instance -- we call it almost like it was a function:
b1 = Building()
b2 = Building()
b3 = Building()
There, we just created three buildings. They are identical, which isn't very useful. Maybe we can give it a name when we create it, and store it in an instance variable. That requires creating a constructor that takes an argument. All class methods must also take self as its first argument, so our constructor will take two arguments:
class Building():
def __init__(self, name):
self.name = name
Now we can create three different buildings:
b1 = Building("firehouse")
b2 = Building("hospital")
b3 = Building("church")
If we want to create a "driver" class to create three buildings, we can do that pretty easily. If you want to set an instance variable or do some work when you create an instance, you can do that in a constructor. For example, this creates such a class that creates three buildings and stores them in an array:
class Town():
def __init__(self):
self.buildings = [
Building("firehouse"),
Building("hospital"),
Building("church")
]
We now can create a single object that creates three other objects. Hopefully that's enough to get you over the initial hurdle of understanding classes.
OK I will try to answer the main question here: What a class is.
From Google: class /klas/
noun: class; plural noun: classes
1.
a set or category of things having some property or attribute in common and
differentiated from others by kind, type, or quality.
In programming, a class is just that. say, for example you have class Dog. Dogs bark "ruf ruff".
We can define the dog class in python using just that information.
class Dog:
def bark(self):
print "ruf ruff"
To use the class, it is instantiated by calling () its constructor:
Spot = Dog()
We then want Spot to bark, so we call a method of the class:
Spot.bark()
To further explain the details of the code here would be to go outside of the scope of this question.
Further reading:
http://en.wikipedia.org/wiki/Instance_%28computer_science%29
http://en.wikipedia.org/wiki/Method_%28computer_programming%29
As a direct answer:
class Zoo(object):
def __init__(self, name="Python Zoo"):
self.name = name
self.animals = list()
def getAnimal(self,index):
index = index - 1 # account for zero-indexing
try:
print("Animal is {0.name}\n Diet: {0.diet}\n Habitat: {0.habitat}".format(
self.animals[index]))
except IndexError:
print("No animal at index {}".format(index+1))
def listAnimals(self,index):
for i,animal in enumerate(self.animals, start=1):
print("{i:>3}: {animal.name}".format(i=i,animal=animal))
class Animal(object):
def __init__(self, name=None, diet=None, habitat=None):
if any([attr is None for attr in [name,diet,habitat]]):
raise ValueError("Must supply values for all attributes")
self.name = name
self.diet = diet
self.habitat = habitat
class ZooDriver(object):
def __init__(self):
self.zoo = Zoo()
self.zoo.animals = [Animal(name="Giraffe", diet="Herbivore", habitat="Outside"),
Animal(name="Lion", diet="Carnivore", habitat="Inside"),
Animal(name="Bear", diet="Herbivore", habitat="Inside")]
def run(self):
while True:
print("1. List Animals")
print("2. Get information about an animal (by index)"
print("3. Quit")
input_correct = False
while not input_correct:
in_ = input(">> ")
if in_ in ['1','2','3']:
input_correct = True
{'1':self.zoo.listAnimals,
'2':lambda x: self.zoo.getAnimal(input("index #: ")),
'3': self.exit}[in_]()
else:
print("Incorrect input")
def exit(self):
return
if __name__ == "__main__":
ZooDriver().run()
I haven't actually run this code so some silly typos may have occurred and the usual "off-by-one" errors (oops), but I'm fairly confident in it. It displays a lot of concepts your instructor won't expect you to have mastered yet (such as string formatting, most likely, and almost certainly hash tables with lambdas). For that reason, I STRONGLY recommend you not copy this code to turn in.
Related
New to OOP and python, I am struggling enormously to grasp what good classes actually are for. I tried to ask help from a lecturer who said "oh, then you should read about general methods to classes". Been putting in a days work but get no where.
I get it that a class allow you to collect an instance structure and methods to it, like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idA.show_list()
But what is even the point of a class if there were not MANY instances you would classify? If I have a method within the class, I must hard code the actual instance to call the class for. What if you want a user to search and select an instance, to then do operations to (e.g. print, compute or whatever)??
I thought of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
select_item = input("enter item id")
select_item.show_list()
Replacing hard coded variable with input variable doesn't work, probably logically. I then played with the idea of doing it like this:
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
iL = [Items('idA', 'A'), Items('idB', 'B')]
selected_item = input("enter item id")
for selected_item in iL:
print(f'{selected_item.item_id} {selected_item.item_name}')
Now all are called thanks to making it a list instead of separate instances, but how do I actually apply code to filter and only use one instance in the list (dynamically, based on input)?
I would love the one who brought me sense to classes. You guys who work interactively with large data sets must do something what I today believe exist in another dimension.
See examples above^^
It seems you want to find all the instances of a certain element within a class.
This is as simple as:
print([x for x in iL if x.item_id == selected_item])
Now, you may ask why you can't just store the elements of iL as tuples instead of classes. The answer is, you can, but
("idA", "A")
is much less descriptive than:
item_id = "idA"
item_name = "A"
Any code you write with classes, you should in theory be able to write without classes. Classes are for the benefit of the coder, not the end-user of the program. They serve to make the program more readable, which I'm sure you'll find is a desirable property.
Your point here is to lookup for Items instances based on their item_id attribute.
That's a thing to create instances of a class.
It's a completely different thing to search for items objects stored in memory - that is not directly linked to the concept of OOP, classes and instances.
You could use dictionary to store references of your objects and then lookup in your dictionary.
class Items:
def __init__(self, item_id, item_name):
self.item_id = item_id
self.item_name = item_name
def show_list(self):
print(self.item_id, self.item_name)
idA = Items("idA", "A")
idB = Items("idB", "B")
lookup_dict = {"idA": idA, "idB": idB}
select_item = input("enter item id")
found_item = lookup_dict.get(select_item)
if found_item:
found_item.show_list()
else:
print(f"item {select_item} not found")
EDIT: CHECK AT THE BOTTOM FOR A MORE CLEAR VIEW OF WHAT I AM DOING, PLEASE!
As an example, let's say I have information on three cars:
Car One
500hp
180mph
15mpg
Car Two
380hp
140mph
24mpg
Car Three
450hp
170mph
20mpg
I want to put that in a dictionary, or SOMETHING, so that I can easily access it through a function.
def fuel_eco(car):
return("The fuel economy for %s is %s" % (car, mpg))
def top_speed(car):
return("The top speed for %s is %s" % (car, speed))
def horsepower(car):
return("The horsepower for %s is %s" % (car, hp))
Basically have a module with some functions and a list/dictionary/whatever of the information, and then have another script that asks what car they want to view info on, and what information they want to know.
import carstats
car = input("What car do you want to find out about?")
stat = input("What information do you want to know?")
getStat = getattr (carstats, stat)
print(getStat(car))
How do I store the information for the three vehicles (And more if I add them) in a dictionary, so I can retrieve the information?
Okay, these are the actual files I am working with:
File one is asoiaf.py:
def sigil (house):
"""
Function to return a description of the sigil of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
sigil = "a grey direwolf on a white field."
elif house == "lannister":
sigil = "a golden lion rampant on a crimson field."
elif house == "targaryen":
sigil = "a red three-headed dragon on a black field."
else:
sigil = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The sigil for House %s is %s" % (house, sigil))
def motto (house):
"""
Function to return the family motto of a specified Great House.
Takes one argument, the name of the House.
"""
house = house.lower ()
if house == "stark":
motto = "Winter is coming!"
elif house == "lannister":
motto = "Hear me roar!"
elif house == "targaryen":
motto = "Fire and blood!"
else:
motto = "Unknown"
house = str(house[0].upper()) + str(house[1:len(house)])
return("The motto for House %s is: %s" % (house, motto))
The second file is encyclopedia.py:
import asoiaf
#import sl4a
#droid = sl4a.Android ()
#sound = input ("Would you like to turn on sound?")
info = "yes"
while info == "yes":
#if sound == "yes":
# droid.ttsSpeak ("What house do you want to learn about?")
house = input ("What house do you want to learn about?")
house = str(house[0].upper()) + str(house[1:len(house)])
#if sound == "yes":
# droid.ttsSpeak ("What do you want to know about House %s?" % house)
area = input ("What do you want to know about House %s?" % house)
getArea = getattr (asoiaf, area)
#if sound == "yes":
# droid.ttsSpeak (getArea (house))
print (getArea (house))
#if sound == "yes":
# droid.ttsSpeak ("Would you like to continue learning?")
info = input ("Would you like to continue learning?")
if info == "no":
print ("Goodbye!")
You'll see a lot of commenting out in the last code, because I had to comment out the TTS that I have for my phone, since most people are not on an Android right now. As you can see, I am using IF, ELIF, ELSE in the functions, and I am just trying to see if there is an easier way. I apologize if it is/was confusing.
Creating a class should be the best way to do it:
class Car: # define the class
def __init__(self, name, speed, hp, mpg):
# This is the constructor. The self parameter is handled by python,
# You have to put it. it represents the object itself
self.name = name
self.speed = speed
self.hp = hp
self.mpg = hp
# This bind the parameters to the object
# So you can access them throught the object
You can then use the object this way:
my_car1 = Car('Car One', 180, 500, 15)
my_car1.speed # Will return 180
Concercing the __init__ name, it has to be this name, all constructors have this name (that's how Python know it is the class constructor). The __init__ method is called when you call Car('car one', 180, 500, 15). You have to ommit the self parameter, Python handle it.
You can add other function to your class, like
def top_speed(self):
return 'The top speed is {}'.format(self.speed)
Then you simply have to do my_car1.topspeed()
In every function you define in a class self must be the first parameter (except some rare cases such as classmethod or staticmethods). Obviously the topseed function works only if you create it in the class Car: block.
I'd suggest you should read more about object oriented programming (OOP) in Python. Just google OOP python and you will have a lot of serious ressources explaining you how to create classes and how to use them.
This official python classes tutorial should help you a lot in understanding the concept.
EDIT:
Regarding the accessing of the class in an other script. It's simple:
let's say you save the code above in a car.py file. Just place that file in the same folder as your other script, and in your other script do:
from car import Car # car is the name of the .py file, Car is the class you want to import
name = input('Car name: ')
speed = int(input('Car speed: ')) # input return a string, you have to cast to an integer to have a number
hp = int(input('Car hp: '))
mpg = int(input('Car mpg : '))
my_car = Car(name,speed,hp,mpg) # Then you just create a Car Object with the data you fetched from a user.
stuff = my_car.speed * my_car.hp # An example of how to use your class
print('The given car have {} mph top speed and have {} horsepower'.format(my_car.speed,my_car.hp))
What you have to understand is that a Class is some kind of a formated data type. When creating a Car class, you are defining how to create a car object. And Each time you call Car(...), you actually create one of these object, the value you put in the object are whatever values you want to put. It could be random number, user input or even network fetched data. You can use this object as you want.
Edit 2:
Given your code. Creating classes will change some things. Let's Give an example.
File 1 houses.py:
class House: # defining a house class
def __init__(self,name, sigil, motto):
self.name = name
self.sigil = sigil
self.moto = motto
# Then, in the same file, you create your houses.
starks = House('starks','grey direwolf on a white field','Winter is coming!')
lannisters = House('lannisters', 'a golden lion rampant on a crimson field', 'Hear me roar!')
# let's skip targaryen, it's the same way...
unknown_house = House('unknown','unknown','unknow')
houses = [starks, lannisters]
def get_house(name):
for house in houses:
if house.name == name:
return house
return unknow_house # if no house match, return unknow
Then in your second file. You just se that:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
print('this is the house {}; Sigil {} and motto {}'.format(my_house.name, my_house.sigil, my_house.motto))
If you plan on working on biggers set. You should have a look at Enums. That could fit what you want.
If you want to getting a precise attribute, you can do it this way:
import houses
house_wanted = input('What house do you want to know about?')
my_house = houses.get_house(house_wanted)
attr= input('What do you want to know about that house?')
print(getattr(my_house,attr.lower()))
Note this last thing will raise an error if you call for non-existent attr (like foo).
There are many ways to solve the broader problem you describe in the text of your question (the question of how to store multiple pieces of information about an object). Classes maybe one good one. Classes have the advantage of better robustness than dictionaries.
To answer the specific question in the summary/title: "how to have more than one item associated with one key in a dictionary" - use dictionaries as the values, like this:
car_info = {'CarOne': {'power': 500, 'speed': 180, 'mileage': 18},
'CarTwo': {'power': 380, 'spead': 200, 'mileage': 10}
}
print "Car Two has power %d mileage %d" % (car_info['CarTwo']['power'], car_info['CarTwo']['mileage'])
You can see that this is not especially robust by trying to access the 'speed' for "CarTwo". If you look closely you will see that because I made a deliberate typo in the initializer for CarTwo, it does not have a speed at all, it has a spead. Classes will catch this error, dictionaries will not.
This is not a reason not to do it with dictionaries - just something to be aware of when deciding for your particular case.
You could create a class, called car, with whatever attributes you want!
Here's a great tutorial on how to do that: class tutorial
I'm on the road right now, but if you're having trouble, please tell me so that I can write some useful code...
So I have this class:
class hero():
def __init__(self, name="Jimmy", prof="Warrior", weapon="Sword"):
"""Constructor for hero"""
self.name = name
self.prof = prof
self.weapon = weapon
self.herodict = {
"Name": self.name,
"Class": self.prof,
"Weapon": self.weapon
}
self.herotext = {
"Welcome": "Greetings, hero. What is thine name? ",
"AskClass": "A fine name, {Name}. What is your class? ",
"AskWeapon": "A {Class}, hmm? What shalt thy weapon be? ",
}
def setHeroDicts(self, textkey, herokey):
n = raw_input(self.herotext[textkey].format(**self.herodict))
if n == "":
n = self.herodict[herokey]
self.herodict[herokey] = n
#print self.herodict[herokey]
def heroMake(self):
h = hero()
h.setHeroDicts("Welcome", "Name")
h.setHeroDicts("AskClass", "Class")
h.setHeroDicts("AskWeapon", "Weapon")
And in another class I have this executing
def Someclass(self):
h = hero()
print h.herodict["Class"]
h.heroMake()
print h.getClass()
if "Mage" in h.herodict["Class"]:
print "OMG MAGE"
elif "Warrior" in h.herodict["Class"]:
print "Warrior!"
else:
print "NONE"
So if I input nothing each time, it will result in a blank user input, and give the default values. But if I put an input, then it will change the herodict values to what I customize. My problem is, if I try and access those updated values in Someclass it only gives me the default values instead of the new ones. How do I go about accessing the updated values?
The main issue with your class is that you are creating a new object within heromake instead of using the existing one. You can fix this by replacing h with self (so that each time you are calling setHeroDicts on the object):
def heromake(self):
self.setHeroDicts("Welcome", "Name")
self.setHeroDicts("AskClass", "Class")
self.setHeroDicts("AskWeapon", "Weapon")
The first argument to a method is always set to the instance itself, so if you want to interact with the instance or mutate it, you need to use it directly. When you do h = hero() in your original code, you create a whole new hero object, manipulate it and then it disappears when control passes back to your function.
A few other notes: you should name your classes with CamelCase, so it's easier to tell they are classes (e.g., you should really have class Hero) and in python 2, you need to make your classes descend from object (so class Hero(object)). Finally, you are duplicating nearly the entire point of having classes with your herodict, you should consider accessing the attributes of the object directly, instead of having the intermediary herodict (e.g., instead of doing h.herodict["Class"] you could do h.prof directly.
I am writing a program to add to and update an address book. Here is my code:
EDITED
import sys
import os
list = []
class bookEntry(dict):
total = 0
def __init__(self):
bookEntry.total += 1
self.d = {}
def __del__(self):
bookEntry.total -= 1
list.remove(self)
class Person(bookEntry):
def __init__(self, n):
self.n = n
print '%s has been created' % (self.n)
def __del__(self):
print '%s has been deleted' % (self.n)
def addnewperson(self, n, e = '', ph = '', note = ''):
self.d['name'] = n
self.d['email'] = e
self.d['phone'] = ph
self.d['note'] = note
list.append()
def updateperson(self):
key = raw_input('What else would you like to add to this person?')
val = raw_input('Please add a value for %s' % (key))
self.d[key] = val
def startup():
aor = raw_input('Hello! Would you like to add an entry or retrieve one?')
if aor == 'add':
info = raw_input('Would you like to add a person or a company?')
if info == 'person':
n = raw_input('Please enter this persons name:')
e = raw_input('Please enter this persons email address:')
ph = raw_input('Please enter this persons phone number:')
note = raw_input('Please add any notes if applicable:')
X = Person(n)
X.addnewperson(n, e, ph, note)
startup()
When I run this code I get the following error:
in addnewperson
self.d['name'] = n
AttributeError: 'Person' object has no attribute 'd'
I have two questions:
UPDATED QUESTIONS
1. why isnt the d object being inherited from bookentry()?
I know this question/code is lengthy but I do not know where to go from here. Any help would be greatly appreciated.
The addnewperson shoud have 'self' as first argument; actually, the name doesn't matter ('self' is just a convention), but the first argument represent the object itself. In your case, it's interpreting n as the "self" and the other 3 as regular arguments.
____del____ must not take arguments besides 'self'.
Edit: BTW I spotted a few other problems in your example, that maybe you're not aware of:
1) d in bookentry is a class member, not an instance member. It's shared by all bookentry's instances. To create an instance member, use:
class bookentry(dict):
def __init__(self,n):
self.d = {}
# rest of your constructor
2) you're trying to access d directly (as you would do in Java, C++ etc), but Python doesn't support that. You must have a 'self' parameter in your methods, and access instance variables through it:
class person(bookentry):
def foo(self,bar):
self.d[bar] = ...
person().foo(bar)
Update: for the last problem, the solution is to call the super constructor (which must be done explicitly in Python):
class Person(bookEntry):
def __init__(self, n):
super(Person, self).__init__()
self.n = n
print '%s has been created' % (self.n)
A brief explanation: for people with background in OO languages without multiple inheritance, it feels natural to expect the super type constructor to be called implicitly, automatically choosing the most suitable one if no one is mentioned explicitly. However, things get messy when a class can inherit from two or more at the same time, for this reason Python requires the programmer to make the choices himself: which superclass constructor to call first? Or at all?
The behavior of constructors (and destructors) can vary wildly from language to language. If you have further questions about the life cycle of Python objects, a good place to start would be here, here and here.
why isnt the d object being inherited from bookentry()?
That's because __init__ of the bookEntry is not called in the __init__ of the Person:
super(Person, self).__init__()
BTW, why inherit from dict if its functionality is not used? It's better to remove it and inherit from object instead (also class names are usually CamelCased):
class BookEntry(object):
As an example, just a couple of dummy objects that will be used together. FWIW this is using Python 2.7.2.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)
That's probably enough code, you see where I'm going with this. If I call Hammer.break(), I'm calling it on the same instance of the object; if Billy's hammer is broken, so is Tommy's (it's really the same Hammer after all).
Now obviously if the program were limited to just Billy and Tommy as instances of Students, the fix would be obvious - instantiate more Hammers. But clearly I'm asking because it isn't that simple, heh. I would like to know if it's possible to create objects which show up as unique instances of themselves for every time they're called into being.
EDIT: The kind of answers I'm getting lead me to believe that I have a gaping hole in my understanding of instantiation. If I have something like this:
class Foo(object):
pass
class Moo(Foo):
pass
class Guy(object):
def __init__(self, thing):
self.thing = thing
Bill = Guy(Moo())
Steve = Guy(Moo())
Each time I use Moo(), is that a separate instance, or do they both reference the same object? If they're separate, then my whole question can be withdrawn, because it'll ahve to make way for my mind getting blown.
You have to create new instances of the Tool for each Student.
class Student(object):
def __init__(self, tool):
self.tool = tool
def draw(self):
if self.tool.broken != True:
print "I used my tool. Sweet."
else:
print "My tool is broken. Wah."
class Tool(object):
def __init__(self, name):
self.name = name
self.broken = False
def break(self):
print "The %s busted." % self.name
self.broken = True
# Instead of instance, make it a callable that returns a new one
def Hammer():
return Tool('hammer')
# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
I'll try to be brief. Well.. I always try to be brief, but my level of success is pretty much random.randint(0, never). So yeah.
Lol. You even failed to be brief about announcing that you will try to be brief.
First, we need to be clear about what "called into being" means. Presumably you want a new hammer every time self.tool = object happens. You don't want a new instance every time, for example, you access the tool attribute, or you'd always a get a new, presumably unbroken, hammer every time you check self.tool.broken.
A couple approaches.
One, give Tool a copy method that produces a new object that should equal the original object, but be a different instance. For example:
class Tool:
def __init__(self, kind):
self.kind = kind
self.broken = False
def copy(self):
result = Tool(self.kind)
result.broken = self.broken
return result
Then in Student's init you say
self.tool = tool.copy()
Option two, use a factory function.
def makehammer():
return Tool(hammer)
class Student:
def __init__(self, factory):
self.tool = factory()
Billy = Student(makehammer)
I can't think any way in Python that you can write the line self.tool = object and have object automagically make a copy, and I don't think you want to. One thing I like about Python is WYSIWYG. If you want magic use C++. I think it makes code hard to understand when you not only can't tell what a line of code is doing, you can't even tell it's doing anything special.
Note you can get even fancier with a factory object. For example:
class RealisticFactory:
def __init__(self, kind, failurerate):
self.kind = kind
self.failurerate = failurerate
def make(self):
result = Tool(self.kind)
if random.random() < self.failurerate:
result.broken = True
if (self.failurerate < 0.01):
self.failurerate += 0.0001
return result
factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
You could change your lines like this:
Billy = Student(Tool('hammer'))
Tommy = Student(Tool('hammer'))
That'll produce a distinct instance of your Tool class for each instance of the Student class. the trouble with your posted example code is that you haven't "called the Tool into being" (to use your words) more than once.
Just call Tool('hammer') every time you want to create a new tool.
h1 = Tool('hammer')
h2 = Tool('hammer')
Billy = Student(h1)
Tommy = Student(h2)
Oh wait, I forgot, Python does have magic.
class Student:
def __setattr__(self, attr, value):
if attr == 'tool':
self.__dict__[attr] = value.copy()
else:
self.__dict__[attr] = value
But I still say you should use magic sparingly.
After seeing the tenor of the answers here and remembering the Zen of Python, I'm going to answer my own dang question by saying, "I probably should have just thought harder about it."
I will restate my own question as the answer. Suppose I have this tiny program:
class Item(object):
def __init__(self):
self.broken = False
def smash(self):
print "This object broke."
self.broken = True
class Person(object):
def __init__(self, holding):
self.holding = holding
def using(self):
if self.holding.broken != True:
print "Pass."
else:
print "Fail."
Foo = Person(Item())
Bar = Person(Item())
Foo.holding.smash()
Foo.using()
Bar.using()
The program will return "Fail" for Foo.using() and "Pass" for Bar.using(). Upon actually thinking about what I'm doing, "Foo.holding = Item()" and "Bar.holding = Item()" are clearly different instances. I even ran this dumpy program to prove it worked as I surmised it did, and no surprises to you pros, it does. So I withdraw my question on the basis that I wasn't actually using my brain when I asked it. The funny thing is, with the program I've been working on, I was already doing it this way but assuming it was the wrong way to do it. So thanks for humoring me.