I am designing an API which deals with different type of vehicles for example cars, buses and vans. There is an endpoint POST /vehicle which will take a defined body.
{
'registration': 'str',
'date_of_purchase': 'datetime str',
'vehicle_type': 'car'
...
}
The first thing that we do is load an object based on the vehicle_type.
if body[vehicle_type] == 'car':
passed_in_vehicle = Car(body)
elif body['vehicle_type'] == 'bus':
passed_in_vehicle = Bus(body)
...
Within the python program there are multiple classes including:
vehicles.py
cars.py
vans.py
buses.py
The API entry point goes to vehicles.py which does some logic and then depending on the input will route it to one of the other classes to do more specific logic.
Each class has the same set of base methods.
update_vehicle_specs
register_vehicle
At the moment in the bottom of vehicles.py I have
if is_instance(passed_in_vehicle, Car):
carService = Cars()
carService.register_vehicle(passed_in_vehicle)
elif is_instance(passed_in_vehicle, Van):
vanService = Vans()
vanService.register_vehicle(passed_in_vehicle)
...
However, this cannot be scalable. What is the correct solution to route to specific classes based on a condition?
The way it looks to me, you can improve design a bit. Since each vehicle 'knows' where it is serviced, you should probably do in each something like
def __init__(self, ..., service):
super().__init__(self, ...)
...
...
service.register_vehicle(self)
this is coming from the perspective that "the car drives to the garage to register" rather than the "garage looks for new cars".
When you initialize a vehicle, pass it its servicing class, as in
new_car = Car(..., my_car_service)
where
my_car_service = Cars()
somewhere before. This is since we assume each new car needs to register, and we do not make a new service for each new car. Another approach, is for the class to contain (composition) its service object from the get go:
class Car:
service = Cars()
def __init__(...)
...
Car.service.register_vehicle(self)
so the class contains the servicing object always, and all initialized objects share the same one through a class variable. I actually prefer this.
First initialization:
With regard to your initial initialization of the vehicle, while using locals might be a nifty solution for that, it might be a bit more type safe to have something like what you have. In Python I like to use the "Python switch dictionary" (TM) for stuff like that:
{'car': Car,
'bus': Bus,
...
'jet': Jet
}[body[vehicle_type]](body)
I don't know if using locals would be ideal, but at least I believe it would scale better than your current solution:
use string name to instantiate classes from built-in locals() which will store imports as a dictionary
class Car(object):
def print(self):
print("I'm a car")
class Bus(object):
def print(self):
print("I'm a bus")
vehicle_type = "Car"
currV_class = locals()[vehicle_type]
currV_instance = currV_class()
#currV_instance.register_vehicle
currV_instance.print()
vehicle_type = "Bus"
currV_class = locals()[vehicle_type]
currV_instance = currV_class()
#currV_instance.register_vehicle
currV_instance.print()
OUTPUT:
I'm a car
I'm a bus
Related
I have an application that reads bluetooth advertisements from sensors and does stuff.
Currently, I use a dict to manage instances of the sensors and attributes of them. I have a bunch of functions that I call to do stuff based on the details of the bluetooth advertisements.
I am interested in switching from a dict to creating a class called Sensors. Having functions/methods within the class itself will clean up my code a lot (make it more readable) and set me up better for adding new features.
When a new advertisement comes in, the first step I do is to check whether it's from a sensor I've seen before. This is simple with a dict using
newAdvertisement = {"name": "sensor1", "attribute": "value"}
sensor = newAdvertisement['name']
if sensor in sensorDict
...
I am having trouble figuring out how to do something similar with instances of a class. If I read a string value from the advertisement, how can I check if there is an instance of my class with that name?
I've tried to work out a way to use isInstance with something like this:
newAdvertisement = {"name": "sensor1", "attribute": "value"}
sensor = newAdvertisement['name']
if isInstance(sensor, Sensor):
...
But line 2 sets the variable 'sensor' to the string 'sensor1', so this checks if that string is an instance of Sensor (which it is of course, not). What I want is to check if there is an instance of Sensor called sensor1.
I've also tried to create a list of instances like so:
sensor1 = Sensor(name='sensor1', attribute='value')
sensor2 = Sensor(name='sensor2', attribute='value')
sensorList = [sensor1, sensor2]
newAdvertisement = {"name": "sensor1", "attribute": "value"}
sensor = newAdvertisement['name']
if sensor in sensorList:
...
But again, I'm comparing the string 'sensor' to a list of objects, so it's never going to be there.
Does it make sense to do this? Can I do it cleanly? Is it useful to have my own class in this code?
There's nothing that does this automatically.
Your class can maintain a dictionary of all the instances.
import weakref
class Sensor:
sensor_dict = weakref.WeakValueDictionary()
def __init__(self, name, attribute)
self.sensor_dict[name] = self
self.name = name
self.attribute = attribute
Then you can do
if sensor in Sensor.sensor_dict:
I've used a WeakValueDictionary so the elements will be removed automatically when the Sensor instances are GC'ed.
There are many ways of achieving this. One would be to perform some list comprehension to your list when checking to see if the sensor is present in the list of sensors.
For example:
sensor1 = Sensor(name='sensor1', attribute='value')
sensor2 = Sensor(name='sensor2', attribute='value')
sensorList = [sensor1, sensor2]
newAdvertisement = {"name": "sensor1", "attribute": "value"}
sensor = newAdvertisement['name']
if sensor in [i.name for i in sensorList]:
...
Another way would be to overwrite the __eq__ method in your sensor class.
For example:
class Sensor:
def __init__(self, name, attribute):
self.name = name
self.attribute = attribute
def __eq__(self, other):
return other == self.name
sensor1 = Sensor(name='sensor1', attribute='value')
sensor2 = Sensor(name='sensor2', attribute='value')
sensorList = [sensor1, sensor2]
if "sensor1" in sensorList:
print("found")
In the example above, "found" is successfully printed because when you use the in keyword, python checks for equality on all of the elements in the container. Since the __eq__ method is set to compare to the sensor name, once it reaches the sensor with that name it returns True.
I am attempting to construct classes to play out a game of MTG (A card game). I have three relevant classes: MTGGame(...), MTGCard(...), and AbilityList(). An object of MTGGame has several attributes about the player (turn, mana,..., deck).
A player must have a deck of cards to play, so I create a list of MTGCard objects for each player that is a deck, and create an MTGGame object for each from the respective decks. The cards have abilities, and when creating the cards I store abilities as functions/params into each MTGCard. However, I need the abilities to inherit and access methods/attributes from MTGGame and update them, but if I use super().__init__, then I will need to call my deck as a parameter for AbilityList when making MTGCards, which I wouldn't have yet.
Can this be achieved? If not, any suggestions improving my OOP logic to achieve this task?
I am aware that I can do something like this:
class MTGGame():
def __init__(self, deck, turn = 0, mana = 0, lifeTotal = 20, cavalcadeCount = 0, hand = [], board = []):
self.turn = turn
self.mana = mana
self.lifeTotal = lifeTotal
...
def gainLife(self, lifeGained):
self.lifeTotal = self.lifeTotal +lifeGained
def combatPhase(self):
for card in self.board:
card.attackingAbility()
class MTGCard():
def __init__(self, name, CMC, cardType, power, toughness, castedAbility, attackingAbility, activatedAbility, canAttack = False):
....
self.attackingAbility = attackingAbility
Class abilityList():
def healersHawkAbility(self, lifeAmt):
MTGGame.gainLife(lifeAmt)
But this would affect all instances of MTGGame, not the specific MTGGame object this would've been called from. I'd like it to simply update the specific object in question. I'd like to do something like this but I don't know how abilityList methods could access MTGGame attributes/methods ('AbilityList' object has no attribute 'gainLife'):
Class abilityList():
def healersHawkAbility(self, lifeAmt):
#How do I access methods/attributes in MTGGame from here? self?
self.gainLife(lifeAmt)
aL = abilityList()
#One example card:
card1 = MTGCard("Healers Hawk",1,'Creature',1,1, aL.nullAbility(), aL.healersHawkAbility, aL.nullAbility())
whiteDeck = [list of constructed MTGCard() objects, card1, card2,...,cardLast]
player1 = MTGGame(whiteDeck)
...
#Call the ability in a method contained in MTGGame:
player1.combatPhase()
#Would call something like this inside
card.attackingAbility()
#Which may refer to card.healersHawkAbility() since we stored healersHawkAbility() as an attribute for that MTGCard,
#and would declare gainLife(), which refers to self.lifeTotal or player1.lifeTotal in this case.
This is an excellent start and clearly you have already thought a lot of this through. However, you haven't thought through the relationship between the classes.
First thing to note:
MTGGame.gainLife(lifeAmt) is a method call accessed via the class rather than an instance. This means that the self paramter is not actually filled in i.e. you will get an error becuase your method expects 2 arguments but only receive one.
What you perhaps meant to do is the following:
class MTGGame:
lifeTotal = 20 # Notice this is declared as a class variable
def __init__(self, ...):
...
#classmethod
def healersHawkAbility(cls, lifeGained):
cls.lifeTotal = cls.lifeTotal + lifeGained
However, this requires class variables which here defeats the point of having an instance.
Your naming throughout the program should suggest that your classes are a little off.
For instance player1 = MTGGame(). Is player a game? No, of course not. So actually you might want to rename your class MTGGame to Player to make it clear it refers to the player, not the game. A seperate class called MTGGame will probably need to be created to manage the interactions between the players e.g. whose turn it is, the stack holding the cards whilst resolving.
The main focus of your question: how to deal with the cards accessing the game/player object.
Cards should be able to access instances of the player and game classes, and if the player has a is_playing attribute, the card should not have this. The rule of thumb for inheritance is 'is a'. Since card 'is not a' player, it should not inherit from it or MTGGame. Instead, card should be like this for example:
game = RevisedMTGGame()
player1 = Player()
player2 = Player()
class Card:
def __init__(self, name, text, cost):
self.name = name
self.text = text
self.cost = cost
self.owner = None
self.game = None
class Creature(Card):
def __init__(self, name, text, cost, power, toughness):
super().__init__(self, name, text, cost)
self.power = power
self.toughness = toughness
def lifelink(self):
self.owner.heal(self.power) # NOTE: this is NOT how I would implement lifelink, it is an example of how to access the owner
healersHawk = Creature("Healer's Hawk", "Flying, Lifelink", 1, 1, 1)
healersHawk.game = game
healersHawk.owner = player1
You can see from this incomplete example how you can set up your cards easily, even with complex mechanics, and as the base classes have been defined you can avoid repitition of code. You might want to look into the event model in order to implement the lifelink mechanic, as an example. I wish you luck in continuing your game!
I'm super new to Python (I started about 3 weeks ago) and I'm trying to make a script that scrapes web pages for information. After it's retrieved the information it runs through a function to format it and then passes it to a class that takes 17 variables as parameters. The class uses this information to calculate some other variables and currently has a method to construct a dictionary. The code works as intended but a plugin I'm using with Pycharm called SonarLint highlights that 17 variables is too many to use as parameters?
I've had a look for alternate ways to pass the information to the class, such as in a tuple or a list but couldn't find much information that seemed relevant. What's the best practice for passing many variables to a class as parameters? Or shouldn't I be using a class for this kind of thing at all?
I've reduced the amount of variables and code for legibility but here is the class;
Class GenericEvent:
def __init__(self, type, date_scraped, date_of_event, time, link,
blurb):
countdown_delta = date_of_event - date_scraped
countdown = countdown_delta.days
if countdown < 0:
has_passed = True
else:
has_passed = False
self.type = type
self.date_scraped = date_scraped
self.date_of_event = date_of_event
self.time = time
self.link = link
self.countdown = countdown
self.has_passed = has_passed
self.blurb = blurb
def get_dictionary(self):
event_dict = {}
event_dict['type'] = self.type
event_dict['scraped'] = self.date_scraped
event_dict['date'] = self.date_of_event
event_dict['time'] = self.time
event_dict['url'] = self.link
event_dict['countdown'] = self.countdown
event_dict['blurb'] = self.blurb
event_dict['has_passed'] = self.has_passed
return event_dict
I've been passing the variables as key:value pairs to the class after I've cleaned up the data the following way:
event_info = GenericEvent(type="Lunar"
date_scraped=30/01/19
date_of_event=28/07/19
time=12:00
link="www.someurl.com"
blurb="Some string.")
and retrieving a dictionary by calling:
event_info.get_dictionary()
I intend to add other methods to the class to be able to perform other operations too (not just to create 1 dictionary) but would like to resolve this before I extend the functionality of the class.
Any help or links would be much appreciated!
One option is a named tuple:
from typing import Any, NamedTuple
class GenericEvent(NamedTuple):
type: Any
date_scraped: Any
date_of_event: Any
time: Any
link: str
countdown: Any
blurb: str
#property
def countdown(self):
countdown_delta = date_of_event - date_scraped
return countdown_delta.days
#property
def has_passed(self):
return self.countdown < 0
def get_dictionary(self):
return {
**self._asdict(),
'countdown': self.countdown,
'has_passed': self.has_passed,
}
(Replace the Anys with the fields’ actual types, e.g. datetime.datetime.)
Or, if you want it to be mutable, a data class.
I don't think there's anything wrong with what you're doing. You could, however, take your parameters in as a single dict object, and then deal with them by iterating over the dict or doing something explicitly with each one. Seems like that would, in your case, make your code messier.
Since all of your parameters to your constructor are named parameters, you could just do this:
def __init__(self, **params):
This would give you a dict named params that you could then process. The keys would be your parameter names, and the values the parameter values.
If you aligned your param names with what you want the keys to be in your get_dictionary method's return value, saving off this parameter as a whole could make that method trivial to write.
Here's an abbreviated version of your code (with a few syntax errors fixed) that illustrates this idea:
from pprint import pprint
class GenericEvent:
def __init__(self, **params):
pprint(params)
event_info = GenericEvent(type="Lunar",
date_scraped="30/01/19",
date_of_event="28/07/19",
time="12:00",
link="www.someurl.com",
blurb="Some string.")
Result:
{'blurb': 'Some string.',
'date_of_event': '28/07/19',
'date_scraped': '30/01/19',
'link': 'www.someurl.com',
'time': '12:00',
'type': 'Lunar'}
For context, I'm working on an inventory system in an RPG, and I'm prototyping it with python code.
What I don't understand is how to make separate variables for each instance of an item without declaring them manually. For a short example:
class Player(object):
def __init__(self):
self.Items = {}
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
#What do i place here?
def Pick_Up(self, owner):
owner.Items[self.???] = self #What do i then put at "???"
def Equip(self):
self.Equipped = 1
PC = Player()
#Below this line is what i want to be able to do
Leather_Pants(NPC) #<-Create a unique instance in an NPC's inventory
Leather_Pants(Treasure_Chest5) #Spawn a unique instance of pants in a treasure chest
Leather_Pants1.Pick_Up(PC) #Place a specific instance of pants into player's inventory
PC.Items[Leather_Pants1].Equip() #Make the PC equip his new leather pants.
If I did something silly in the above code, please point it out.
What I want to do if the code doesn't make it clear is that I want to be able to dynamically create variables for all items as I spawn them, so no two items will share the same variable name which will serve as an identifier for me.
I don't mind if I have to use another class or function for it like "Create_Item(Leather_Pants(), Treasure_Chest3)"
What's the best way to go about this, or if you think I'm doing it all wrong, which way would be more right?
As a general rule, you don't want to create dynamic variables, and you want to keep data out of your variable names.
Instead of trying to create variables named pants0, pants1, etc., why not just create, say, a single list of all leather pants? Then you just do pants[0], pants[1], etc. And none of the other parts of your code have to know anything about how the pants are being stored. So all of your problems vanish.
And meanwhile, you probably don't want creating a Leather_Pants to automatically add itself to the global environment. Just assign it normally.
So:
pants = []
pants.append(Leather_Pants(NPC))
pants.append(Leather_Pants(chests[5]))
pants[1].pickup(PC)
The pants don't have to know that they're #1. Whenever you call a method on them, they've got a self argument that they can use. And the player's items don't need to map some arbitrary name to each item; just store the items directly in a list or set. Like this:
class Player(object):
def __init__(self):
self.Items = set()
class Item(object):
def __init__(self):
self.Equipped = 0
class Leather_Pants(Item):
def __init__(self):
pass # there is nothing to do here
def Pick_Up(self, owner):
self.owner.Items.add(self)
def Equip(self):
self.Equipped = 1
Abernat has tackled a few issues, but I thought I weigh in with a few more.
You appear to be using OOP, but are mixing a few issues with your objects. For example, my pants don't care if they are worn or not, I care though for a whole host of reasons. In python terms the Pants class shouldn't track whether it is equipped (only that it is equippable), the Player class should:
class CarryableItem:
isEquipable = False
class Pants(CarryableItem):
isEquipable = True
class Player:
def __init__(self):
self.pants = None # Its chilly in here
self.shirt = None # Better take a jumper
self.inventory = [] # Find some loot
def equip(self,item):
if is.isEquipable:
pass # Find the right slot and equip it,
# put the previously equipped item in inventory, etc...
Also, its very rare that an item will need to know who its owner is, or that its been grabbed, so verbs like that again should go onto the Player:
class Player:
maxCarry = 10
def take(Item):
if len(inventory) < maxCarry:
inventory.append(item)
Lastly, although we've tried to move most verbs on to actors which actually do things, sometimes this isn't always the case. For example, when instantiating a chest:
import random
class StorageItem:
pass
class Chest(StorageItem):
__init__(self):
self.storage = random.randint(5)
self.loot = self.spawnLoot()
def spawnLoot(self):
for i in range(self.storge):
# Lets make some loot
item = MakeAnItem # Scaled according to type level of dungeon, etc.
loot.append(item)
def remove(item):
self.loot[self.loot.index(item)]=None
Now the question about what to do when a Player wants to plunder a chest?
class Player:
def plunder(storage):
for item in storage.loot:
# do some Ui to ask if player wants it.
if item is not None and self.wantsItem(item) or \
(self.name="Jane" and self.wantsItem(item) and self.doesntWantToPay):
self.take(item)
storage.remove(item)
edit: Answering the comment:
If you are curious about calculating armor class, or the like, that again is a factor of the user, not the item. For example:
class Player:
#property
def clothing(self):
return [self.pants,self.top]
#property
def armorClass(self):
ac = self.defence
for item in self.clothing:
def = item.armorClass
if self.race="orc":
if item.material in ["leather","dragonhide"]:
def *= 1.5 # Orcs get a bonus for wearing gruesome dead things
elif item.material in ["wool","silk"]:
def *= 0.5 # Orcs hate the fineries of humans
ac += def
return ac
pants = Pants(material="leather")
grum = Player(race="orc")
grum.equip(pants)
print grum.armorClass
>>> 17 # For example?
Is there anyway to transform the following code in Java to Python's equivalence?
public class Animal{
public enum AnimalBreed{
Dog, Cat, Cow, Chicken, Elephant
}
private static final int Animals = AnimalBreed.Dog.ordinal();
private static final String[] myAnimal = new String[Animals];
private static Animal[] animal = new Animal[Animals];
public static final Animal DogAnimal = new Animal(AnimalBreed.Dog, "woff");
public static final Animal CatAnimal = new Animal(AnimalBreed.Cat, "meow");
private AnimalBreed breed;
public static Animal myDog (String name) {
return new Animal(AnimalBreed.Dog, name);
}
}
Translating this code directly would be a waste of time. The hardest thing when moving from Java to Python is giving up most of what you know. But the simple fact is that Python is not Java, and translating line by line won't work as you expect. It's better to translate algorithms rather than code, and let Python do what it's good at.
It's unclear to me what the desired semantics of your Java would be. I'm guessing you're sort of trying to model a collection of animals (species, not breeds, incidentally) and imbue a set of associated classes with the behavior that varies according to the type of animal (roughly speaking the sounds that each makes).
In Python the natural way to do this would be through meta programming. You create a class or a factory function which returns each of the classes by passing arguments into a template. Since functions and classes are first order object in Python they can be passed around like any other object. Since classes are themselves objects you can access their attributes using setattr (and its cousins: hasattr and getattr).
Here's a simple example:
#!/usr/bin/env python
def Animal(species, sound):
class meta: pass
def makeSound(meta, sound=sound):
print sound
setattr(meta, makeSound.__name__, makeSound)
def name(meta, myname=species):
return myname
setattr(meta, 'name', name)
return meta
if __name__ == '__main__':
animal_sounds = (('Dog', 'woof'),
('Cat', 'meow'),
('Cow', 'moo'),
('Chicken', 'cluck'),
('Elephant', 'eraunngh'))
menagerie = dict()
for animal, sound in animal_sounds:
menagerie[animal] = Animal(animal, sound)
for Beast in menagerie:
beast = Beast()
print beast.name(), ' says ',
beast.makeSound()
Dog = menagerie['Dog']
fido = Dog() # equivalent to fido = menagerie['Dog']()
fido.makeSound()
# prints "woof"
Cat = menagerie['Cat']
felix = Cat()
felix.makeSound()
Mouse = Animal('Mouse', 'squeak')
mickey = Mouse()
mouse.makeSound()
# prints "squeak"
This seems like a trite example but I hope it gets the point across. I can create a table (in this case a tuple of tuples) which provides the arguments which will be used to fill in the varying parameters/behavior of our classes. The classes returned by Animal are just like any other Python classes. I've tried to show that in the examples here.
This is not a line-for-line translation, but something in the ballpark:
class Animal(object):
animal_breeds = "Dog Cat Cow Chicken Elephant".split()
animals = {}
def __init__(self, breed, name):
self._breed = breed
self.name = name
Animal.animals[name] = self
#property
def breed(self):
return Animal.animal_breeds[self._breed]
#staticmethod
def myDog(name):
return Animal(Animal.AnimalBreed.Dog, name)
# add enumeration of Animal breeds to Animal class
class Constants(object): pass
Animal.AnimalBreed = Constants()
for i,b in enumerate(Animal.animal_breeds):
setattr(Animal.AnimalBreed, b, i)
# define some class-level constant animals
# (although "woff" and "meow" are not what I would expect
# for names of animals)
Animal.DogAnimal = Animal(Animal.AnimalBreed.Dog, "woff")
Animal.CatAnimal = Animal(Animal.AnimalBreed.Cat, "meow")
# this code would be in a separate module that would import this
# code using
# from animal import Animal
#
print Animal.myDog("Rex").breed
print Animal.animals.keys()
http://code.activestate.com/recipes/413486/ contains a lot of help on this topic. Be warned that deepcopy support probably doesn't work with it.