Not sure title is correct, but it's hard to ask a question about something when you don't know what to call it.
Imagine you have a class of something like
class Creature():
def __init__( self ):
self.str = 13
self.dex = 10
...
This creature will be given other definitions that affect it. In this case, imagine a creature object was given the race of Orc. Orc would increase it's str by 2. It could be possible for a creature to get multiple of these property modifying templates.
Is there a way to organize the code that would make this elegant, pythonic, easy to maintain, and possibly data driven?
First, here's how you'd do it without making it data-driven:
class Orc(Creature):
def __init__(self):
super().__init__()
self.str += 2
self.race = 'Orc'
But what if you wanted it to be data driven? Let's assume the data are just stored in a table that maps race names to dicts, which themselves map attribute names to bonuses. Like this:
races = {
'Human': {},
'Orc': {'str': +2, 'cha': -1},
'Elf': {'str': -1, 'dex': +1, 'int': +1}
}
That may be stored in a JSON file, or a SQLite database, or anything you want; it doesn't have to be a dict literal embedded in your source code. I'm just doing it that way to keep the example simple.
We could programmatically create Human, Orc, and Elf subclasses from this, but there's probably no good reason for that. All we really need is a factory function that creates an instance of the race, and after that they all act the same. So:
def create_creature(race):
bonuses = races.get(race, {})
creature = Creature()
for attr, bonus in bonuses.items():
setattr(creature, attr, getattr(creature, attr) + bonus)
creature.race = race
return creature
The only tricky part here is that setattr line. The key is that we don't know the name of the attribute at the time we're writing the code, we only know it at runtime, but we still want to be able to get or set the value of that attribute. Normally you don't want to use setattr and getattr—but when you need to dynamically access attributes by name, that's exactly what they're there for.
However, it's worth noting that there's another alternative. How much of your code actually relies on them being attributes of the Creature class? Could they just be members of a dict?
class Creature:
def __init__(self):
self.stats = {'str': 13,
'dex': 10,
# ...
}
If so, then that ugly setattr line becomes a lot nicer:
creature.stats[attr] += bonus
On the other hand, some other code in your program might become uglier. Maybe instead of this:
if player.str + roll(20) > enemy.str + roll(20):
… you have to write:
if play.stats['str'] + roll(20) > enemy.stats['str'] + roll(20)
This tradeoff pretty much always comes up with data-driven objects: one part of your program wants to treat them like data, another part wants to treat them like objects, and you have to balance the two.
Here, Orc inherits from Creature and increases its str by 2:
class Creature():
def __init__( self ):
self.str = 13
self.dex = 10
class Orc(Creature):
def __init__( self ):
Creature.__init__( self )
self.str += 2
Here is one class that would rule them all:
class Creature():
d_str = {None:13, "Orc":15}
d_dex = {None:10, "Orc":10}
def __init__( self, race=None ):
self.race = race
self.str = self.d_str[race]
self.dex = self.d_dex[race]
Related
So I am trying to get my data structure set up for an automated generator I am writing for a roleplaying game and I am having trouble with some specific inheritance quirks. Here is an excerpt of the data structure.
class data():
def __init__(self):
self.races = Races()
class Races(data):
def __init__(self):
self.humans = Humans()
class Humans(Races):
def __init__(self):
self.Characteristics = {
'Brawn':2,
'Agility':2,
'Intellect':2,
'Cunning':2,
'Willpower':2,
'Presence':2
}
There is a lot more in the structure but this is just a bottom to top overview. I also know it is indented weirdly but that is strictly stack overflow.
Now I wish to have two behaviors from this object.
The ability to call any characteristic with
data.races.humans.Characteristic['brawn']
as the calling format.
And too also be able to iterate through subclasses with a generator like:
(subclass for subclass in data.races.__subclasses__())
obviously after I have instantiated the object.
Now I have tried changing the structure several times and I can get it so EITHER I can call it with dot notation, but it returns AttributeError: 'Races' object has no attribute '__subclasses__'
Or vice versa by completely separating it into a more traditional structure but then I cannot call in dot notation and this makes it very hard to keep everything organized and readable.
Can anyone suggest what I am doing wrong or a more Pythonic way to approach the problem?
Let's start in the middle. Presumably, a character of any race has the same attributes, just different values for those attributes.
class Race:
def __init__(self):
self.life = 100 # 100% healthy
class Humanoid(Race):
def __init__(self):
super().__init__()
self.legs = 2
class Insectoid(Race):
def __init__(self):
super().__init__()
self.legs = 8
class Human(Humanoid):
def __init__(self):
super().__init__()
self.brawn = 2
self.agility = 2
self.intellect = 2
self.cunning = 2,
self.willpower = 2
self.presence = 2
class Elf(Humanoid):
def __init__(self):
super.__init__()
self.brawn = 1
self.agility = 3
self.intellect = 3
self.cunning = 2
self.willpower = 3
self.presence = 1
Now, any particular character would be instantiated as the correct class:
some_elf_1 = Elf()
some_human_1 = Human()
some_human_2 = Human()
for character in [some_elf_1, some_human_1, some_human_2]:
print("Brawn: ", character.brawn)
In the preceding, it doesn't matter what the actual type of each character is; as long as you know that it is some subclass of Race (or an instance of Race itself), it will have a brawn attribute that you can access.
You data class doesn't really seem necessary without more detail.
So, While the answer given put me on the right track I realized what I needed and am just throwing in my lot for any poor souls.
Firstly - I realized what was wrong with my generator, I was calling on the initialized object instead of the class object. Objects do not have a subclasses attrib and I was mis-informed by most of the guides I read!
Secondly, I considered using a metaclass to get the iterating behavior I wanted from my objects can simply be achieved with a registry attribute that is a dict of all the initialized subclasses.
lass Races(data):
def __init__(self):
self.humans = Humans()
self.droids = Droids()
self.twileks = Twileks()
self.registry = {
'humans':self.humans,
'droids':self.droids,
'twileks':self.twileks
}
This allows me to iterate through certain values for different races after they have been initialized.
Thanks for all the great answers!
I currently have the following two ways:
class Venue:
store = Database.store()
ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
OR
class Venue:
#classmethod
def set_venue_ids(cls):
store = Database.store()
cls.ids = [vid for vid in store.find(Venue.id, Venue.type == "Y")]
def __init__(self):
self.a = 1
self.b = 2
And before using/instantiating the class I would call:
Venue.set_venue_ids()
What would be the correct way of achieving this?
If it's the first way, what would I do if the instantiation of the attribute required more complex logic that could be done more simply through the use of a function?
Or is there an entirely different way to structure my code to accomplish what I'm trying to do?
From a purely technical POV, a class is an instance of its metaclass so the metaclass initializer is an obvious candidate for class attributes initialization (at least when you have anything a bit complex).
Now given the canonical lifetime of a class object (usually the whole process), I would definitly not use an attribute here - if anyone adds or removes venues from your database while your process is running, your ids attributes will get out of sync. Why don't you use a classmethod instead to make sure your data are always have up to date ?
Oh and yes, another way to construct your Venue.ids (or any other class attribute requiring non-trivial code) without having complex code at the class top-level polluthing the class namespace (did you noticed that in your first example store becomes a class attributes too, as well as vid if using Python 2.x ?) is to put the code in a plain function and call that function from within your class statement's body, ie:
def list_venue_ids():
store = Database.store()
# I assume `store.find()` returns some iterator (not a `list`)
# if it does return a list, you could just
# `return store.find(...)`.
return list(store.find(Venue.id, Venue.type == "Y"))
class Venue(object):
ids = list_venue_ids()
def __init__(self):
self.a = 1
self.b = 2
I'm new to classes, this is a small piece of code I've written, but I'm still really shaky on this concept, and am wondering exactly how the method node_name comes into play here and if it's even needed?
from rdflib import BNode
class HigherNode(object):
def node_name(name):
return name
def __init__(self, **kwargs):
self.node_type = kwargs.get('node_type', 'cog_con')
self.position = kwargs.get('position', 0)
self.node_id = self.node_name
self.node = kwargs.get(self.node_name(), BNode())
for key, value in kwargs.items():
setattr(self, key, value)
def __str__(self):
return 'This is the node of {} in the graph'.format(self.node_id)
I behavior that I'm seeking is something equivalent to this:
elephant = BNode()
when used as:
some_node = HigherNode(node_id = 'elephant')
So, first off, methods have to be called by an instance of the class. So, your behavior would look something like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name()
However, you never declared name inside the class. So, you'll have to do something like this:
def node_name(self):
return self.name
(All instances pass a reference to themselves to thier functions when called, so you'll always have to have at least one variable in the function call. You don't have to call it self.)
Really, it looks like what you want is actually a name setter/getter.
Try this:
Declare/set the variable in __init__.
def __init__(self, **kwargs):
self.node_name= kwargs.get('node_name', None)
Then you can use the variable like this:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
Since your class extends object, use getter/setter properties.
#property
def node_name(self):
return self.node_name
#node_name.setter
def node_name(self, x):
self.node_name = str(x)
These are called exactly the same as above in option 1:
# create an instance
node = HigherNode()
# get the name
print node.node_name
# set the name
node.node_name = "bluh"
I prefer this method, since it allows you much more control over how things are set, or even whether or not you can set or get them! (Just make a getter property without a corresponding setter property, for instance.)
However, this second method is more work to set up and may not be suitable for simple variables.
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.
I have a base class with two subclasses, say Car with subclasses HeavyCar and LightCar. Is it possible to have the creation of a base class return an object of a subclass dependent of a variable? Like this:
weightA = 2000
myAcar = Car(weigthA) # myAcar is now of type HeavyCar
weightB = 500
myBcar = Car(weightB) # myBcar is now of type LightCar
I know that the normal way to do this would be to look at the weight variable and then decide what type of object I want and then create that specific object. However I would like to leave it up to my class to decide which type it should be and not have to bother about that outside the class, i.e not have to look at the variable weight at all.
You can override __new__ to make it return the desired type. However, it would just be simpler to define a function that does the same, as it would be less prone to errors.
using __new__
class Car(object):
def __init__(self, weight):
self.weight = weight
def __new__(cls, weight):
if cls is Car:
if weight > 1000:
return object.__new__(HeavyCar)
else:
return object.__new__(LightCar)
else:
return object.__new__(cls)
class LightCar(Car):
def __init__(self, weight):
super(LightCar, self).__init__(weight)
self.speed = "fast"
class HeavyCar(Car):
pass
assert isinstance(Car(10), LightCar)
assert Car(10).weight == 10 # Car.__init__ invoked
assert hasattr(Car(10), "speed") # LightCar.__init__ invoked as well
assert isinstance(Car(1001), HeavyCar)
assert Car(1001).weight == 1001 # Car.__init__ invoked
Using a function
def create_car(weight):
if weight > 1000:
return HeavyCar(weight)
else:
return LightCar(weight)
assert isinstance(create_car(10), LightCar)
Even if it's somehow possible to do this, it's not really a very sane object design. Things can be too dynamic at some point. The sane solution would simply be a factory function:
class LightCar(Car):
maxWeight = 500
def __init__(self, weight):
assert(weight <= self.maxWeight)
self._weight = weight
# analogous for HeavyCar
def new_car(weight):
if weight <= LightCar.maxWeight:
return LightCar(weight)
..
From the point of view of the consumer, it makes little difference:
import cars
car = cars.new_car(450)
print(type(car))
Whether you write cars.new_car(450) or cars.Car(450) hardly makes any difference, except that the former is a dedicated factory function which does exactly what you're wanting: it returns a LightCar or HeavyCar depending on the weight.
It may be possible by overwriting the __new__ method, but that would be complex and take a long time.
... Just use a function or have a child class that uses multiple inheritance to contain light car and heavy car. If you make another child class you will have to deal with method resolution conflicts.
def car(weight):
if weight > 2:
return HeavyCar(weight)
return LightCar(weight)
__new__
http://rafekettler.com/magicmethods.html
https://www.python.org/download/releases/2.2/descrintro/#new
mro
http://www.phyast.pitt.edu/~micheles/python/meta2.html
Method Resolution Order (MRO) in new style Python classes